1 | /* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. |
2 | |
3 | This program is free software; you can redistribute it and/or modify |
4 | it under the terms of the GNU General Public License as published by |
5 | the Free Software Foundation; version 2 of the License. |
6 | |
7 | This program is distributed in the hope that it will be useful, |
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | GNU General Public License for more details. |
11 | |
12 | You should have received a copy of the GNU General Public License |
13 | along with this program; if not, write to the Free Software Foundation, |
14 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ |
15 | |
16 | /** |
17 | @file storage/perfschema/pfs.cc |
18 | The performance schema implementation of all instruments. |
19 | */ |
20 | #include "my_global.h" |
21 | #include "thr_lock.h" |
22 | #include "mysql/psi/psi.h" |
23 | #include "mysql/psi/mysql_thread.h" |
24 | #include "my_pthread.h" |
25 | #include "sql_const.h" |
26 | #include "pfs.h" |
27 | #include "pfs_instr_class.h" |
28 | #include "pfs_instr.h" |
29 | #include "pfs_host.h" |
30 | #include "pfs_user.h" |
31 | #include "pfs_account.h" |
32 | #include "pfs_global.h" |
33 | #include "pfs_column_values.h" |
34 | #include "pfs_timer.h" |
35 | #include "pfs_events_waits.h" |
36 | #include "pfs_events_stages.h" |
37 | #include "pfs_events_statements.h" |
38 | #include "pfs_setup_actor.h" |
39 | #include "pfs_setup_object.h" |
40 | #include "sql_error.h" |
41 | #include "sp_head.h" |
42 | #include "pfs_digest.h" |
43 | |
44 | /** |
45 | @page PAGE_PERFORMANCE_SCHEMA The Performance Schema main page |
46 | MySQL PERFORMANCE_SCHEMA implementation. |
47 | |
48 | @section INTRO Introduction |
49 | The PERFORMANCE_SCHEMA is a way to introspect the internal execution of |
50 | the server at runtime. |
51 | The performance schema focuses primarily on performance data, |
52 | as opposed to the INFORMATION_SCHEMA whose purpose is to inspect metadata. |
53 | |
54 | From a user point of view, the performance schema consists of: |
55 | - a dedicated database schema, named PERFORMANCE_SCHEMA, |
56 | - SQL tables, used to query the server internal state or change |
57 | configuration settings. |
58 | |
59 | From an implementation point of view, the performance schema is a dedicated |
60 | Storage Engine which exposes data collected by 'Instrumentation Points' |
61 | placed in the server code. |
62 | |
63 | @section INTERFACES Multiple interfaces |
64 | |
65 | The performance schema exposes many different interfaces, |
66 | for different components, and for different purposes. |
67 | |
68 | @subsection INT_INSTRUMENTING Instrumenting interface |
69 | |
70 | All the data representing the server internal state exposed |
71 | in the performance schema must be first collected: |
72 | this is the role of the instrumenting interface. |
73 | The instrumenting interface is a coding interface provided |
74 | by implementors (of the performance schema) to implementors |
75 | (of the server or server components). |
76 | |
77 | This interface is available to: |
78 | - C implementations |
79 | - C++ implementations |
80 | - the core SQL layer (/sql) |
81 | - the mysys library (/mysys) |
82 | - MySQL plugins, including storage engines, |
83 | - third party plugins, including third party storage engines. |
84 | |
85 | For details, see the @ref PAGE_INSTRUMENTATION_INTERFACE |
86 | "instrumentation interface page". |
87 | |
88 | @subsection INT_COMPILING Compiling interface |
89 | |
90 | The implementation of the performance schema can be enabled or disabled at |
91 | build time, when building MySQL from the source code. |
92 | |
93 | When building with the performance schema code, some compilation flags |
94 | are available to change the default values used in the code, if required. |
95 | |
96 | For more details, see: |
97 | @verbatim ./configure --help @endverbatim |
98 | |
99 | To compile with the performance schema: |
100 | @verbatim ./configure --with-perfschema @endverbatim |
101 | |
102 | The implementation of all the compiling options is located in |
103 | @verbatim ./storage/perfschema/plug.in @endverbatim |
104 | |
105 | @subsection INT_STARTUP Server startup interface |
106 | |
107 | The server startup interface consists of the "./mysqld ..." |
108 | command line used to start the server. |
109 | When the performance schema is compiled in the server binary, |
110 | extra command line options are available. |
111 | |
112 | These extra start options allow the DBA to: |
113 | - enable or disable the performance schema |
114 | - specify some sizing parameters. |
115 | |
116 | To see help for the performance schema startup options, see: |
117 | @verbatim ./sql/mysqld --verbose --help @endverbatim |
118 | |
119 | The implementation of all the startup options is located in |
120 | @verbatim ./sql/mysqld.cc, my_long_options[] @endverbatim |
121 | |
122 | @subsection INT_BOOTSTRAP Server bootstrap interface |
123 | |
124 | The bootstrap interface is a private interface exposed by |
125 | the performance schema, and used by the SQL layer. |
126 | Its role is to advertise all the SQL tables natively |
127 | supported by the performance schema to the SQL server. |
128 | The code consists of creating MySQL tables for the |
129 | performance schema itself, and is used in './mysql --bootstrap' |
130 | mode when a server is installed. |
131 | |
132 | The implementation of the database creation script is located in |
133 | @verbatim ./scripts/mysql_performance_tables.sql @endverbatim |
134 | |
135 | @subsection INT_CONFIG Runtime configuration interface |
136 | |
137 | When the performance schema is used at runtime, various configuration |
138 | parameters can be used to specify what kind of data is collected, |
139 | what kind of aggregations are computed, what kind of timers are used, |
140 | what events are timed, etc. |
141 | |
142 | For all these capabilities, not a single statement or special syntax |
143 | was introduced in the parser. |
144 | Instead of new SQL statements, the interface consists of DML |
145 | (SELECT, INSERT, UPDATE, DELETE) against special "SETUP" tables. |
146 | |
147 | For example: |
148 | @verbatim mysql> update performance_schema.SETUP_INSTRUMENTS |
149 | set ENABLED='YES', TIMED='YES'; |
150 | Query OK, 234 rows affected (0.00 sec) |
151 | Rows matched: 234 Changed: 234 Warnings: 0 @endverbatim |
152 | |
153 | @subsection INT_STATUS Internal audit interface |
154 | |
155 | The internal audit interface is provided to the DBA to inspect if the |
156 | performance schema code itself is functioning properly. |
157 | This interface is necessary because a failure caused while |
158 | instrumenting code in the server should not cause failures in the |
159 | MySQL server itself, so that the performance schema implementation |
160 | never raises errors during runtime execution. |
161 | |
162 | This auditing interface consists of: |
163 | @verbatim SHOW ENGINE PERFORMANCE_SCHEMA STATUS; @endverbatim |
164 | It displays data related to the memory usage of the performance schema, |
165 | as well as statistics about lost events, if any. |
166 | |
167 | The SHOW STATUS command is implemented in |
168 | @verbatim ./storage/perfschema/pfs_engine_table.cc @endverbatim |
169 | |
170 | @subsection INT_QUERY Query interface |
171 | |
172 | The query interface is used to query the internal state of a running server. |
173 | It is provided as SQL tables. |
174 | |
175 | For example: |
176 | @verbatim mysql> select * from performance_schema.EVENTS_WAITS_CURRENT; |
177 | @endverbatim |
178 | |
179 | @section DESIGN_PRINCIPLES Design principles |
180 | |
181 | @subsection PRINCIPLE_BEHAVIOR No behavior changes |
182 | |
183 | The primary goal of the performance schema is to measure (instrument) the |
184 | execution of the server. A good measure should not cause any change |
185 | in behavior. |
186 | |
187 | To achieve this, the overall design of the performance schema complies |
188 | with the following very severe design constraints: |
189 | |
190 | The parser is unchanged. There are no new keywords, no new statements. |
191 | This guarantees that existing applications will run the same way with or |
192 | without the performance schema. |
193 | |
194 | All the instrumentation points return "void", there are no error codes. |
195 | Even if the performance schema internally fails, execution of the server |
196 | code will proceed. |
197 | |
198 | None of the instrumentation points allocate memory. |
199 | All the memory used by the performance schema is pre-allocated at startup, |
200 | and is considered "static" during the server life time. |
201 | |
202 | None of the instrumentation points use any pthread_mutex, pthread_rwlock, |
203 | or pthread_cond (or platform equivalents). |
204 | Executing the instrumentation point should not cause thread scheduling to |
205 | change in the server. |
206 | |
207 | In other words, the implementation of the instrumentation points, |
208 | including all the code called by the instrumentation points, is: |
209 | - malloc free |
210 | - mutex free |
211 | - rwlock free |
212 | |
213 | TODO: All the code located in storage/perfschema is malloc free, |
214 | but unfortunately the usage of LF_HASH introduces some memory allocation. |
215 | This should be revised if possible, to use a lock-free, |
216 | malloc-free hash code table. |
217 | |
218 | @subsection PRINCIPLE_PERFORMANCE No performance hit |
219 | |
220 | The instrumentation of the server should be as fast as possible. |
221 | In cases when there are choices between: |
222 | - doing some processing when recording the performance data |
223 | in the instrumentation, |
224 | - doing some processing when retrieving the performance data, |
225 | |
226 | priority is given in the design to make the instrumentation faster, |
227 | pushing some complexity to data retrieval. |
228 | |
229 | As a result, some parts of the design, related to: |
230 | - the setup code path, |
231 | - the query code path, |
232 | |
233 | might appear to be sub-optimal. |
234 | |
235 | The criterion used here is to optimize primarily the critical path (data |
236 | collection), possibly at the expense of non-critical code paths. |
237 | |
238 | @subsection PRINCIPLE_NOT_INTRUSIVE Unintrusive instrumentation |
239 | |
240 | For the performance schema in general to be successful, the barrier |
241 | of entry for a developer should be low, so it's easy to instrument code. |
242 | |
243 | In particular, the instrumentation interface: |
244 | - is available for C and C++ code (so it's a C interface), |
245 | - does not require parameters that the calling code can't easily provide, |
246 | - supports partial instrumentation (for example, instrumenting mutexes does |
247 | not require that every mutex is instrumented) |
248 | |
249 | @subsection PRINCIPLE_EXTENDABLE Extendable instrumentation |
250 | |
251 | As the content of the performance schema improves, |
252 | with more tables exposed and more data collected, |
253 | the instrumentation interface will also be augmented |
254 | to support instrumenting new concepts. |
255 | Existing instrumentations should not be affected when additional |
256 | instrumentation is made available, and making a new instrumentation |
257 | available should not require existing instrumented code to support it. |
258 | |
259 | @subsection PRINCIPLE_VERSIONED Versioned instrumentation |
260 | |
261 | Given that the instrumentation offered by the performance schema will |
262 | be augmented with time, when more features are implemented, |
263 | the interface itself should be versioned, to keep compatibility |
264 | with previous instrumented code. |
265 | |
266 | For example, after both plugin-A and plugin-B have been instrumented for |
267 | mutexes, read write locks and conditions, using the instrumentation |
268 | interface, we can anticipate that the instrumentation interface |
269 | is expanded to support file based operations. |
270 | |
271 | Plugin-A, a file based storage engine, will most likely use the expanded |
272 | interface and instrument its file usage, using the version 2 |
273 | interface, while Plugin-B, a network based storage engine, will not change |
274 | its code and not release a new binary. |
275 | |
276 | When later the instrumentation interface is expanded to support network |
277 | based operations (which will define interface version 3), the Plugin-B code |
278 | can then be changed to make use of it. |
279 | |
280 | Note, this is just an example to illustrate the design concept here. |
281 | Both mutexes and file instrumentation are already available |
282 | since version 1 of the instrumentation interface. |
283 | |
284 | @subsection PRINCIPLE_DEPLOYMENT Easy deployment |
285 | |
286 | Internally, we might want every plugin implementation to upgrade the |
287 | instrumented code to the latest available, but this will cause additional |
288 | work and this is not practical if the code change is monolithic. |
289 | |
290 | Externally, for third party plugin implementors, asking implementors to |
291 | always stay aligned to the latest instrumentation and make new releases, |
292 | even when the change does not provide new functionality for them, |
293 | is a bad idea. |
294 | |
295 | For example, requiring a network based engine to re-release because the |
296 | instrumentation interface changed for file based operations, will create |
297 | too many deployment issues. |
298 | |
299 | So, the performance schema implementation must support concurrently, |
300 | in the same deployment, multiple versions of the instrumentation |
301 | interface, and ensure binary compatibility with each version. |
302 | |
303 | In addition to this, the performance schema can be included or excluded |
304 | from the server binary, using build time configuration options. |
305 | |
306 | Regardless, the following types of deployment are valid: |
307 | - a server supporting the performance schema + a storage engine |
308 | that is not instrumented |
309 | - a server not supporting the performance schema + a storage engine |
310 | that is instrumented |
311 | */ |
312 | |
313 | /** |
314 | @page PAGE_INSTRUMENTATION_INTERFACE Performance schema: instrumentation interface page. |
315 | MySQL performance schema instrumentation interface. |
316 | |
317 | @section INTRO Introduction |
318 | |
319 | The instrumentation interface consist of two layers: |
320 | - a raw ABI (Application Binary Interface) layer, that exposes the primitive |
321 | instrumentation functions exported by the performance schema instrumentation |
322 | - an API (Application Programing Interface) layer, |
323 | that provides many helpers for a developer instrumenting some code, |
324 | to make the instrumentation as easy as possible. |
325 | |
326 | The ABI layer consists of: |
327 | @code |
328 | #include "mysql/psi/psi.h" |
329 | @endcode |
330 | |
331 | The API layer consists of: |
332 | @code |
333 | #include "mysql/psi/mutex_mutex.h" |
334 | #include "mysql/psi/mutex_file.h" |
335 | @endcode |
336 | |
337 | The first helper is for mutexes, rwlocks and conditions, |
338 | the second for file io. |
339 | |
340 | The API layer exposes C macros and typedefs which will expand: |
341 | - either to non-instrumented code, when compiled without the performance |
342 | schema instrumentation |
343 | - or to instrumented code, that will issue the raw calls to the ABI layer |
344 | so that the implementation can collect data. |
345 | |
346 | Note that all the names introduced (for example, @c mysql_mutex_lock) do not |
347 | collide with any other namespace. |
348 | In particular, the macro @c mysql_mutex_lock is on purpose not named |
349 | @c pthread_mutex_lock. |
350 | This is to: |
351 | - avoid overloading @c pthread_mutex_lock with yet another macro, |
352 | which is dangerous as it can affect user code and pollute |
353 | the end-user namespace. |
354 | - allow the developer instrumenting code to selectively instrument |
355 | some code but not all. |
356 | |
357 | @section PRINCIPLES Design principles |
358 | |
359 | The ABI part is designed as a facade, that exposes basic primitives. |
360 | The expectation is that each primitive will be very stable over time, |
361 | but the list will constantly grow when more instruments are supported. |
362 | To support binary compatibility with plugins compiled with a different |
363 | version of the instrumentation, the ABI itself is versioned |
364 | (see @c PSI_v1, @c PSI_v2). |
365 | |
366 | For a given instrumentation point in the API, the basic coding pattern |
367 | used is: |
368 | - (a) notify the performance schema of the operation |
369 | about to be performed. |
370 | - (b) execute the instrumented code. |
371 | - (c) notify the performance schema that the operation |
372 | is completed. |
373 | |
374 | An opaque "locker" pointer is returned by (a), that is given to (c). |
375 | This pointer helps the implementation to keep context, for performances. |
376 | |
377 | The following code fragment is annotated to show how in detail this pattern |
378 | in implemented, when the instrumentation is compiled in: |
379 | |
380 | @verbatim |
381 | static inline int mysql_mutex_lock( |
382 | mysql_mutex_t *that, myf flags, const char *src_file, uint src_line) |
383 | { |
384 | int result; |
385 | struct PSI_mutex_locker_state state; |
386 | struct PSI_mutex_locker *locker= NULL; |
387 | |
388 | ............... (a) |
389 | locker= PSI_server->start_mutex_wait(&state, that->p_psi, |
390 | PSI_MUTEX_LOCK, locker, src_file, src_line); |
391 | |
392 | ............... (b) |
393 | result= pthread_mutex_lock(&that->m_mutex); |
394 | |
395 | ............... (c) |
396 | PSI_server->end_mutex_wait(locker, result); |
397 | |
398 | return result; |
399 | } |
400 | @endverbatim |
401 | |
402 | When the performance schema instrumentation is not compiled in, |
403 | the code becomes simply a wrapper, expanded in line by the compiler: |
404 | |
405 | @verbatim |
406 | static inline int mysql_mutex_lock(...) |
407 | { |
408 | int result; |
409 | |
410 | ............... (b) |
411 | result= pthread_mutex_lock(&that->m_mutex); |
412 | |
413 | return result; |
414 | } |
415 | @endverbatim |
416 | */ |
417 | |
418 | /** |
419 | @page PAGE_AGGREGATES Performance schema: the aggregates page. |
420 | Performance schema aggregates. |
421 | |
422 | @section INTRO Introduction |
423 | |
424 | Aggregates tables are tables that can be formally defined as |
425 | SELECT ... from EVENTS_WAITS_HISTORY_INFINITE ... group by 'group clause'. |
426 | |
427 | Each group clause defines a different kind of aggregate, and corresponds to |
428 | a different table exposed by the performance schema. |
429 | |
430 | Aggregates can be either: |
431 | - computed on the fly, |
432 | - computed on demand, based on other available data. |
433 | |
434 | 'EVENTS_WAITS_HISTORY_INFINITE' is a table that does not exist, |
435 | the best approximation is EVENTS_WAITS_HISTORY_LONG. |
436 | Aggregates computed on the fly in fact are based on EVENTS_WAITS_CURRENT, |
437 | while aggregates computed on demand are based on other |
438 | EVENTS_WAITS_SUMMARY_BY_xxx tables. |
439 | |
440 | To better understand the implementation itself, a bit of math is |
441 | required first, to understand the model behind the code: |
442 | the code is deceptively simple, the real complexity resides |
443 | in the flyweight of pointers between various performance schema buffers. |
444 | |
445 | @section DIMENSION Concept of dimension |
446 | |
447 | An event measured by the instrumentation has many attributes. |
448 | An event is represented as a data point P(x1, x2, ..., xN), |
449 | where each x_i coordinate represents a given attribute value. |
450 | |
451 | Examples of attributes are: |
452 | - the time waited |
453 | - the object waited on |
454 | - the instrument waited on |
455 | - the thread that waited |
456 | - the operation performed |
457 | - per object or per operation additional attributes, such as spins, |
458 | number of bytes, etc. |
459 | |
460 | Computing an aggregate per thread is fundamentally different from |
461 | computing an aggregate by instrument, so the "_BY_THREAD" and |
462 | "_BY_EVENT_NAME" aggregates are different dimensions, |
463 | operating on different x_i and x_j coordinates. |
464 | These aggregates are "orthogonal". |
465 | |
466 | @section PROJECTION Concept of projection |
467 | |
468 | A given x_i attribute value can convey either just one basic information, |
469 | such as a number of bytes, or can convey implied information, |
470 | such as an object fully qualified name. |
471 | |
472 | For example, from the value "test.t1", the name of the object schema |
473 | "test" can be separated from the object name "t1", so that now aggregates |
474 | by object schema can be implemented. |
475 | |
476 | In math terms, that corresponds to defining a function: |
477 | F_i (x): x --> y |
478 | Applying this function to our point P gives another point P': |
479 | |
480 | F_i (P): |
481 | P(x1, x2, ..., x{i-1}, x_i, x{i+1}, ..., x_N) |
482 | --> P' (x1, x2, ..., x{i-1}, f_i(x_i), x{i+1}, ..., x_N) |
483 | |
484 | That function defines in fact an aggregate ! |
485 | In SQL terms, this aggregate would look like the following table: |
486 | |
487 | @verbatim |
488 | CREATE VIEW EVENTS_WAITS_SUMMARY_BY_Func_i AS |
489 | SELECT col_1, col_2, ..., col_{i-1}, |
490 | Func_i(col_i), |
491 | COUNT(col_i), |
492 | MIN(col_i), AVG(col_i), MAX(col_i), -- if col_i is a numeric value |
493 | col_{i+1}, ..., col_N |
494 | FROM EVENTS_WAITS_HISTORY_INFINITE |
495 | group by col_1, col_2, ..., col_{i-1}, col{i+1}, ..., col_N. |
496 | @endverbatim |
497 | |
498 | Note that not all columns have to be included, |
499 | in particular some columns that are dependent on the x_i column should |
500 | be removed, so that in practice, MySQL's aggregation method tends to |
501 | remove many attributes at each aggregation steps. |
502 | |
503 | For example, when aggregating wait events by object instances, |
504 | - the wait_time and number_of_bytes can be summed, |
505 | and sum(wait_time) now becomes an object instance attribute. |
506 | - the source, timer_start, timer_end columns are not in the |
507 | _BY_INSTANCE table, because these attributes are only |
508 | meaningful for a wait. |
509 | |
510 | @section COMPOSITION Concept of composition |
511 | |
512 | Now, the "test.t1" --> "test" example was purely theory, |
513 | just to explain the concept, and does not lead very far. |
514 | Let's look at a more interesting example of data that can be derived |
515 | from the row event. |
516 | |
517 | An event creates a transient object, PFS_wait_locker, per operation. |
518 | This object's life cycle is extremely short: it's created just |
519 | before the start_wait() instrumentation call, and is destroyed in |
520 | the end_wait() call. |
521 | |
522 | The wait locker itself contains a pointer to the object instance |
523 | waited on. |
524 | That allows to implement a wait_locker --> object instance projection, |
525 | with m_target. |
526 | The object instance life cycle depends on _init and _destroy calls |
527 | from the code, such as mysql_mutex_init() |
528 | and mysql_mutex_destroy() for a mutex. |
529 | |
530 | The object instance waited on contains a pointer to the object class, |
531 | which is represented by the instrument name. |
532 | That allows to implement an object instance --> object class projection. |
533 | The object class life cycle is permanent, as instruments are loaded in |
534 | the server and never removed. |
535 | |
536 | The object class is named in such a way |
537 | (for example, "wait/sync/mutex/sql/LOCK_open", |
538 | "wait/io/file/maria/data_file) that the component ("sql", "maria") |
539 | that it belongs to can be inferred. |
540 | That allows to implement an object class --> server component projection. |
541 | |
542 | Back to math again, we have, for example for mutexes: |
543 | |
544 | F1 (l) : PFS_wait_locker l --> PFS_mutex m = l->m_target.m_mutex |
545 | |
546 | F1_to_2 (m) : PFS_mutex m --> PFS_mutex_class i = m->m_class |
547 | |
548 | F2_to_3 (i) : PFS_mutex_class i --> const char *component = |
549 | substring(i->m_name, ...) |
550 | |
551 | Per components aggregates are not implemented, this is just an illustration. |
552 | |
553 | F1 alone defines this aggregate: |
554 | |
555 | EVENTS_WAITS_HISTORY_INFINITE --> EVENTS_WAITS_SUMMARY_BY_INSTANCE |
556 | (or MUTEX_INSTANCE) |
557 | |
558 | F1_to_2 alone could define this aggregate: |
559 | |
560 | EVENTS_WAITS_SUMMARY_BY_INSTANCE --> EVENTS_WAITS_SUMMARY_BY_EVENT_NAME |
561 | |
562 | Alternatively, using function composition, with |
563 | F2 = F1_to_2 o F1, F2 defines: |
564 | |
565 | EVENTS_WAITS_HISTORY_INFINITE --> EVENTS_WAITS_SUMMARY_BY_EVENT_NAME |
566 | |
567 | Likewise, F_2_to_3 defines: |
568 | |
569 | EVENTS_WAITS_SUMMARY_BY_EVENT_NAME --> EVENTS_WAITS_SUMMARY_BY_COMPONENT |
570 | |
571 | and F3 = F_2_to_3 o F_1_to_2 o F1 defines: |
572 | |
573 | EVENTS_WAITS_HISTORY_INFINITE --> EVENTS_WAITS_SUMMARY_BY_COMPONENT |
574 | |
575 | What has all this to do with the code ? |
576 | |
577 | Functions (or aggregates) such as F_3 are not implemented as is. |
578 | Instead, they are decomposed into F_2_to_3 o F_1_to_2 o F1, |
579 | and each intermediate aggregate is stored into an internal buffer. |
580 | This allows to support every F1, F2, F3 aggregates from shared |
581 | internal buffers, where computation already performed to compute F2 |
582 | is reused when computing F3. |
583 | |
584 | @section OBJECT_GRAPH Object graph |
585 | |
586 | In terms of object instances, or records, pointers between |
587 | different buffers define an object instance graph. |
588 | |
589 | For example, assuming the following scenario: |
590 | - A mutex class "M" is instrumented, the instrument name |
591 | is "wait/sync/mutex/sql/M" |
592 | - This mutex instrument has been instantiated twice, |
593 | mutex instances are noted M-1 and M-2 |
594 | - Threads T-A and T-B are locking mutex instance M-1 |
595 | - Threads T-C and T-D are locking mutex instance M-2 |
596 | |
597 | The performance schema will record the following data: |
598 | - EVENTS_WAITS_CURRENT has 4 rows, one for each mutex locker |
599 | - EVENTS_WAITS_SUMMARY_BY_INSTANCE shows 2 rows, for M-1 and M-2 |
600 | - EVENTS_WAITS_SUMMARY_BY_EVENT_NAME shows 1 row, for M |
601 | |
602 | The graph of structures will look like: |
603 | |
604 | @verbatim |
605 | PFS_wait_locker (T-A, M-1) ---------- |
606 | | |
607 | v |
608 | PFS_mutex (M-1) |
609 | - m_wait_stat ------------ |
610 | ^ | |
611 | | | |
612 | PFS_wait_locker (T-B, M-1) ---------- | |
613 | v |
614 | PFS_mutex_class (M) |
615 | - m_wait_stat |
616 | PFS_wait_locker (T-C, M-2) ---------- ^ |
617 | | | |
618 | v | |
619 | PFS_mutex (M-2) | |
620 | - m_wait_stat ------------ |
621 | ^ |
622 | | |
623 | PFS_wait_locker (T-D, M-2) ---------- |
624 | |
625 | || || || |
626 | || || || |
627 | vv vv vv |
628 | |
629 | EVENTS_WAITS_CURRENT ..._SUMMARY_BY_INSTANCE ..._SUMMARY_BY_EVENT_NAME |
630 | @endverbatim |
631 | |
632 | @section ON_THE_FLY On the fly aggregates |
633 | |
634 | 'On the fly' aggregates are computed during the code execution. |
635 | This is necessary because the data the aggregate is based on is volatile, |
636 | and can not be kept indefinitely. |
637 | |
638 | With on the fly aggregates: |
639 | - the writer thread does all the computation |
640 | - the reader thread accesses the result directly |
641 | |
642 | This model is to be avoided if possible, due to the overhead |
643 | caused when instrumenting code. |
644 | |
645 | @section HIGHER_LEVEL Higher level aggregates |
646 | |
647 | 'Higher level' aggregates are implemented on demand only. |
648 | The code executing a SELECT from the aggregate table is |
649 | collecting data from multiple internal buffers to produce the result. |
650 | |
651 | With higher level aggregates: |
652 | - the reader thread does all the computation |
653 | - the writer thread has no overhead. |
654 | |
655 | @section MIXED Mixed level aggregates |
656 | |
657 | The 'Mixed' model is a compromise between 'On the fly' and 'Higher level' |
658 | aggregates, for internal buffers that are not permanent. |
659 | |
660 | While an object is present in a buffer, the higher level model is used. |
661 | When an object is about to be destroyed, statistics are saved into |
662 | a 'parent' buffer with a longer life cycle, to follow the on the fly model. |
663 | |
664 | With mixed aggregates: |
665 | - the reader thread does a lot of complex computation, |
666 | - the writer thread has minimal overhead, on destroy events. |
667 | |
668 | @section IMPL_WAIT Implementation for waits aggregates |
669 | |
670 | For waits, the tables that contains aggregated wait data are: |
671 | - EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME |
672 | - EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME |
673 | - EVENTS_WAITS_SUMMARY_BY_INSTANCE |
674 | - EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME |
675 | - EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME |
676 | - EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME |
677 | - FILE_SUMMARY_BY_EVENT_NAME |
678 | - FILE_SUMMARY_BY_INSTANCE |
679 | - SOCKET_SUMMARY_BY_INSTANCE |
680 | - SOCKET_SUMMARY_BY_EVENT_NAME |
681 | - OBJECTS_SUMMARY_GLOBAL_BY_TYPE |
682 | |
683 | The instrumented code that generates waits events consist of: |
684 | - mutexes (mysql_mutex_t) |
685 | - rwlocks (mysql_rwlock_t) |
686 | - conditions (mysql_cond_t) |
687 | - file io (MYSQL_FILE) |
688 | - socket io (MYSQL_SOCKET) |
689 | - table io |
690 | - table lock |
691 | - idle |
692 | |
693 | The flow of data between aggregates tables varies for each instrumentation. |
694 | |
695 | @subsection IMPL_WAIT_MUTEX Mutex waits |
696 | |
697 | @verbatim |
698 | mutex_locker(T, M) |
699 | | |
700 | | [1] |
701 | | |
702 | |-> pfs_mutex(M) =====>> [B], [C] |
703 | | | |
704 | | | [2] |
705 | | | |
706 | | |-> pfs_mutex_class(M.class) =====>> [C] |
707 | | |
708 | |-> pfs_thread(T).event_name(M) =====>> [A], [D], [E], [F] |
709 | | |
710 | | [3] |
711 | | |
712 | 3a |-> pfs_account(U, H).event_name(M) =====>> [D], [E], [F] |
713 | . | |
714 | . | [4-RESET] |
715 | . | |
716 | 3b .....+-> pfs_user(U).event_name(M) =====>> [E] |
717 | . | |
718 | 3c .....+-> pfs_host(H).event_name(M) =====>> [F] |
719 | @endverbatim |
720 | |
721 | How to read this diagram: |
722 | - events that occur during the instrumented code execution are noted with numbers, |
723 | as in [1]. Code executed by these events has an impact on overhead. |
724 | - events that occur during TRUNCATE TABLE operations are noted with numbers, |
725 | followed by "-RESET", as in [4-RESET]. |
726 | Code executed by these events has no impact on overhead, |
727 | since they are executed by independent monitoring sessions. |
728 | - events that occur when a reader extracts data from a performance schema table |
729 | are noted with letters, as in [A]. The name of the table involved, |
730 | and the method that builds a row are documented. Code executed by these events |
731 | has no impact on the instrumentation overhead. Note that the table |
732 | implementation may pull data from different buffers. |
733 | - nominal code paths are in plain lines. A "nominal" code path corresponds to |
734 | cases where the performance schema buffers are sized so that no records are lost. |
735 | - degenerated code paths are in dotted lines. A "degenerated" code path corresponds |
736 | to edge cases where parent buffers are full, which forces the code to aggregate to |
737 | grand parents directly. |
738 | |
739 | Implemented as: |
740 | - [1] @c start_mutex_wait_v1(), @c end_mutex_wait_v1() |
741 | - [2] @c destroy_mutex_v1() |
742 | - [3] @c aggregate_thread_waits() |
743 | - [4] @c PFS_account::aggregate_waits() |
744 | - [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME, |
745 | @c table_ews_by_thread_by_event_name::make_row() |
746 | - [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE, |
747 | @c table_events_waits_summary_by_instance::make_mutex_row() |
748 | - [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME, |
749 | @c table_ews_global_by_event_name::make_mutex_row() |
750 | - [D] EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME, |
751 | @c table_ews_by_account_by_event_name::make_row() |
752 | - [E] EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME, |
753 | @c table_ews_by_user_by_event_name::make_row() |
754 | - [F] EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME, |
755 | @c table_ews_by_host_by_event_name::make_row() |
756 | |
757 | Table EVENTS_WAITS_SUMMARY_BY_INSTANCE is a 'on the fly' aggregate, |
758 | because the data is collected on the fly by (1) and stored into a buffer, |
759 | pfs_mutex. The table implementation [B] simply reads the results directly |
760 | from this buffer. |
761 | |
762 | Table EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME is a 'mixed' aggregate, |
763 | because some data is collected on the fly (1), |
764 | some data is preserved with (2) at a later time in the life cycle, |
765 | and two different buffers pfs_mutex and pfs_mutex_class are used to store the |
766 | statistics collected. The table implementation [C] is more complex, since |
767 | it reads from two buffers pfs_mutex and pfs_mutex_class. |
768 | |
769 | @subsection IMPL_WAIT_RWLOCK Rwlock waits |
770 | |
771 | @verbatim |
772 | rwlock_locker(T, R) |
773 | | |
774 | | [1] |
775 | | |
776 | |-> pfs_rwlock(R) =====>> [B], [C] |
777 | | | |
778 | | | [2] |
779 | | | |
780 | | |-> pfs_rwlock_class(R.class) =====>> [C] |
781 | | |
782 | |-> pfs_thread(T).event_name(R) =====>> [A] |
783 | | |
784 | ... |
785 | @endverbatim |
786 | |
787 | Implemented as: |
788 | - [1] @c start_rwlock_rdwait_v1(), @c end_rwlock_rdwait_v1(), ... |
789 | - [2] @c destroy_rwlock_v1() |
790 | - [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME, |
791 | @c table_ews_by_thread_by_event_name::make_row() |
792 | - [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE, |
793 | @c table_events_waits_summary_by_instance::make_rwlock_row() |
794 | - [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME, |
795 | @c table_ews_global_by_event_name::make_rwlock_row() |
796 | |
797 | @subsection IMPL_WAIT_COND Cond waits |
798 | |
799 | @verbatim |
800 | cond_locker(T, C) |
801 | | |
802 | | [1] |
803 | | |
804 | |-> pfs_cond(C) =====>> [B], [C] |
805 | | | |
806 | | | [2] |
807 | | | |
808 | | |-> pfs_cond_class(C.class) =====>> [C] |
809 | | |
810 | |-> pfs_thread(T).event_name(C) =====>> [A] |
811 | | |
812 | ... |
813 | @endverbatim |
814 | |
815 | Implemented as: |
816 | - [1] @c start_cond_wait_v1(), @c end_cond_wait_v1() |
817 | - [2] @c destroy_cond_v1() |
818 | - [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME, |
819 | @c table_ews_by_thread_by_event_name::make_row() |
820 | - [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE, |
821 | @c table_events_waits_summary_by_instance::make_cond_row() |
822 | - [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME, |
823 | @c table_ews_global_by_event_name::make_cond_row() |
824 | |
825 | @subsection IMPL_WAIT_FILE File waits |
826 | |
827 | @verbatim |
828 | file_locker(T, F) |
829 | | |
830 | | [1] |
831 | | |
832 | |-> pfs_file(F) =====>> [B], [C], [D], [E] |
833 | | | |
834 | | | [2] |
835 | | | |
836 | | |-> pfs_file_class(F.class) =====>> [C], [D] |
837 | | |
838 | |-> pfs_thread(T).event_name(F) =====>> [A] |
839 | | |
840 | ... |
841 | @endverbatim |
842 | |
843 | Implemented as: |
844 | - [1] @c get_thread_file_name_locker_v1(), @c start_file_wait_v1(), |
845 | @c end_file_wait_v1(), ... |
846 | - [2] @c close_file_v1() |
847 | - [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME, |
848 | @c table_ews_by_thread_by_event_name::make_row() |
849 | - [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE, |
850 | @c table_events_waits_summary_by_instance::make_file_row() |
851 | - [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME, |
852 | @c table_ews_global_by_event_name::make_file_row() |
853 | - [D] FILE_SUMMARY_BY_EVENT_NAME, |
854 | @c table_file_summary_by_event_name::make_row() |
855 | - [E] FILE_SUMMARY_BY_INSTANCE, |
856 | @c table_file_summary_by_instance::make_row() |
857 | |
858 | @subsection IMPL_WAIT_SOCKET Socket waits |
859 | |
860 | @verbatim |
861 | socket_locker(T, S) |
862 | | |
863 | | [1] |
864 | | |
865 | |-> pfs_socket(S) =====>> [A], [B], [C], [D], [E] |
866 | | |
867 | | [2] |
868 | | |
869 | |-> pfs_socket_class(S.class) =====>> [C], [D] |
870 | | |
871 | |-> pfs_thread(T).event_name(S) =====>> [A] |
872 | | |
873 | | [3] |
874 | | |
875 | 3a |-> pfs_account(U, H).event_name(S) =====>> [F], [G], [H] |
876 | . | |
877 | . | [4-RESET] |
878 | . | |
879 | 3b .....+-> pfs_user(U).event_name(S) =====>> [G] |
880 | . | |
881 | 3c .....+-> pfs_host(H).event_name(S) =====>> [H] |
882 | @endverbatim |
883 | |
884 | Implemented as: |
885 | - [1] @c start_socket_wait_v1(), @c end_socket_wait_v1(). |
886 | - [2] @c close_socket_v1() |
887 | - [3] @c aggregate_thread_waits() |
888 | - [4] @c PFS_account::aggregate_waits() |
889 | - [5] @c PFS_host::aggregate_waits() |
890 | - [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME, |
891 | @c table_ews_by_thread_by_event_name::make_row() |
892 | - [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE, |
893 | @c table_events_waits_summary_by_instance::make_socket_row() |
894 | - [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME, |
895 | @c table_ews_global_by_event_name::make_socket_row() |
896 | - [D] SOCKET_SUMMARY_BY_EVENT_NAME, |
897 | @c table_socket_summary_by_event_name::make_row() |
898 | - [E] SOCKET_SUMMARY_BY_INSTANCE, |
899 | @c table_socket_summary_by_instance::make_row() |
900 | - [F] EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME, |
901 | @c table_ews_by_account_by_event_name::make_row() |
902 | - [G] EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME, |
903 | @c table_ews_by_user_by_event_name::make_row() |
904 | - [H] EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME, |
905 | @c table_ews_by_host_by_event_name::make_row() |
906 | |
907 | @subsection IMPL_WAIT_TABLE Table waits |
908 | |
909 | @verbatim |
910 | table_locker(Thread Th, Table Tb, Event = io or lock) |
911 | | |
912 | | [1] |
913 | | |
914 | 1a |-> pfs_table(Tb) =====>> [A], [B], [C] |
915 | | | |
916 | | | [2] |
917 | | | |
918 | | |-> pfs_table_share(Tb.share) =====>> [B], [C] |
919 | | | |
920 | | | [3] |
921 | | | |
922 | | |-> global_table_io_stat =====>> [C] |
923 | | | |
924 | | |-> global_table_lock_stat =====>> [C] |
925 | | |
926 | 1b |-> pfs_thread(Th).event_name(E) =====>> [D], [E], [F], [G] |
927 | | | |
928 | | | [ 4-RESET] |
929 | | | |
930 | | |-> pfs_account(U, H).event_name(E) =====>> [E], [F], [G] |
931 | | . | |
932 | | . | [5-RESET] |
933 | | . | |
934 | | .....+-> pfs_user(U).event_name(E) =====>> [F] |
935 | | . | |
936 | | .....+-> pfs_host(H).event_name(E) =====>> [G] |
937 | | |
938 | 1c |-> pfs_thread(Th).waits_current(W) =====>> [H] |
939 | | |
940 | 1d |-> pfs_thread(Th).waits_history(W) =====>> [I] |
941 | | |
942 | 1e |-> waits_history_long(W) =====>> [J] |
943 | @endverbatim |
944 | |
945 | Implemented as: |
946 | - [1] @c start_table_io_wait_v1(), @c end_table_io_wait_v1() |
947 | - [2] @c close_table_v1() |
948 | - [3] @c drop_table_share_v1() |
949 | - [4] @c TRUNCATE TABLE EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME |
950 | - [5] @c TRUNCATE TABLE EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME |
951 | - [A] EVENTS_WAITS_SUMMARY_BY_INSTANCE, |
952 | @c table_events_waits_summary_by_instance::make_table_row() |
953 | - [B] OBJECTS_SUMMARY_GLOBAL_BY_TYPE, |
954 | @c table_os_global_by_type::make_row() |
955 | - [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME, |
956 | @c table_ews_global_by_event_name::make_table_io_row(), |
957 | @c table_ews_global_by_event_name::make_table_lock_row() |
958 | - [D] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME, |
959 | @c table_ews_by_thread_by_event_name::make_row() |
960 | - [E] EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME, |
961 | @c table_ews_by_user_by_account_name::make_row() |
962 | - [F] EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME, |
963 | @c table_ews_by_user_by_event_name::make_row() |
964 | - [G] EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME, |
965 | @c table_ews_by_host_by_event_name::make_row() |
966 | - [H] EVENTS_WAITS_CURRENT, |
967 | @c table_events_waits_common::make_row() |
968 | - [I] EVENTS_WAITS_HISTORY, |
969 | @c table_events_waits_common::make_row() |
970 | - [J] EVENTS_WAITS_HISTORY_LONG, |
971 | @c table_events_waits_common::make_row() |
972 | |
973 | @section IMPL_STAGE Implementation for stages aggregates |
974 | |
975 | For stages, the tables that contains aggregated data are: |
976 | - EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME |
977 | - EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME |
978 | - EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME |
979 | - EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME |
980 | - EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME |
981 | |
982 | @verbatim |
983 | start_stage(T, S) |
984 | | |
985 | | [1] |
986 | | |
987 | 1a |-> pfs_thread(T).event_name(S) =====>> [A], [B], [C], [D], [E] |
988 | | | |
989 | | | [2] |
990 | | | |
991 | | 2a |-> pfs_account(U, H).event_name(S) =====>> [B], [C], [D], [E] |
992 | | . | |
993 | | . | [3-RESET] |
994 | | . | |
995 | | 2b .....+-> pfs_user(U).event_name(S) =====>> [C] |
996 | | . | |
997 | | 2c .....+-> pfs_host(H).event_name(S) =====>> [D], [E] |
998 | | . . | |
999 | | . . | [4-RESET] |
1000 | | 2d . . | |
1001 | 1b |----+----+----+-> pfs_stage_class(S) =====>> [E] |
1002 | |
1003 | @endverbatim |
1004 | |
1005 | Implemented as: |
1006 | - [1] @c start_stage_v1() |
1007 | - [2] @c delete_thread_v1(), @c aggregate_thread_stages() |
1008 | - [3] @c PFS_account::aggregate_stages() |
1009 | - [4] @c PFS_host::aggregate_stages() |
1010 | - [A] EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME, |
1011 | @c table_esgs_by_thread_by_event_name::make_row() |
1012 | - [B] EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME, |
1013 | @c table_esgs_by_account_by_event_name::make_row() |
1014 | - [C] EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME, |
1015 | @c table_esgs_by_user_by_event_name::make_row() |
1016 | - [D] EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME, |
1017 | @c table_esgs_by_host_by_event_name::make_row() |
1018 | - [E] EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME, |
1019 | @c table_esgs_global_by_event_name::make_row() |
1020 | |
1021 | @section IMPL_STATEMENT Implementation for statements consumers |
1022 | |
1023 | For statements, the tables that contains individual event data are: |
1024 | - EVENTS_STATEMENTS_CURRENT |
1025 | - EVENTS_STATEMENTS_HISTORY |
1026 | - EVENTS_STATEMENTS_HISTORY_LONG |
1027 | |
1028 | For statements, the tables that contains aggregated data are: |
1029 | - EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME |
1030 | - EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME |
1031 | - EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME |
1032 | - EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME |
1033 | - EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME |
1034 | - EVENTS_STATEMENTS_SUMMARY_BY_DIGEST |
1035 | |
1036 | @verbatim |
1037 | statement_locker(T, S) |
1038 | | |
1039 | | [1] |
1040 | | |
1041 | 1a |-> pfs_thread(T).event_name(S) =====>> [A], [B], [C], [D], [E] |
1042 | | | |
1043 | | | [2] |
1044 | | | |
1045 | | 2a |-> pfs_account(U, H).event_name(S) =====>> [B], [C], [D], [E] |
1046 | | . | |
1047 | | . | [3-RESET] |
1048 | | . | |
1049 | | 2b .....+-> pfs_user(U).event_name(S) =====>> [C] |
1050 | | . | |
1051 | | 2c .....+-> pfs_host(H).event_name(S) =====>> [D], [E] |
1052 | | . . | |
1053 | | . . | [4-RESET] |
1054 | | 2d . . | |
1055 | 1b |----+----+----+-> pfs_statement_class(S) =====>> [E] |
1056 | | |
1057 | 1c |-> pfs_thread(T).statement_current(S) =====>> [F] |
1058 | | |
1059 | 1d |-> pfs_thread(T).statement_history(S) =====>> [G] |
1060 | | |
1061 | 1e |-> statement_history_long(S) =====>> [H] |
1062 | | |
1063 | 1f |-> statement_digest(S) =====>> [I] |
1064 | |
1065 | @endverbatim |
1066 | |
1067 | Implemented as: |
1068 | - [1] @c start_statement_v1(), end_statement_v1() |
1069 | (1a, 1b) is an aggregation by EVENT_NAME, |
1070 | (1c, 1d, 1e) is an aggregation by TIME, |
1071 | (1f) is an aggregation by DIGEST |
1072 | all of these are orthogonal, |
1073 | and implemented in end_statement_v1(). |
1074 | - [2] @c delete_thread_v1(), @c aggregate_thread_statements() |
1075 | - [3] @c PFS_account::aggregate_statements() |
1076 | - [4] @c PFS_host::aggregate_statements() |
1077 | - [A] EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME, |
1078 | @c table_esms_by_thread_by_event_name::make_row() |
1079 | - [B] EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME, |
1080 | @c table_esms_by_account_by_event_name::make_row() |
1081 | - [C] EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME, |
1082 | @c table_esms_by_user_by_event_name::make_row() |
1083 | - [D] EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME, |
1084 | @c table_esms_by_host_by_event_name::make_row() |
1085 | - [E] EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME, |
1086 | @c table_esms_global_by_event_name::make_row() |
1087 | - [F] EVENTS_STATEMENTS_CURRENT, |
1088 | @c table_events_statements_current::rnd_next(), |
1089 | @c table_events_statements_common::make_row() |
1090 | - [G] EVENTS_STATEMENTS_HISTORY, |
1091 | @c table_events_statements_history::rnd_next(), |
1092 | @c table_events_statements_common::make_row() |
1093 | - [H] EVENTS_STATEMENTS_HISTORY_LONG, |
1094 | @c table_events_statements_history_long::rnd_next(), |
1095 | @c table_events_statements_common::make_row() |
1096 | - [I] EVENTS_STATEMENTS_SUMMARY_BY_DIGEST |
1097 | @c table_esms_by_digest::make_row() |
1098 | */ |
1099 | |
1100 | /** |
1101 | @defgroup Performance_schema Performance Schema |
1102 | The performance schema component. |
1103 | For details, see the |
1104 | @ref PAGE_PERFORMANCE_SCHEMA "performance schema main page". |
1105 | |
1106 | @defgroup Performance_schema_implementation Performance Schema Implementation |
1107 | @ingroup Performance_schema |
1108 | |
1109 | @defgroup Performance_schema_tables Performance Schema Tables |
1110 | @ingroup Performance_schema_implementation |
1111 | */ |
1112 | |
1113 | pthread_key(PFS_thread*, THR_PFS); |
1114 | bool THR_PFS_initialized= false; |
1115 | |
1116 | /** |
1117 | Conversion map from PSI_mutex_operation to enum_operation_type. |
1118 | Indexed by enum PSI_mutex_operation. |
1119 | */ |
1120 | static enum_operation_type mutex_operation_map[]= |
1121 | { |
1122 | OPERATION_TYPE_LOCK, |
1123 | OPERATION_TYPE_TRYLOCK |
1124 | }; |
1125 | |
1126 | /** |
1127 | Conversion map from PSI_rwlock_operation to enum_operation_type. |
1128 | Indexed by enum PSI_rwlock_operation. |
1129 | */ |
1130 | static enum_operation_type rwlock_operation_map[]= |
1131 | { |
1132 | OPERATION_TYPE_READLOCK, |
1133 | OPERATION_TYPE_WRITELOCK, |
1134 | OPERATION_TYPE_TRYREADLOCK, |
1135 | OPERATION_TYPE_TRYWRITELOCK |
1136 | }; |
1137 | |
1138 | /** |
1139 | Conversion map from PSI_cond_operation to enum_operation_type. |
1140 | Indexed by enum PSI_cond_operation. |
1141 | */ |
1142 | static enum_operation_type cond_operation_map[]= |
1143 | { |
1144 | OPERATION_TYPE_WAIT, |
1145 | OPERATION_TYPE_TIMEDWAIT |
1146 | }; |
1147 | |
1148 | /** |
1149 | Conversion map from PSI_file_operation to enum_operation_type. |
1150 | Indexed by enum PSI_file_operation. |
1151 | */ |
1152 | static enum_operation_type file_operation_map[]= |
1153 | { |
1154 | OPERATION_TYPE_FILECREATE, |
1155 | OPERATION_TYPE_FILECREATETMP, |
1156 | OPERATION_TYPE_FILEOPEN, |
1157 | OPERATION_TYPE_FILESTREAMOPEN, |
1158 | OPERATION_TYPE_FILECLOSE, |
1159 | OPERATION_TYPE_FILESTREAMCLOSE, |
1160 | OPERATION_TYPE_FILEREAD, |
1161 | OPERATION_TYPE_FILEWRITE, |
1162 | OPERATION_TYPE_FILESEEK, |
1163 | OPERATION_TYPE_FILETELL, |
1164 | OPERATION_TYPE_FILEFLUSH, |
1165 | OPERATION_TYPE_FILESTAT, |
1166 | OPERATION_TYPE_FILEFSTAT, |
1167 | OPERATION_TYPE_FILECHSIZE, |
1168 | OPERATION_TYPE_FILEDELETE, |
1169 | OPERATION_TYPE_FILERENAME, |
1170 | OPERATION_TYPE_FILESYNC |
1171 | }; |
1172 | |
1173 | /** |
1174 | Conversion map from PSI_table_operation to enum_operation_type. |
1175 | Indexed by enum PSI_table_io_operation. |
1176 | */ |
1177 | static enum_operation_type table_io_operation_map[]= |
1178 | { |
1179 | OPERATION_TYPE_TABLE_FETCH, |
1180 | OPERATION_TYPE_TABLE_WRITE_ROW, |
1181 | OPERATION_TYPE_TABLE_UPDATE_ROW, |
1182 | OPERATION_TYPE_TABLE_DELETE_ROW |
1183 | }; |
1184 | |
1185 | /** |
1186 | Conversion map from enum PFS_TL_LOCK_TYPE to enum_operation_type. |
1187 | Indexed by enum PFS_TL_LOCK_TYPE. |
1188 | */ |
1189 | static enum_operation_type table_lock_operation_map[]= |
1190 | { |
1191 | OPERATION_TYPE_TL_READ_NORMAL, /* PFS_TL_READ */ |
1192 | OPERATION_TYPE_TL_READ_WITH_SHARED_LOCKS, /* PFS_TL_READ_WITH_SHARED_LOCKS */ |
1193 | OPERATION_TYPE_TL_READ_HIGH_PRIORITY, /* PFS_TL_READ_HIGH_PRIORITY */ |
1194 | OPERATION_TYPE_TL_READ_NO_INSERTS, /* PFS_TL_READ_NO_INSERT */ |
1195 | OPERATION_TYPE_TL_WRITE_ALLOW_WRITE, /* PFS_TL_WRITE_ALLOW_WRITE */ |
1196 | OPERATION_TYPE_TL_WRITE_CONCURRENT_INSERT, /* PFS_TL_WRITE_CONCURRENT_INSERT */ |
1197 | OPERATION_TYPE_TL_WRITE_DELAYED, /* PFS_TL_WRITE_DELAYED */ |
1198 | OPERATION_TYPE_TL_WRITE_LOW_PRIORITY, /* PFS_TL_WRITE_LOW_PRIORITY */ |
1199 | OPERATION_TYPE_TL_WRITE_NORMAL, /* PFS_TL_WRITE */ |
1200 | OPERATION_TYPE_TL_READ_EXTERNAL, /* PFS_TL_READ_EXTERNAL */ |
1201 | OPERATION_TYPE_TL_WRITE_EXTERNAL /* PFS_TL_WRITE_EXTERNAL */ |
1202 | }; |
1203 | |
1204 | /** |
1205 | Conversion map from PSI_socket_operation to enum_operation_type. |
1206 | Indexed by enum PSI_socket_operation. |
1207 | */ |
1208 | static enum_operation_type socket_operation_map[]= |
1209 | { |
1210 | OPERATION_TYPE_SOCKETCREATE, |
1211 | OPERATION_TYPE_SOCKETCONNECT, |
1212 | OPERATION_TYPE_SOCKETBIND, |
1213 | OPERATION_TYPE_SOCKETCLOSE, |
1214 | OPERATION_TYPE_SOCKETSEND, |
1215 | OPERATION_TYPE_SOCKETRECV, |
1216 | OPERATION_TYPE_SOCKETSENDTO, |
1217 | OPERATION_TYPE_SOCKETRECVFROM, |
1218 | OPERATION_TYPE_SOCKETSENDMSG, |
1219 | OPERATION_TYPE_SOCKETRECVMSG, |
1220 | OPERATION_TYPE_SOCKETSEEK, |
1221 | OPERATION_TYPE_SOCKETOPT, |
1222 | OPERATION_TYPE_SOCKETSTAT, |
1223 | OPERATION_TYPE_SOCKETSHUTDOWN, |
1224 | OPERATION_TYPE_SOCKETSELECT |
1225 | }; |
1226 | |
1227 | /** |
1228 | Build the prefix name of a class of instruments in a category. |
1229 | For example, this function builds the string 'wait/sync/mutex/sql/' from |
1230 | a prefix 'wait/sync/mutex' and a category 'sql'. |
1231 | This prefix is used later to build each instrument name, such as |
1232 | 'wait/sync/mutex/sql/LOCK_open'. |
1233 | @param prefix Prefix for this class of instruments |
1234 | @param category Category name |
1235 | @param [out] output Buffer of length PFS_MAX_INFO_NAME_LENGTH. |
1236 | @param [out] output_length Length of the resulting output string. |
1237 | @return 0 for success, non zero for errors |
1238 | */ |
1239 | static int build_prefix(const LEX_CSTRING *prefix, const char *category, |
1240 | char *output, int *output_length) |
1241 | { |
1242 | size_t len= strlen(category); |
1243 | char *out_ptr= output; |
1244 | size_t prefix_length= prefix->length; |
1245 | |
1246 | if (unlikely((prefix_length + len + 1) >= |
1247 | PFS_MAX_FULL_PREFIX_NAME_LENGTH)) |
1248 | { |
1249 | pfs_print_error("build_prefix: prefix+category is too long <%s> <%s>\n" , |
1250 | prefix->str, category); |
1251 | return 1; |
1252 | } |
1253 | |
1254 | if (unlikely(strchr(category, '/') != NULL)) |
1255 | { |
1256 | pfs_print_error("build_prefix: invalid category <%s>\n" , |
1257 | category); |
1258 | return 1; |
1259 | } |
1260 | |
1261 | /* output = prefix + category + '/' */ |
1262 | memcpy(out_ptr, prefix->str, prefix_length); |
1263 | out_ptr+= prefix_length; |
1264 | memcpy(out_ptr, category, len); |
1265 | out_ptr+= len; |
1266 | *out_ptr= '/'; |
1267 | out_ptr++; |
1268 | *output_length= (int)(out_ptr - output); |
1269 | |
1270 | return 0; |
1271 | } |
1272 | |
1273 | #define REGISTER_BODY_V1(KEY_T, PREFIX, REGISTER_FUNC) \ |
1274 | KEY_T key; \ |
1275 | char formatted_name[PFS_MAX_INFO_NAME_LENGTH]; \ |
1276 | int prefix_length; \ |
1277 | int len; \ |
1278 | int full_length; \ |
1279 | \ |
1280 | DBUG_ASSERT(category != NULL); \ |
1281 | DBUG_ASSERT(info != NULL); \ |
1282 | if (unlikely(build_prefix(&PREFIX, category, \ |
1283 | formatted_name, &prefix_length))) \ |
1284 | { \ |
1285 | for (; count>0; count--, info++) \ |
1286 | *(info->m_key)= 0; \ |
1287 | return ; \ |
1288 | } \ |
1289 | \ |
1290 | for (; count>0; count--, info++) \ |
1291 | { \ |
1292 | DBUG_ASSERT(info->m_key != NULL); \ |
1293 | DBUG_ASSERT(info->m_name != NULL); \ |
1294 | len= (int)strlen(info->m_name); \ |
1295 | full_length= prefix_length + len; \ |
1296 | if (likely(full_length <= PFS_MAX_INFO_NAME_LENGTH)) \ |
1297 | { \ |
1298 | memcpy(formatted_name + prefix_length, info->m_name, len); \ |
1299 | key= REGISTER_FUNC(formatted_name, full_length, info->m_flags); \ |
1300 | } \ |
1301 | else \ |
1302 | { \ |
1303 | pfs_print_error("REGISTER_BODY_V1: name too long <%s> <%s>\n", \ |
1304 | category, info->m_name); \ |
1305 | key= 0; \ |
1306 | } \ |
1307 | \ |
1308 | *(info->m_key)= key; \ |
1309 | } \ |
1310 | return; |
1311 | |
1312 | /* Use C linkage for the interface functions. */ |
1313 | |
1314 | C_MODE_START |
1315 | |
1316 | /** |
1317 | Implementation of the mutex instrumentation interface. |
1318 | @sa PSI_v1::register_mutex. |
1319 | */ |
1320 | static void register_mutex_v1(const char *category, |
1321 | PSI_mutex_info_v1 *info, |
1322 | int count) |
1323 | { |
1324 | REGISTER_BODY_V1(PSI_mutex_key, |
1325 | mutex_instrument_prefix, |
1326 | register_mutex_class) |
1327 | } |
1328 | |
1329 | /** |
1330 | Implementation of the rwlock instrumentation interface. |
1331 | @sa PSI_v1::register_rwlock. |
1332 | */ |
1333 | static void register_rwlock_v1(const char *category, |
1334 | PSI_rwlock_info_v1 *info, |
1335 | int count) |
1336 | { |
1337 | REGISTER_BODY_V1(PSI_rwlock_key, |
1338 | rwlock_instrument_prefix, |
1339 | register_rwlock_class) |
1340 | } |
1341 | |
1342 | /** |
1343 | Implementation of the cond instrumentation interface. |
1344 | @sa PSI_v1::register_cond. |
1345 | */ |
1346 | static void register_cond_v1(const char *category, |
1347 | PSI_cond_info_v1 *info, |
1348 | int count) |
1349 | { |
1350 | REGISTER_BODY_V1(PSI_cond_key, |
1351 | cond_instrument_prefix, |
1352 | register_cond_class) |
1353 | } |
1354 | |
1355 | /** |
1356 | Implementation of the thread instrumentation interface. |
1357 | @sa PSI_v1::register_thread. |
1358 | */ |
1359 | static void register_thread_v1(const char *category, |
1360 | PSI_thread_info_v1 *info, |
1361 | int count) |
1362 | { |
1363 | REGISTER_BODY_V1(PSI_thread_key, |
1364 | thread_instrument_prefix, |
1365 | register_thread_class) |
1366 | } |
1367 | |
1368 | /** |
1369 | Implementation of the file instrumentation interface. |
1370 | @sa PSI_v1::register_file. |
1371 | */ |
1372 | static void register_file_v1(const char *category, |
1373 | PSI_file_info_v1 *info, |
1374 | int count) |
1375 | { |
1376 | REGISTER_BODY_V1(PSI_file_key, |
1377 | file_instrument_prefix, |
1378 | register_file_class) |
1379 | } |
1380 | |
1381 | static void register_stage_v1(const char *category, |
1382 | PSI_stage_info_v1 **info_array, |
1383 | int count) |
1384 | { |
1385 | char formatted_name[PFS_MAX_INFO_NAME_LENGTH]; |
1386 | int prefix_length; |
1387 | int len; |
1388 | int full_length; |
1389 | PSI_stage_info_v1 *info; |
1390 | |
1391 | DBUG_ASSERT(category != NULL); |
1392 | DBUG_ASSERT(info_array != NULL); |
1393 | if (unlikely(build_prefix(&stage_instrument_prefix, category, |
1394 | formatted_name, &prefix_length))) |
1395 | { |
1396 | for (; count>0; count--, info_array++) |
1397 | (*info_array)->m_key= 0; |
1398 | return ; |
1399 | } |
1400 | |
1401 | for (; count>0; count--, info_array++) |
1402 | { |
1403 | info= *info_array; |
1404 | DBUG_ASSERT(info != NULL); |
1405 | DBUG_ASSERT(info->m_name != NULL); |
1406 | len= (int)strlen(info->m_name); |
1407 | full_length= prefix_length + len; |
1408 | if (likely(full_length <= PFS_MAX_INFO_NAME_LENGTH)) |
1409 | { |
1410 | memcpy(formatted_name + prefix_length, info->m_name, len); |
1411 | info->m_key= register_stage_class(formatted_name, |
1412 | prefix_length, |
1413 | full_length, |
1414 | info->m_flags); |
1415 | } |
1416 | else |
1417 | { |
1418 | pfs_print_error("register_stage_v1: name too long <%s> <%s>\n" , |
1419 | category, info->m_name); |
1420 | info->m_key= 0; |
1421 | } |
1422 | } |
1423 | return; |
1424 | } |
1425 | |
1426 | static void register_statement_v1(const char *category, |
1427 | PSI_statement_info_v1 *info, |
1428 | int count) |
1429 | { |
1430 | char formatted_name[PFS_MAX_INFO_NAME_LENGTH]; |
1431 | int prefix_length; |
1432 | int len; |
1433 | int full_length; |
1434 | |
1435 | DBUG_ASSERT(category != NULL); |
1436 | DBUG_ASSERT(info != NULL); |
1437 | if (unlikely(build_prefix(&statement_instrument_prefix, |
1438 | category, formatted_name, &prefix_length))) |
1439 | { |
1440 | for (; count>0; count--, info++) |
1441 | info->m_key= 0; |
1442 | return ; |
1443 | } |
1444 | |
1445 | for (; count>0; count--, info++) |
1446 | { |
1447 | if (info->m_name == NULL) |
1448 | continue; |
1449 | |
1450 | len= (int)strlen(info->m_name); |
1451 | full_length= prefix_length + len; |
1452 | if (likely(full_length <= PFS_MAX_INFO_NAME_LENGTH)) |
1453 | { |
1454 | memcpy(formatted_name + prefix_length, info->m_name, len); |
1455 | info->m_key= register_statement_class(formatted_name, full_length, info->m_flags); |
1456 | } |
1457 | else |
1458 | { |
1459 | pfs_print_error("register_statement_v1: name too long <%s>\n" , |
1460 | info->m_name); |
1461 | info->m_key= 0; |
1462 | } |
1463 | } |
1464 | return; |
1465 | } |
1466 | |
1467 | static void register_socket_v1(const char *category, |
1468 | PSI_socket_info_v1 *info, |
1469 | int count) |
1470 | { |
1471 | REGISTER_BODY_V1(PSI_socket_key, |
1472 | socket_instrument_prefix, |
1473 | register_socket_class) |
1474 | } |
1475 | |
1476 | #define INIT_BODY_V1(T, KEY, ID) \ |
1477 | PFS_##T##_class *klass; \ |
1478 | PFS_##T *pfs; \ |
1479 | klass= find_##T##_class(KEY); \ |
1480 | if (unlikely(klass == NULL)) \ |
1481 | return NULL; \ |
1482 | if (! klass->m_enabled) \ |
1483 | return NULL; \ |
1484 | pfs= create_##T(klass, ID); \ |
1485 | return reinterpret_cast<PSI_##T *> (pfs) |
1486 | |
1487 | /** |
1488 | Implementation of the mutex instrumentation interface. |
1489 | @sa PSI_v1::init_mutex. |
1490 | */ |
1491 | static PSI_mutex* |
1492 | init_mutex_v1(PSI_mutex_key key, const void *identity) |
1493 | { |
1494 | INIT_BODY_V1(mutex, key, identity); |
1495 | } |
1496 | |
1497 | /** |
1498 | Implementation of the mutex instrumentation interface. |
1499 | @sa PSI_v1::destroy_mutex. |
1500 | */ |
1501 | static void destroy_mutex_v1(PSI_mutex* mutex) |
1502 | { |
1503 | PFS_mutex *pfs= reinterpret_cast<PFS_mutex*> (mutex); |
1504 | |
1505 | DBUG_ASSERT(pfs != NULL); |
1506 | |
1507 | destroy_mutex(pfs); |
1508 | } |
1509 | |
1510 | /** |
1511 | Implementation of the rwlock instrumentation interface. |
1512 | @sa PSI_v1::init_rwlock. |
1513 | */ |
1514 | static PSI_rwlock* |
1515 | init_rwlock_v1(PSI_rwlock_key key, const void *identity) |
1516 | { |
1517 | INIT_BODY_V1(rwlock, key, identity); |
1518 | } |
1519 | |
1520 | /** |
1521 | Implementation of the rwlock instrumentation interface. |
1522 | @sa PSI_v1::destroy_rwlock. |
1523 | */ |
1524 | static void destroy_rwlock_v1(PSI_rwlock* rwlock) |
1525 | { |
1526 | PFS_rwlock *pfs= reinterpret_cast<PFS_rwlock*> (rwlock); |
1527 | |
1528 | DBUG_ASSERT(pfs != NULL); |
1529 | |
1530 | destroy_rwlock(pfs); |
1531 | } |
1532 | |
1533 | /** |
1534 | Implementation of the cond instrumentation interface. |
1535 | @sa PSI_v1::init_cond. |
1536 | */ |
1537 | static PSI_cond* |
1538 | init_cond_v1(PSI_cond_key key, const void *identity) |
1539 | { |
1540 | INIT_BODY_V1(cond, key, identity); |
1541 | } |
1542 | |
1543 | /** |
1544 | Implementation of the cond instrumentation interface. |
1545 | @sa PSI_v1::destroy_cond. |
1546 | */ |
1547 | static void destroy_cond_v1(PSI_cond* cond) |
1548 | { |
1549 | PFS_cond *pfs= reinterpret_cast<PFS_cond*> (cond); |
1550 | |
1551 | DBUG_ASSERT(pfs != NULL); |
1552 | |
1553 | destroy_cond(pfs); |
1554 | } |
1555 | |
1556 | /** |
1557 | Implementation of the table instrumentation interface. |
1558 | @sa PSI_v1::get_table_share. |
1559 | */ |
1560 | static PSI_table_share* |
1561 | get_table_share_v1(my_bool temporary, TABLE_SHARE *share) |
1562 | { |
1563 | /* Ignore temporary tables and views. */ |
1564 | if (temporary || share->is_view) |
1565 | return NULL; |
1566 | /* An instrumented thread is required, for LF_PINS. */ |
1567 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
1568 | if (unlikely(pfs_thread == NULL)) |
1569 | return NULL; |
1570 | PFS_table_share* pfs_share; |
1571 | pfs_share= find_or_create_table_share(pfs_thread, temporary, share); |
1572 | return reinterpret_cast<PSI_table_share*> (pfs_share); |
1573 | } |
1574 | |
1575 | /** |
1576 | Implementation of the table instrumentation interface. |
1577 | @sa PSI_v1::release_table_share. |
1578 | */ |
1579 | static void release_table_share_v1(PSI_table_share* share) |
1580 | { |
1581 | PFS_table_share* pfs= reinterpret_cast<PFS_table_share*> (share); |
1582 | |
1583 | if (unlikely(pfs == NULL)) |
1584 | return; |
1585 | |
1586 | release_table_share(pfs); |
1587 | } |
1588 | |
1589 | /** |
1590 | Implementation of the table instrumentation interface. |
1591 | @sa PSI_v1::drop_table_share. |
1592 | */ |
1593 | static void |
1594 | drop_table_share_v1(my_bool temporary, |
1595 | const char *schema_name, int schema_name_length, |
1596 | const char *table_name, int table_name_length) |
1597 | { |
1598 | /* Ignore temporary tables. */ |
1599 | if (temporary) |
1600 | return; |
1601 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
1602 | if (unlikely(pfs_thread == NULL)) |
1603 | return; |
1604 | /* TODO: temporary tables */ |
1605 | drop_table_share(pfs_thread, temporary, schema_name, schema_name_length, |
1606 | table_name, table_name_length); |
1607 | } |
1608 | |
1609 | /** |
1610 | Implementation of the table instrumentation interface. |
1611 | @sa PSI_v1::open_table. |
1612 | */ |
1613 | static PSI_table* |
1614 | open_table_v1(PSI_table_share *share, const void *identity) |
1615 | { |
1616 | PFS_table_share *pfs_table_share= reinterpret_cast<PFS_table_share*> (share); |
1617 | |
1618 | /* |
1619 | When the performance schema is off, do not instrument anything. |
1620 | Table handles have short life cycle, instrumentation will happen |
1621 | again if needed during the next open(). |
1622 | */ |
1623 | if (psi_unlikely(! flag_global_instrumentation)) |
1624 | return NULL; |
1625 | |
1626 | if (unlikely(pfs_table_share == NULL)) |
1627 | return NULL; |
1628 | |
1629 | /* This object is not to be instrumented. */ |
1630 | if (! pfs_table_share->m_enabled) |
1631 | return NULL; |
1632 | |
1633 | /* This object is instrumented, but all table instruments are disabled. */ |
1634 | if (! global_table_io_class.m_enabled && ! global_table_lock_class.m_enabled) |
1635 | return NULL; |
1636 | |
1637 | PFS_thread *thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
1638 | if (unlikely(thread == NULL)) |
1639 | return NULL; |
1640 | |
1641 | PFS_table *pfs_table= create_table(pfs_table_share, thread, identity); |
1642 | return reinterpret_cast<PSI_table *> (pfs_table); |
1643 | } |
1644 | |
1645 | /** |
1646 | Implementation of the table instrumentation interface. |
1647 | @sa PSI_v1::unbind_table. |
1648 | */ |
1649 | static void unbind_table_v1(PSI_table *table) |
1650 | { |
1651 | PFS_table *pfs= reinterpret_cast<PFS_table*> (table); |
1652 | if (likely(pfs != NULL)) |
1653 | { |
1654 | pfs->m_thread_owner= NULL; |
1655 | } |
1656 | } |
1657 | |
1658 | /** |
1659 | Implementation of the table instrumentation interface. |
1660 | @sa PSI_v1::rebind_table. |
1661 | */ |
1662 | static PSI_table * |
1663 | rebind_table_v1(PSI_table_share *share, const void *identity, PSI_table *table) |
1664 | { |
1665 | PFS_table *pfs= reinterpret_cast<PFS_table*> (table); |
1666 | if (likely(pfs != NULL)) |
1667 | { |
1668 | PFS_thread *thread; |
1669 | DBUG_ASSERT(pfs->m_thread_owner == NULL); |
1670 | |
1671 | if (psi_unlikely(! flag_global_instrumentation)) |
1672 | { |
1673 | destroy_table(pfs); |
1674 | return NULL; |
1675 | } |
1676 | |
1677 | /* The table handle was already instrumented, reuse it for this thread. */ |
1678 | thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
1679 | |
1680 | if (unlikely(! pfs->m_share->m_enabled)) |
1681 | { |
1682 | destroy_table(pfs); |
1683 | return NULL; |
1684 | } |
1685 | |
1686 | if (unlikely(! global_table_io_class.m_enabled && ! global_table_lock_class.m_enabled)) |
1687 | { |
1688 | destroy_table(pfs); |
1689 | return NULL; |
1690 | } |
1691 | |
1692 | pfs->m_thread_owner= thread; |
1693 | return table; |
1694 | } |
1695 | |
1696 | if (psi_unlikely(! flag_global_instrumentation)) |
1697 | return NULL; |
1698 | |
1699 | /* See open_table_v1() */ |
1700 | |
1701 | PFS_table_share *pfs_table_share= reinterpret_cast<PFS_table_share*> (share); |
1702 | |
1703 | if (unlikely(pfs_table_share == NULL)) |
1704 | return NULL; |
1705 | |
1706 | if (! pfs_table_share->m_enabled) |
1707 | return NULL; |
1708 | |
1709 | if (! global_table_io_class.m_enabled && ! global_table_lock_class.m_enabled) |
1710 | return NULL; |
1711 | |
1712 | PFS_thread *thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
1713 | if (unlikely(thread == NULL)) |
1714 | return NULL; |
1715 | |
1716 | PFS_table *pfs_table= create_table(pfs_table_share, thread, identity); |
1717 | return reinterpret_cast<PSI_table *> (pfs_table); |
1718 | } |
1719 | |
1720 | /** |
1721 | Implementation of the table instrumentation interface. |
1722 | @sa PSI_v1::close_table. |
1723 | */ |
1724 | static void close_table_v1(PSI_table *table) |
1725 | { |
1726 | PFS_table *pfs= reinterpret_cast<PFS_table*> (table); |
1727 | if (unlikely(pfs == NULL)) |
1728 | return; |
1729 | pfs->aggregate(); |
1730 | destroy_table(pfs); |
1731 | } |
1732 | |
1733 | static PSI_socket* |
1734 | init_socket_v1(PSI_socket_key key, const my_socket *fd, |
1735 | const struct sockaddr *addr, socklen_t addr_len) |
1736 | { |
1737 | PFS_socket_class *klass; |
1738 | PFS_socket *pfs; |
1739 | klass= find_socket_class(key); |
1740 | if (unlikely(klass == NULL)) |
1741 | return NULL; |
1742 | if (! klass->m_enabled) |
1743 | return NULL; |
1744 | pfs= create_socket(klass, fd, addr, addr_len); |
1745 | return reinterpret_cast<PSI_socket *> (pfs); |
1746 | } |
1747 | |
1748 | static void destroy_socket_v1(PSI_socket *socket) |
1749 | { |
1750 | PFS_socket *pfs= reinterpret_cast<PFS_socket*> (socket); |
1751 | |
1752 | DBUG_ASSERT(pfs != NULL); |
1753 | |
1754 | destroy_socket(pfs); |
1755 | } |
1756 | |
1757 | /** |
1758 | Implementation of the file instrumentation interface. |
1759 | @sa PSI_v1::create_file. |
1760 | */ |
1761 | static void create_file_v1(PSI_file_key key, const char *name, File file) |
1762 | { |
1763 | if (psi_unlikely(! flag_global_instrumentation)) |
1764 | return; |
1765 | int index= (int) file; |
1766 | if (unlikely(index < 0)) |
1767 | return; |
1768 | PFS_file_class *klass= find_file_class(key); |
1769 | if (unlikely(klass == NULL)) |
1770 | return; |
1771 | if (! klass->m_enabled) |
1772 | return; |
1773 | |
1774 | /* A thread is needed for LF_PINS */ |
1775 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
1776 | if (unlikely(pfs_thread == NULL)) |
1777 | return; |
1778 | |
1779 | if (flag_thread_instrumentation && ! pfs_thread->m_enabled) |
1780 | return; |
1781 | |
1782 | /* |
1783 | We want this check after pfs_thread->m_enabled, |
1784 | to avoid reporting false loss. |
1785 | */ |
1786 | if (unlikely(index >= file_handle_max)) |
1787 | { |
1788 | file_handle_lost++; |
1789 | return; |
1790 | } |
1791 | |
1792 | uint len= (uint)strlen(name); |
1793 | PFS_file *pfs_file= find_or_create_file(pfs_thread, klass, name, len, true); |
1794 | |
1795 | file_handle_array[index]= pfs_file; |
1796 | } |
1797 | |
1798 | /** |
1799 | Arguments given from a parent to a child thread, packaged in one structure. |
1800 | This data is used when spawning a new instrumented thread. |
1801 | @sa pfs_spawn_thread. |
1802 | */ |
1803 | struct PFS_spawn_thread_arg |
1804 | { |
1805 | ulonglong m_thread_internal_id; |
1806 | char m_username[USERNAME_LENGTH]; |
1807 | uint m_username_length; |
1808 | char m_hostname[HOSTNAME_LENGTH]; |
1809 | uint m_hostname_length; |
1810 | |
1811 | PSI_thread_key m_child_key; |
1812 | const void *m_child_identity; |
1813 | void *(*m_user_start_routine)(void*); |
1814 | void *m_user_arg; |
1815 | }; |
1816 | |
1817 | void* pfs_spawn_thread(void *arg) |
1818 | { |
1819 | PFS_spawn_thread_arg *typed_arg= (PFS_spawn_thread_arg*) arg; |
1820 | void *user_arg; |
1821 | void *(*user_start_routine)(void*); |
1822 | |
1823 | PFS_thread *pfs; |
1824 | |
1825 | /* First, attach instrumentation to this newly created pthread. */ |
1826 | PFS_thread_class *klass= find_thread_class(typed_arg->m_child_key); |
1827 | if (likely(klass != NULL)) |
1828 | { |
1829 | pfs= create_thread(klass, typed_arg->m_child_identity, 0); |
1830 | if (likely(pfs != NULL)) |
1831 | { |
1832 | clear_thread_account(pfs); |
1833 | |
1834 | pfs->m_parent_thread_internal_id= typed_arg->m_thread_internal_id; |
1835 | |
1836 | memcpy(pfs->m_username, typed_arg->m_username, sizeof(pfs->m_username)); |
1837 | pfs->m_username_length= typed_arg->m_username_length; |
1838 | |
1839 | memcpy(pfs->m_hostname, typed_arg->m_hostname, sizeof(pfs->m_hostname)); |
1840 | pfs->m_hostname_length= typed_arg->m_hostname_length; |
1841 | |
1842 | set_thread_account(pfs); |
1843 | } |
1844 | } |
1845 | else |
1846 | { |
1847 | pfs= NULL; |
1848 | } |
1849 | my_pthread_setspecific_ptr(THR_PFS, pfs); |
1850 | |
1851 | /* |
1852 | Secondly, free the memory allocated in spawn_thread_v1(). |
1853 | It is preferable to do this before invoking the user |
1854 | routine, to avoid memory leaks at shutdown, in case |
1855 | the server exits without waiting for this thread. |
1856 | */ |
1857 | user_start_routine= typed_arg->m_user_start_routine; |
1858 | user_arg= typed_arg->m_user_arg; |
1859 | my_free(typed_arg); |
1860 | |
1861 | /* Then, execute the user code for this thread. */ |
1862 | (*user_start_routine)(user_arg); |
1863 | |
1864 | return NULL; |
1865 | } |
1866 | |
1867 | /** |
1868 | Implementation of the thread instrumentation interface. |
1869 | @sa PSI_v1::spawn_thread. |
1870 | */ |
1871 | static int spawn_thread_v1(PSI_thread_key key, |
1872 | pthread_t *thread, const pthread_attr_t *attr, |
1873 | void *(*start_routine)(void*), void *arg) |
1874 | { |
1875 | PFS_spawn_thread_arg *psi_arg; |
1876 | PFS_thread *parent; |
1877 | |
1878 | /* psi_arg can not be global, and can not be a local variable. */ |
1879 | psi_arg= (PFS_spawn_thread_arg*) my_malloc(sizeof(PFS_spawn_thread_arg), |
1880 | MYF(MY_WME)); |
1881 | if (unlikely(psi_arg == NULL)) |
1882 | return EAGAIN; |
1883 | |
1884 | psi_arg->m_child_key= key; |
1885 | psi_arg->m_child_identity= (arg ? arg : thread); |
1886 | psi_arg->m_user_start_routine= start_routine; |
1887 | psi_arg->m_user_arg= arg; |
1888 | |
1889 | parent= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
1890 | if (parent != NULL) |
1891 | { |
1892 | /* |
1893 | Make a copy of the parent attributes. |
1894 | This is required, because instrumentation for this thread (the parent) |
1895 | may be destroyed before the child thread instrumentation is created. |
1896 | */ |
1897 | psi_arg->m_thread_internal_id= parent->m_thread_internal_id; |
1898 | |
1899 | memcpy(psi_arg->m_username, parent->m_username, sizeof(psi_arg->m_username)); |
1900 | psi_arg->m_username_length= parent->m_username_length; |
1901 | |
1902 | memcpy(psi_arg->m_hostname, parent->m_hostname, sizeof(psi_arg->m_hostname)); |
1903 | psi_arg->m_hostname_length= parent->m_hostname_length; |
1904 | } |
1905 | else |
1906 | { |
1907 | psi_arg->m_thread_internal_id= 0; |
1908 | psi_arg->m_username_length= 0; |
1909 | psi_arg->m_hostname_length= 0; |
1910 | } |
1911 | |
1912 | int result= pthread_create(thread, attr, pfs_spawn_thread, psi_arg); |
1913 | if (unlikely(result != 0)) |
1914 | my_free(psi_arg); |
1915 | return result; |
1916 | } |
1917 | |
1918 | /** |
1919 | Implementation of the thread instrumentation interface. |
1920 | @sa PSI_v1::new_thread. |
1921 | */ |
1922 | static PSI_thread* |
1923 | new_thread_v1(PSI_thread_key key, const void *identity, ulonglong processlist_id) |
1924 | { |
1925 | PFS_thread *pfs; |
1926 | |
1927 | PFS_thread_class *klass= find_thread_class(key); |
1928 | if (likely(klass != NULL)) |
1929 | pfs= create_thread(klass, identity, processlist_id); |
1930 | else |
1931 | pfs= NULL; |
1932 | |
1933 | return reinterpret_cast<PSI_thread*> (pfs); |
1934 | } |
1935 | |
1936 | /** |
1937 | Implementation of the thread instrumentation interface. |
1938 | @sa PSI_v1::set_thread_id. |
1939 | */ |
1940 | static void set_thread_id_v1(PSI_thread *thread, ulonglong processlist_id) |
1941 | { |
1942 | PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread); |
1943 | if (unlikely(pfs == NULL)) |
1944 | return; |
1945 | pfs->m_processlist_id= (ulong)processlist_id; |
1946 | } |
1947 | |
1948 | /** |
1949 | Implementation of the thread instrumentation interface. |
1950 | @sa PSI_v1::get_thread_id. |
1951 | */ |
1952 | static PSI_thread* |
1953 | get_thread_v1(void) |
1954 | { |
1955 | PFS_thread *pfs= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
1956 | return reinterpret_cast<PSI_thread*> (pfs); |
1957 | } |
1958 | |
1959 | /** |
1960 | Implementation of the thread instrumentation interface. |
1961 | @sa PSI_v1::set_thread_user. |
1962 | */ |
1963 | static void set_thread_user_v1(const char *user, int user_len) |
1964 | { |
1965 | PFS_thread *pfs= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
1966 | |
1967 | DBUG_ASSERT((user != NULL) || (user_len == 0)); |
1968 | DBUG_ASSERT(user_len >= 0); |
1969 | DBUG_ASSERT((uint) user_len <= sizeof(pfs->m_username)); |
1970 | |
1971 | if (unlikely(pfs == NULL)) |
1972 | return; |
1973 | |
1974 | aggregate_thread(pfs, pfs->m_account, pfs->m_user, pfs->m_host); |
1975 | |
1976 | pfs->m_session_lock.allocated_to_dirty(); |
1977 | |
1978 | clear_thread_account(pfs); |
1979 | |
1980 | if (user_len > 0) |
1981 | memcpy(pfs->m_username, user, user_len); |
1982 | pfs->m_username_length= user_len; |
1983 | |
1984 | set_thread_account(pfs); |
1985 | |
1986 | bool enabled= true; |
1987 | if (flag_thread_instrumentation) |
1988 | { |
1989 | if ((pfs->m_username_length > 0) && (pfs->m_hostname_length > 0)) |
1990 | { |
1991 | /* |
1992 | TODO: performance improvement. |
1993 | Once performance_schema.USERS is exposed, |
1994 | we can use PFS_user::m_enabled instead of looking up |
1995 | SETUP_ACTORS every time. |
1996 | */ |
1997 | lookup_setup_actor(pfs, |
1998 | pfs->m_username, pfs->m_username_length, |
1999 | pfs->m_hostname, pfs->m_hostname_length, |
2000 | &enabled); |
2001 | } |
2002 | } |
2003 | |
2004 | pfs->m_enabled= enabled; |
2005 | |
2006 | pfs->m_session_lock.dirty_to_allocated(); |
2007 | } |
2008 | |
2009 | /** |
2010 | Implementation of the thread instrumentation interface. |
2011 | @sa PSI_v1::set_thread_account. |
2012 | */ |
2013 | static void set_thread_account_v1(const char *user, int user_len, |
2014 | const char *host, int host_len) |
2015 | { |
2016 | PFS_thread *pfs= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
2017 | |
2018 | DBUG_ASSERT((user != NULL) || (user_len == 0)); |
2019 | DBUG_ASSERT(user_len >= 0); |
2020 | DBUG_ASSERT((uint) user_len <= sizeof(pfs->m_username)); |
2021 | DBUG_ASSERT((host != NULL) || (host_len == 0)); |
2022 | DBUG_ASSERT(host_len >= 0); |
2023 | |
2024 | host_len= MY_MIN(host_len, static_cast<int>(sizeof(pfs->m_hostname))); |
2025 | |
2026 | if (unlikely(pfs == NULL)) |
2027 | return; |
2028 | |
2029 | pfs->m_session_lock.allocated_to_dirty(); |
2030 | |
2031 | clear_thread_account(pfs); |
2032 | |
2033 | if (host_len > 0) |
2034 | memcpy(pfs->m_hostname, host, host_len); |
2035 | pfs->m_hostname_length= host_len; |
2036 | |
2037 | if (user_len > 0) |
2038 | memcpy(pfs->m_username, user, user_len); |
2039 | pfs->m_username_length= user_len; |
2040 | |
2041 | set_thread_account(pfs); |
2042 | |
2043 | bool enabled= true; |
2044 | if (flag_thread_instrumentation) |
2045 | { |
2046 | if ((pfs->m_username_length > 0) && (pfs->m_hostname_length > 0)) |
2047 | { |
2048 | /* |
2049 | TODO: performance improvement. |
2050 | Once performance_schema.USERS is exposed, |
2051 | we can use PFS_user::m_enabled instead of looking up |
2052 | SETUP_ACTORS every time. |
2053 | */ |
2054 | lookup_setup_actor(pfs, |
2055 | pfs->m_username, pfs->m_username_length, |
2056 | pfs->m_hostname, pfs->m_hostname_length, |
2057 | &enabled); |
2058 | } |
2059 | } |
2060 | pfs->m_enabled= enabled; |
2061 | |
2062 | pfs->m_session_lock.dirty_to_allocated(); |
2063 | } |
2064 | |
2065 | /** |
2066 | Implementation of the thread instrumentation interface. |
2067 | @sa PSI_v1::set_thread_db. |
2068 | */ |
2069 | static void set_thread_db_v1(const char* db, int db_len) |
2070 | { |
2071 | PFS_thread *pfs= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
2072 | |
2073 | DBUG_ASSERT((db != NULL) || (db_len == 0)); |
2074 | DBUG_ASSERT(db_len >= 0); |
2075 | DBUG_ASSERT((uint) db_len <= sizeof(pfs->m_dbname)); |
2076 | |
2077 | if (likely(pfs != NULL)) |
2078 | { |
2079 | pfs->m_stmt_lock.allocated_to_dirty(); |
2080 | if (db_len > 0) |
2081 | memcpy(pfs->m_dbname, db, db_len); |
2082 | pfs->m_dbname_length= db_len; |
2083 | pfs->m_stmt_lock.dirty_to_allocated(); |
2084 | } |
2085 | } |
2086 | |
2087 | /** |
2088 | Implementation of the thread instrumentation interface. |
2089 | @sa PSI_v1::set_thread_command. |
2090 | */ |
2091 | static void set_thread_command_v1(int command) |
2092 | { |
2093 | PFS_thread *pfs= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
2094 | |
2095 | DBUG_ASSERT(command >= 0); |
2096 | DBUG_ASSERT(command <= (int) COM_END); |
2097 | |
2098 | if (likely(pfs != NULL)) |
2099 | { |
2100 | pfs->m_command= command; |
2101 | } |
2102 | } |
2103 | |
2104 | /** |
2105 | Implementation of the thread instrumentation interface. |
2106 | @sa PSI_v1::set_thread_start_time. |
2107 | */ |
2108 | static void set_thread_start_time_v1(time_t start_time) |
2109 | { |
2110 | PFS_thread *pfs= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
2111 | |
2112 | if (likely(pfs != NULL)) |
2113 | { |
2114 | pfs->m_start_time= start_time; |
2115 | } |
2116 | } |
2117 | |
2118 | /** |
2119 | Implementation of the thread instrumentation interface. |
2120 | @sa PSI_v1::set_thread_state. |
2121 | */ |
2122 | static void set_thread_state_v1(const char* state) |
2123 | { |
2124 | /* DEPRECATED. */ |
2125 | } |
2126 | |
2127 | /** |
2128 | Implementation of the thread instrumentation interface. |
2129 | @sa PSI_v1::set_thread_info. |
2130 | */ |
2131 | static void set_thread_info_v1(const char* info, uint info_len) |
2132 | { |
2133 | PFS_thread *pfs= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
2134 | |
2135 | DBUG_ASSERT((info != NULL) || (info_len == 0)); |
2136 | |
2137 | if (likely(pfs != NULL)) |
2138 | { |
2139 | if ((info != NULL) && (info_len > 0)) |
2140 | { |
2141 | if (info_len > sizeof(pfs->m_processlist_info)) |
2142 | info_len= sizeof(pfs->m_processlist_info); |
2143 | |
2144 | pfs->m_stmt_lock.allocated_to_dirty(); |
2145 | memcpy(pfs->m_processlist_info, info, info_len); |
2146 | pfs->m_processlist_info_length= info_len; |
2147 | pfs->m_stmt_lock.dirty_to_allocated(); |
2148 | } |
2149 | else |
2150 | { |
2151 | pfs->m_stmt_lock.allocated_to_dirty(); |
2152 | pfs->m_processlist_info_length= 0; |
2153 | pfs->m_stmt_lock.dirty_to_allocated(); |
2154 | } |
2155 | } |
2156 | } |
2157 | |
2158 | /** |
2159 | Implementation of the thread instrumentation interface. |
2160 | @sa PSI_v1::set_thread. |
2161 | */ |
2162 | static void set_thread_v1(PSI_thread* thread) |
2163 | { |
2164 | PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread); |
2165 | my_pthread_setspecific_ptr(THR_PFS, pfs); |
2166 | } |
2167 | |
2168 | /** |
2169 | Implementation of the thread instrumentation interface. |
2170 | @sa PSI_v1::delete_current_thread. |
2171 | */ |
2172 | static void delete_current_thread_v1(void) |
2173 | { |
2174 | PFS_thread *thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
2175 | if (thread != NULL) |
2176 | { |
2177 | aggregate_thread(thread, thread->m_account, thread->m_user, thread->m_host); |
2178 | my_pthread_setspecific_ptr(THR_PFS, NULL); |
2179 | destroy_thread(thread); |
2180 | } |
2181 | } |
2182 | |
2183 | /** |
2184 | Implementation of the thread instrumentation interface. |
2185 | @sa PSI_v1::delete_thread. |
2186 | */ |
2187 | static void delete_thread_v1(PSI_thread *thread) |
2188 | { |
2189 | PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread); |
2190 | |
2191 | if (pfs != NULL) |
2192 | { |
2193 | aggregate_thread(pfs, pfs->m_account, pfs->m_user, pfs->m_host); |
2194 | destroy_thread(pfs); |
2195 | } |
2196 | } |
2197 | |
2198 | /** |
2199 | Implementation of the mutex instrumentation interface. |
2200 | @sa PSI_v1::start_mutex_wait. |
2201 | */ |
2202 | static PSI_mutex_locker* |
2203 | start_mutex_wait_v1(PSI_mutex_locker_state *state, |
2204 | PSI_mutex *mutex, PSI_mutex_operation op, |
2205 | const char *src_file, uint src_line) |
2206 | { |
2207 | PFS_mutex *pfs_mutex= reinterpret_cast<PFS_mutex*> (mutex); |
2208 | DBUG_ASSERT((int) op >= 0); |
2209 | DBUG_ASSERT((uint) op < array_elements(mutex_operation_map)); |
2210 | DBUG_ASSERT(state != NULL); |
2211 | |
2212 | DBUG_ASSERT(pfs_mutex != NULL); |
2213 | DBUG_ASSERT(pfs_mutex->m_class != NULL); |
2214 | |
2215 | if (! pfs_mutex->m_enabled) |
2216 | return NULL; |
2217 | |
2218 | uint flags; |
2219 | ulonglong timer_start= 0; |
2220 | |
2221 | if (flag_thread_instrumentation) |
2222 | { |
2223 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
2224 | if (unlikely(pfs_thread == NULL)) |
2225 | return NULL; |
2226 | if (! pfs_thread->m_enabled) |
2227 | return NULL; |
2228 | state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); |
2229 | flags= STATE_FLAG_THREAD; |
2230 | |
2231 | if (pfs_mutex->m_timed) |
2232 | { |
2233 | timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); |
2234 | state->m_timer_start= timer_start; |
2235 | flags|= STATE_FLAG_TIMED; |
2236 | } |
2237 | |
2238 | if (flag_events_waits_current) |
2239 | { |
2240 | if (unlikely(pfs_thread->m_events_waits_current >= |
2241 | & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) |
2242 | { |
2243 | locker_lost++; |
2244 | return NULL; |
2245 | } |
2246 | PFS_events_waits *wait= pfs_thread->m_events_waits_current; |
2247 | state->m_wait= wait; |
2248 | flags|= STATE_FLAG_EVENT; |
2249 | |
2250 | PFS_events_waits *parent_event= wait - 1; |
2251 | wait->m_event_type= EVENT_TYPE_WAIT; |
2252 | wait->m_nesting_event_id= parent_event->m_event_id; |
2253 | wait->m_nesting_event_type= parent_event->m_event_type; |
2254 | |
2255 | wait->m_thread= pfs_thread; |
2256 | wait->m_class= pfs_mutex->m_class; |
2257 | wait->m_timer_start= timer_start; |
2258 | wait->m_timer_end= 0; |
2259 | wait->m_object_instance_addr= pfs_mutex->m_identity; |
2260 | wait->m_event_id= pfs_thread->m_event_id++; |
2261 | wait->m_end_event_id= 0; |
2262 | wait->m_operation= mutex_operation_map[(int) op]; |
2263 | wait->m_source_file= src_file; |
2264 | wait->m_source_line= src_line; |
2265 | wait->m_wait_class= WAIT_CLASS_MUTEX; |
2266 | |
2267 | pfs_thread->m_events_waits_current++; |
2268 | } |
2269 | } |
2270 | else |
2271 | { |
2272 | if (pfs_mutex->m_timed) |
2273 | { |
2274 | timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); |
2275 | state->m_timer_start= timer_start; |
2276 | flags= STATE_FLAG_TIMED; |
2277 | state->m_thread= NULL; |
2278 | } |
2279 | else |
2280 | { |
2281 | /* |
2282 | Complete shortcut. |
2283 | */ |
2284 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ |
2285 | pfs_mutex->m_mutex_stat.m_wait_stat.aggregate_counted(); |
2286 | return NULL; |
2287 | } |
2288 | } |
2289 | |
2290 | state->m_flags= flags; |
2291 | state->m_mutex= mutex; |
2292 | return reinterpret_cast<PSI_mutex_locker*> (state); |
2293 | } |
2294 | |
2295 | /** |
2296 | Implementation of the rwlock instrumentation interface. |
2297 | @sa PSI_v1::start_rwlock_rdwait |
2298 | @sa PSI_v1::start_rwlock_wrwait |
2299 | */ |
2300 | static PSI_rwlock_locker* |
2301 | start_rwlock_wait_v1(PSI_rwlock_locker_state *state, |
2302 | PSI_rwlock *rwlock, |
2303 | PSI_rwlock_operation op, |
2304 | const char *src_file, uint src_line) |
2305 | { |
2306 | PFS_rwlock *pfs_rwlock= reinterpret_cast<PFS_rwlock*> (rwlock); |
2307 | DBUG_ASSERT(static_cast<int> (op) >= 0); |
2308 | DBUG_ASSERT(static_cast<uint> (op) < array_elements(rwlock_operation_map)); |
2309 | DBUG_ASSERT(state != NULL); |
2310 | DBUG_ASSERT(pfs_rwlock != NULL); |
2311 | DBUG_ASSERT(pfs_rwlock->m_class != NULL); |
2312 | |
2313 | if (! pfs_rwlock->m_enabled) |
2314 | return NULL; |
2315 | |
2316 | uint flags; |
2317 | ulonglong timer_start= 0; |
2318 | |
2319 | if (flag_thread_instrumentation) |
2320 | { |
2321 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
2322 | if (unlikely(pfs_thread == NULL)) |
2323 | return NULL; |
2324 | if (! pfs_thread->m_enabled) |
2325 | return NULL; |
2326 | state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); |
2327 | flags= STATE_FLAG_THREAD; |
2328 | |
2329 | if (pfs_rwlock->m_timed) |
2330 | { |
2331 | timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); |
2332 | state->m_timer_start= timer_start; |
2333 | flags|= STATE_FLAG_TIMED; |
2334 | } |
2335 | |
2336 | if (flag_events_waits_current) |
2337 | { |
2338 | if (unlikely(pfs_thread->m_events_waits_current >= |
2339 | & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) |
2340 | { |
2341 | locker_lost++; |
2342 | return NULL; |
2343 | } |
2344 | PFS_events_waits *wait= pfs_thread->m_events_waits_current; |
2345 | state->m_wait= wait; |
2346 | flags|= STATE_FLAG_EVENT; |
2347 | |
2348 | PFS_events_waits *parent_event= wait - 1; |
2349 | wait->m_event_type= EVENT_TYPE_WAIT; |
2350 | wait->m_nesting_event_id= parent_event->m_event_id; |
2351 | wait->m_nesting_event_type= parent_event->m_event_type; |
2352 | |
2353 | wait->m_thread= pfs_thread; |
2354 | wait->m_class= pfs_rwlock->m_class; |
2355 | wait->m_timer_start= timer_start; |
2356 | wait->m_timer_end= 0; |
2357 | wait->m_object_instance_addr= pfs_rwlock->m_identity; |
2358 | wait->m_event_id= pfs_thread->m_event_id++; |
2359 | wait->m_end_event_id= 0; |
2360 | wait->m_operation= rwlock_operation_map[static_cast<int> (op)]; |
2361 | wait->m_source_file= src_file; |
2362 | wait->m_source_line= src_line; |
2363 | wait->m_wait_class= WAIT_CLASS_RWLOCK; |
2364 | |
2365 | pfs_thread->m_events_waits_current++; |
2366 | } |
2367 | } |
2368 | else |
2369 | { |
2370 | if (pfs_rwlock->m_timed) |
2371 | { |
2372 | timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); |
2373 | state->m_timer_start= timer_start; |
2374 | flags= STATE_FLAG_TIMED; |
2375 | state->m_thread= NULL; |
2376 | } |
2377 | else |
2378 | { |
2379 | /* |
2380 | Complete shortcut. |
2381 | */ |
2382 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ |
2383 | pfs_rwlock->m_rwlock_stat.m_wait_stat.aggregate_counted(); |
2384 | return NULL; |
2385 | } |
2386 | } |
2387 | |
2388 | state->m_flags= flags; |
2389 | state->m_rwlock= rwlock; |
2390 | return reinterpret_cast<PSI_rwlock_locker*> (state); |
2391 | } |
2392 | |
2393 | /** |
2394 | Implementation of the cond instrumentation interface. |
2395 | @sa PSI_v1::start_cond_wait. |
2396 | */ |
2397 | static PSI_cond_locker* |
2398 | start_cond_wait_v1(PSI_cond_locker_state *state, |
2399 | PSI_cond *cond, PSI_mutex *mutex, |
2400 | PSI_cond_operation op, |
2401 | const char *src_file, uint src_line) |
2402 | { |
2403 | /* |
2404 | Note about the unused PSI_mutex *mutex parameter: |
2405 | In the pthread library, a call to pthread_cond_wait() |
2406 | causes an unlock() + lock() on the mutex associated with the condition. |
2407 | This mutex operation is not instrumented, so the mutex will still |
2408 | appear as locked when a thread is waiting on a condition. |
2409 | This has no impact now, as unlock_mutex() is not recording events. |
2410 | When unlock_mutex() is implemented by later work logs, |
2411 | this parameter here will be used to adjust the mutex state, |
2412 | in start_cond_wait_v1() and end_cond_wait_v1(). |
2413 | */ |
2414 | PFS_cond *pfs_cond= reinterpret_cast<PFS_cond*> (cond); |
2415 | DBUG_ASSERT(static_cast<int> (op) >= 0); |
2416 | DBUG_ASSERT(static_cast<uint> (op) < array_elements(cond_operation_map)); |
2417 | DBUG_ASSERT(state != NULL); |
2418 | DBUG_ASSERT(pfs_cond != NULL); |
2419 | DBUG_ASSERT(pfs_cond->m_class != NULL); |
2420 | |
2421 | if (! pfs_cond->m_enabled) |
2422 | return NULL; |
2423 | |
2424 | uint flags; |
2425 | ulonglong timer_start= 0; |
2426 | |
2427 | if (flag_thread_instrumentation) |
2428 | { |
2429 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
2430 | if (unlikely(pfs_thread == NULL)) |
2431 | return NULL; |
2432 | if (! pfs_thread->m_enabled) |
2433 | return NULL; |
2434 | state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); |
2435 | flags= STATE_FLAG_THREAD; |
2436 | |
2437 | if (pfs_cond->m_timed) |
2438 | { |
2439 | timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); |
2440 | state->m_timer_start= timer_start; |
2441 | flags|= STATE_FLAG_TIMED; |
2442 | } |
2443 | |
2444 | if (flag_events_waits_current) |
2445 | { |
2446 | if (unlikely(pfs_thread->m_events_waits_current >= |
2447 | & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) |
2448 | { |
2449 | locker_lost++; |
2450 | return NULL; |
2451 | } |
2452 | PFS_events_waits *wait= pfs_thread->m_events_waits_current; |
2453 | state->m_wait= wait; |
2454 | flags|= STATE_FLAG_EVENT; |
2455 | |
2456 | PFS_events_waits *parent_event= wait - 1; |
2457 | wait->m_event_type= EVENT_TYPE_WAIT; |
2458 | wait->m_nesting_event_id= parent_event->m_event_id; |
2459 | wait->m_nesting_event_type= parent_event->m_event_type; |
2460 | |
2461 | wait->m_thread= pfs_thread; |
2462 | wait->m_class= pfs_cond->m_class; |
2463 | wait->m_timer_start= timer_start; |
2464 | wait->m_timer_end= 0; |
2465 | wait->m_object_instance_addr= pfs_cond->m_identity; |
2466 | wait->m_event_id= pfs_thread->m_event_id++; |
2467 | wait->m_end_event_id= 0; |
2468 | wait->m_operation= cond_operation_map[static_cast<int> (op)]; |
2469 | wait->m_source_file= src_file; |
2470 | wait->m_source_line= src_line; |
2471 | wait->m_wait_class= WAIT_CLASS_COND; |
2472 | |
2473 | pfs_thread->m_events_waits_current++; |
2474 | } |
2475 | } |
2476 | else |
2477 | { |
2478 | if (pfs_cond->m_timed) |
2479 | { |
2480 | timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); |
2481 | state->m_timer_start= timer_start; |
2482 | flags= STATE_FLAG_TIMED; |
2483 | } |
2484 | else |
2485 | { |
2486 | /* |
2487 | Complete shortcut. |
2488 | */ |
2489 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ |
2490 | pfs_cond->m_cond_stat.m_wait_stat.aggregate_counted(); |
2491 | return NULL; |
2492 | } |
2493 | } |
2494 | |
2495 | state->m_flags= flags; |
2496 | state->m_cond= cond; |
2497 | state->m_mutex= mutex; |
2498 | return reinterpret_cast<PSI_cond_locker*> (state); |
2499 | } |
2500 | |
2501 | static inline PFS_TL_LOCK_TYPE lock_flags_to_lock_type(uint flags) |
2502 | { |
2503 | enum thr_lock_type value= static_cast<enum thr_lock_type> (flags); |
2504 | |
2505 | switch (value) |
2506 | { |
2507 | case TL_READ: |
2508 | return PFS_TL_READ; |
2509 | case TL_READ_WITH_SHARED_LOCKS: |
2510 | return PFS_TL_READ_WITH_SHARED_LOCKS; |
2511 | case TL_READ_HIGH_PRIORITY: |
2512 | return PFS_TL_READ_HIGH_PRIORITY; |
2513 | case TL_READ_NO_INSERT: |
2514 | return PFS_TL_READ_NO_INSERT; |
2515 | case TL_WRITE_ALLOW_WRITE: |
2516 | return PFS_TL_WRITE_ALLOW_WRITE; |
2517 | case TL_WRITE_CONCURRENT_INSERT: |
2518 | return PFS_TL_WRITE_CONCURRENT_INSERT; |
2519 | case TL_WRITE_DELAYED: |
2520 | return PFS_TL_WRITE_DELAYED; |
2521 | case TL_WRITE_LOW_PRIORITY: |
2522 | return PFS_TL_WRITE_LOW_PRIORITY; |
2523 | case TL_WRITE: |
2524 | return PFS_TL_WRITE; |
2525 | |
2526 | case TL_WRITE_ONLY: |
2527 | case TL_IGNORE: |
2528 | case TL_UNLOCK: |
2529 | case TL_READ_DEFAULT: |
2530 | case TL_WRITE_DEFAULT: |
2531 | default: |
2532 | DBUG_ASSERT(false); |
2533 | } |
2534 | |
2535 | /* Dead code */ |
2536 | return PFS_TL_READ; |
2537 | } |
2538 | |
2539 | static inline PFS_TL_LOCK_TYPE external_lock_flags_to_lock_type(uint flags) |
2540 | { |
2541 | DBUG_ASSERT(flags == F_RDLCK || flags == F_WRLCK); |
2542 | return (flags == F_RDLCK ? PFS_TL_READ_EXTERNAL : PFS_TL_WRITE_EXTERNAL); |
2543 | } |
2544 | |
2545 | /** |
2546 | Implementation of the table instrumentation interface. |
2547 | @sa PSI_v1::start_table_io_wait_v1 |
2548 | */ |
2549 | static PSI_table_locker* |
2550 | start_table_io_wait_v1(PSI_table_locker_state *state, |
2551 | PSI_table *table, |
2552 | PSI_table_io_operation op, |
2553 | uint index, |
2554 | const char *src_file, uint src_line) |
2555 | { |
2556 | DBUG_ASSERT(static_cast<int> (op) >= 0); |
2557 | DBUG_ASSERT(static_cast<uint> (op) < array_elements(table_io_operation_map)); |
2558 | DBUG_ASSERT(state != NULL); |
2559 | PFS_table *pfs_table= reinterpret_cast<PFS_table*> (table); |
2560 | DBUG_ASSERT(pfs_table != NULL); |
2561 | DBUG_ASSERT(pfs_table->m_share != NULL); |
2562 | |
2563 | if (! pfs_table->m_io_enabled) |
2564 | return NULL; |
2565 | |
2566 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
2567 | |
2568 | uint flags; |
2569 | ulonglong timer_start= 0; |
2570 | |
2571 | if (flag_thread_instrumentation) |
2572 | { |
2573 | if (pfs_thread == NULL) |
2574 | return NULL; |
2575 | if (! pfs_thread->m_enabled) |
2576 | return NULL; |
2577 | state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); |
2578 | flags= STATE_FLAG_THREAD; |
2579 | |
2580 | if (pfs_table->m_io_timed) |
2581 | { |
2582 | timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); |
2583 | state->m_timer_start= timer_start; |
2584 | flags|= STATE_FLAG_TIMED; |
2585 | } |
2586 | |
2587 | if (flag_events_waits_current) |
2588 | { |
2589 | if (unlikely(pfs_thread->m_events_waits_current >= |
2590 | & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) |
2591 | { |
2592 | locker_lost++; |
2593 | return NULL; |
2594 | } |
2595 | PFS_events_waits *wait= pfs_thread->m_events_waits_current; |
2596 | state->m_wait= wait; |
2597 | flags|= STATE_FLAG_EVENT; |
2598 | |
2599 | PFS_events_waits *parent_event= wait - 1; |
2600 | wait->m_event_type= EVENT_TYPE_WAIT; |
2601 | wait->m_nesting_event_id= parent_event->m_event_id; |
2602 | wait->m_nesting_event_type= parent_event->m_event_type; |
2603 | |
2604 | PFS_table_share *share= pfs_table->m_share; |
2605 | wait->m_thread= pfs_thread; |
2606 | wait->m_class= &global_table_io_class; |
2607 | wait->m_timer_start= timer_start; |
2608 | wait->m_timer_end= 0; |
2609 | wait->m_object_instance_addr= pfs_table->m_identity; |
2610 | wait->m_event_id= pfs_thread->m_event_id++; |
2611 | wait->m_end_event_id= 0; |
2612 | wait->m_operation= table_io_operation_map[static_cast<int> (op)]; |
2613 | wait->m_flags= 0; |
2614 | wait->m_object_type= share->get_object_type(); |
2615 | wait->m_weak_table_share= share; |
2616 | wait->m_weak_version= share->get_version(); |
2617 | wait->m_index= index; |
2618 | wait->m_source_file= src_file; |
2619 | wait->m_source_line= src_line; |
2620 | wait->m_wait_class= WAIT_CLASS_TABLE; |
2621 | |
2622 | pfs_thread->m_events_waits_current++; |
2623 | } |
2624 | } |
2625 | else |
2626 | { |
2627 | if (pfs_table->m_io_timed) |
2628 | { |
2629 | timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); |
2630 | state->m_timer_start= timer_start; |
2631 | flags= STATE_FLAG_TIMED; |
2632 | } |
2633 | else |
2634 | { |
2635 | /* TODO: consider a shortcut here */ |
2636 | flags= 0; |
2637 | } |
2638 | } |
2639 | |
2640 | state->m_flags= flags; |
2641 | state->m_table= table; |
2642 | state->m_io_operation= op; |
2643 | state->m_index= index; |
2644 | return reinterpret_cast<PSI_table_locker*> (state); |
2645 | } |
2646 | |
2647 | /** |
2648 | Implementation of the table instrumentation interface. |
2649 | @sa PSI_v1::start_table_lock_wait. |
2650 | */ |
2651 | static PSI_table_locker* |
2652 | start_table_lock_wait_v1(PSI_table_locker_state *state, |
2653 | PSI_table *table, |
2654 | PSI_table_lock_operation op, |
2655 | ulong op_flags, |
2656 | const char *src_file, uint src_line) |
2657 | { |
2658 | DBUG_ASSERT(state != NULL); |
2659 | DBUG_ASSERT((op == PSI_TABLE_LOCK) || (op == PSI_TABLE_EXTERNAL_LOCK)); |
2660 | |
2661 | PFS_table *pfs_table= reinterpret_cast<PFS_table*> (table); |
2662 | |
2663 | DBUG_ASSERT(pfs_table != NULL); |
2664 | DBUG_ASSERT(pfs_table->m_share != NULL); |
2665 | |
2666 | if (! pfs_table->m_lock_enabled) |
2667 | return NULL; |
2668 | |
2669 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
2670 | |
2671 | PFS_TL_LOCK_TYPE lock_type; |
2672 | |
2673 | switch (op) |
2674 | { |
2675 | case PSI_TABLE_LOCK: |
2676 | lock_type= lock_flags_to_lock_type(op_flags); |
2677 | break; |
2678 | case PSI_TABLE_EXTERNAL_LOCK: |
2679 | /* |
2680 | See the handler::external_lock() API design, |
2681 | there is no handler::external_unlock(). |
2682 | */ |
2683 | if (op_flags == F_UNLCK) |
2684 | return NULL; |
2685 | lock_type= external_lock_flags_to_lock_type(op_flags); |
2686 | break; |
2687 | default: |
2688 | lock_type= PFS_TL_READ; |
2689 | DBUG_ASSERT(false); |
2690 | } |
2691 | |
2692 | DBUG_ASSERT((uint) lock_type < array_elements(table_lock_operation_map)); |
2693 | |
2694 | uint flags; |
2695 | ulonglong timer_start= 0; |
2696 | |
2697 | if (flag_thread_instrumentation) |
2698 | { |
2699 | if (pfs_thread == NULL) |
2700 | return NULL; |
2701 | if (! pfs_thread->m_enabled) |
2702 | return NULL; |
2703 | state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); |
2704 | flags= STATE_FLAG_THREAD; |
2705 | |
2706 | if (pfs_table->m_lock_timed) |
2707 | { |
2708 | timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); |
2709 | state->m_timer_start= timer_start; |
2710 | flags|= STATE_FLAG_TIMED; |
2711 | } |
2712 | |
2713 | if (flag_events_waits_current) |
2714 | { |
2715 | if (unlikely(pfs_thread->m_events_waits_current >= |
2716 | & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) |
2717 | { |
2718 | locker_lost++; |
2719 | return NULL; |
2720 | } |
2721 | PFS_events_waits *wait= pfs_thread->m_events_waits_current; |
2722 | state->m_wait= wait; |
2723 | flags|= STATE_FLAG_EVENT; |
2724 | |
2725 | PFS_events_waits *parent_event= wait - 1; |
2726 | wait->m_event_type= EVENT_TYPE_WAIT; |
2727 | wait->m_nesting_event_id= parent_event->m_event_id; |
2728 | wait->m_nesting_event_type= parent_event->m_event_type; |
2729 | |
2730 | PFS_table_share *share= pfs_table->m_share; |
2731 | wait->m_thread= pfs_thread; |
2732 | wait->m_class= &global_table_lock_class; |
2733 | wait->m_timer_start= timer_start; |
2734 | wait->m_timer_end= 0; |
2735 | wait->m_object_instance_addr= pfs_table->m_identity; |
2736 | wait->m_event_id= pfs_thread->m_event_id++; |
2737 | wait->m_end_event_id= 0; |
2738 | wait->m_operation= table_lock_operation_map[lock_type]; |
2739 | wait->m_flags= 0; |
2740 | wait->m_object_type= share->get_object_type(); |
2741 | wait->m_weak_table_share= share; |
2742 | wait->m_weak_version= share->get_version(); |
2743 | wait->m_index= 0; |
2744 | wait->m_source_file= src_file; |
2745 | wait->m_source_line= src_line; |
2746 | wait->m_wait_class= WAIT_CLASS_TABLE; |
2747 | |
2748 | pfs_thread->m_events_waits_current++; |
2749 | } |
2750 | } |
2751 | else |
2752 | { |
2753 | if (pfs_table->m_lock_timed) |
2754 | { |
2755 | timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); |
2756 | state->m_timer_start= timer_start; |
2757 | flags= STATE_FLAG_TIMED; |
2758 | } |
2759 | else |
2760 | { |
2761 | /* TODO: consider a shortcut here */ |
2762 | flags= 0; |
2763 | } |
2764 | } |
2765 | |
2766 | state->m_flags= flags; |
2767 | state->m_table= table; |
2768 | state->m_index= lock_type; |
2769 | return reinterpret_cast<PSI_table_locker*> (state); |
2770 | } |
2771 | |
2772 | /** |
2773 | Implementation of the file instrumentation interface. |
2774 | @sa PSI_v1::get_thread_file_name_locker. |
2775 | */ |
2776 | static PSI_file_locker* |
2777 | get_thread_file_name_locker_v1(PSI_file_locker_state *state, |
2778 | PSI_file_key key, |
2779 | PSI_file_operation op, |
2780 | const char *name, const void *identity) |
2781 | { |
2782 | DBUG_ASSERT(static_cast<int> (op) >= 0); |
2783 | DBUG_ASSERT(static_cast<uint> (op) < array_elements(file_operation_map)); |
2784 | DBUG_ASSERT(state != NULL); |
2785 | |
2786 | if (psi_unlikely(! flag_global_instrumentation)) |
2787 | return NULL; |
2788 | PFS_file_class *klass= find_file_class(key); |
2789 | if (unlikely(klass == NULL)) |
2790 | return NULL; |
2791 | if (! klass->m_enabled) |
2792 | return NULL; |
2793 | |
2794 | /* Needed for the LF_HASH */ |
2795 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
2796 | if (unlikely(pfs_thread == NULL)) |
2797 | return NULL; |
2798 | |
2799 | if (flag_thread_instrumentation && ! pfs_thread->m_enabled) |
2800 | return NULL; |
2801 | |
2802 | uint flags; |
2803 | |
2804 | state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); |
2805 | flags= STATE_FLAG_THREAD; |
2806 | |
2807 | if (klass->m_timed) |
2808 | flags|= STATE_FLAG_TIMED; |
2809 | |
2810 | if (flag_events_waits_current) |
2811 | { |
2812 | if (unlikely(pfs_thread->m_events_waits_current >= |
2813 | & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) |
2814 | { |
2815 | locker_lost++; |
2816 | return NULL; |
2817 | } |
2818 | PFS_events_waits *wait= pfs_thread->m_events_waits_current; |
2819 | state->m_wait= wait; |
2820 | flags|= STATE_FLAG_EVENT; |
2821 | |
2822 | PFS_events_waits *parent_event= wait - 1; |
2823 | wait->m_event_type= EVENT_TYPE_WAIT; |
2824 | wait->m_nesting_event_id= parent_event->m_event_id; |
2825 | wait->m_nesting_event_type= parent_event->m_event_type; |
2826 | |
2827 | wait->m_thread= pfs_thread; |
2828 | wait->m_class= klass; |
2829 | wait->m_timer_start= 0; |
2830 | wait->m_timer_end= 0; |
2831 | wait->m_object_instance_addr= NULL; |
2832 | wait->m_weak_file= NULL; |
2833 | wait->m_weak_version= 0; |
2834 | wait->m_event_id= pfs_thread->m_event_id++; |
2835 | wait->m_end_event_id= 0; |
2836 | wait->m_operation= file_operation_map[static_cast<int> (op)]; |
2837 | wait->m_wait_class= WAIT_CLASS_FILE; |
2838 | |
2839 | pfs_thread->m_events_waits_current++; |
2840 | } |
2841 | |
2842 | state->m_flags= flags; |
2843 | state->m_file= NULL; |
2844 | state->m_name= name; |
2845 | state->m_class= klass; |
2846 | state->m_operation= op; |
2847 | return reinterpret_cast<PSI_file_locker*> (state); |
2848 | } |
2849 | |
2850 | /** |
2851 | Implementation of the file instrumentation interface. |
2852 | @sa PSI_v1::get_thread_file_stream_locker. |
2853 | */ |
2854 | static PSI_file_locker* |
2855 | get_thread_file_stream_locker_v1(PSI_file_locker_state *state, |
2856 | PSI_file *file, PSI_file_operation op) |
2857 | { |
2858 | PFS_file *pfs_file= reinterpret_cast<PFS_file*> (file); |
2859 | DBUG_ASSERT(static_cast<int> (op) >= 0); |
2860 | DBUG_ASSERT(static_cast<uint> (op) < array_elements(file_operation_map)); |
2861 | DBUG_ASSERT(state != NULL); |
2862 | |
2863 | if (unlikely(pfs_file == NULL)) |
2864 | return NULL; |
2865 | DBUG_ASSERT(pfs_file->m_class != NULL); |
2866 | PFS_file_class *klass= pfs_file->m_class; |
2867 | |
2868 | if (! pfs_file->m_enabled) |
2869 | return NULL; |
2870 | |
2871 | uint flags; |
2872 | |
2873 | if (flag_thread_instrumentation) |
2874 | { |
2875 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
2876 | if (unlikely(pfs_thread == NULL)) |
2877 | return NULL; |
2878 | if (! pfs_thread->m_enabled) |
2879 | return NULL; |
2880 | state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); |
2881 | flags= STATE_FLAG_THREAD; |
2882 | |
2883 | if (pfs_file->m_timed) |
2884 | flags|= STATE_FLAG_TIMED; |
2885 | |
2886 | if (flag_events_waits_current) |
2887 | { |
2888 | if (unlikely(pfs_thread->m_events_waits_current >= |
2889 | & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) |
2890 | { |
2891 | locker_lost++; |
2892 | return NULL; |
2893 | } |
2894 | PFS_events_waits *wait= pfs_thread->m_events_waits_current; |
2895 | state->m_wait= wait; |
2896 | flags|= STATE_FLAG_EVENT; |
2897 | |
2898 | PFS_events_waits *parent_event= wait - 1; |
2899 | wait->m_event_type= EVENT_TYPE_WAIT; |
2900 | wait->m_nesting_event_id= parent_event->m_event_id; |
2901 | wait->m_nesting_event_type= parent_event->m_event_type; |
2902 | |
2903 | wait->m_thread= pfs_thread; |
2904 | wait->m_class= klass; |
2905 | wait->m_timer_start= 0; |
2906 | wait->m_timer_end= 0; |
2907 | wait->m_object_instance_addr= pfs_file; |
2908 | wait->m_weak_file= pfs_file; |
2909 | wait->m_weak_version= pfs_file->get_version(); |
2910 | wait->m_event_id= pfs_thread->m_event_id++; |
2911 | wait->m_end_event_id= 0; |
2912 | wait->m_operation= file_operation_map[static_cast<int> (op)]; |
2913 | wait->m_wait_class= WAIT_CLASS_FILE; |
2914 | |
2915 | pfs_thread->m_events_waits_current++; |
2916 | } |
2917 | } |
2918 | else |
2919 | { |
2920 | state->m_thread= NULL; |
2921 | if (pfs_file->m_timed) |
2922 | { |
2923 | flags= STATE_FLAG_TIMED; |
2924 | } |
2925 | else |
2926 | { |
2927 | /* TODO: consider a shortcut. */ |
2928 | flags= 0; |
2929 | } |
2930 | } |
2931 | |
2932 | state->m_flags= flags; |
2933 | state->m_file= reinterpret_cast<PSI_file*> (pfs_file); |
2934 | state->m_operation= op; |
2935 | state->m_name= NULL; |
2936 | state->m_class= klass; |
2937 | return reinterpret_cast<PSI_file_locker*> (state); |
2938 | } |
2939 | |
2940 | /** |
2941 | Implementation of the file instrumentation interface. |
2942 | @sa PSI_v1::get_thread_file_descriptor_locker. |
2943 | */ |
2944 | static PSI_file_locker* |
2945 | get_thread_file_descriptor_locker_v1(PSI_file_locker_state *state, |
2946 | File file, PSI_file_operation op) |
2947 | { |
2948 | int index= static_cast<int> (file); |
2949 | DBUG_ASSERT(static_cast<int> (op) >= 0); |
2950 | DBUG_ASSERT(static_cast<uint> (op) < array_elements(file_operation_map)); |
2951 | DBUG_ASSERT(state != NULL); |
2952 | |
2953 | if (unlikely((index < 0) || (index >= file_handle_max))) |
2954 | return NULL; |
2955 | |
2956 | PFS_file *pfs_file= file_handle_array[index]; |
2957 | if (unlikely(pfs_file == NULL)) |
2958 | return NULL; |
2959 | |
2960 | /* |
2961 | We are about to close a file by descriptor number, |
2962 | and the calling code still holds the descriptor. |
2963 | Cleanup the file descriptor <--> file instrument association. |
2964 | Remove the instrumentation *before* the close to avoid race |
2965 | conditions with another thread opening a file |
2966 | (that could be given the same descriptor). |
2967 | */ |
2968 | if (op == PSI_FILE_CLOSE) |
2969 | file_handle_array[index]= NULL; |
2970 | |
2971 | if (! pfs_file->m_enabled) |
2972 | return NULL; |
2973 | |
2974 | DBUG_ASSERT(pfs_file->m_class != NULL); |
2975 | PFS_file_class *klass= pfs_file->m_class; |
2976 | |
2977 | uint flags; |
2978 | |
2979 | if (flag_thread_instrumentation) |
2980 | { |
2981 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
2982 | if (unlikely(pfs_thread == NULL)) |
2983 | return NULL; |
2984 | if (! pfs_thread->m_enabled) |
2985 | return NULL; |
2986 | state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); |
2987 | flags= STATE_FLAG_THREAD; |
2988 | |
2989 | if (pfs_file->m_timed) |
2990 | flags|= STATE_FLAG_TIMED; |
2991 | |
2992 | if (flag_events_waits_current) |
2993 | { |
2994 | if (unlikely(pfs_thread->m_events_waits_current >= |
2995 | & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) |
2996 | { |
2997 | locker_lost++; |
2998 | return NULL; |
2999 | } |
3000 | PFS_events_waits *wait= pfs_thread->m_events_waits_current; |
3001 | state->m_wait= wait; |
3002 | flags|= STATE_FLAG_EVENT; |
3003 | |
3004 | PFS_events_waits *parent_event= wait - 1; |
3005 | wait->m_event_type= EVENT_TYPE_WAIT; |
3006 | wait->m_nesting_event_id= parent_event->m_event_id; |
3007 | wait->m_nesting_event_type= parent_event->m_event_type; |
3008 | |
3009 | wait->m_thread= pfs_thread; |
3010 | wait->m_class= klass; |
3011 | wait->m_timer_start= 0; |
3012 | wait->m_timer_end= 0; |
3013 | wait->m_object_instance_addr= pfs_file; |
3014 | wait->m_weak_file= pfs_file; |
3015 | wait->m_weak_version= pfs_file->get_version(); |
3016 | wait->m_event_id= pfs_thread->m_event_id++; |
3017 | wait->m_end_event_id= 0; |
3018 | wait->m_operation= file_operation_map[static_cast<int> (op)]; |
3019 | wait->m_wait_class= WAIT_CLASS_FILE; |
3020 | |
3021 | pfs_thread->m_events_waits_current++; |
3022 | } |
3023 | } |
3024 | else |
3025 | { |
3026 | state->m_thread= NULL; |
3027 | if (pfs_file->m_timed) |
3028 | { |
3029 | flags= STATE_FLAG_TIMED; |
3030 | } |
3031 | else |
3032 | { |
3033 | /* TODO: consider a shortcut. */ |
3034 | flags= 0; |
3035 | } |
3036 | } |
3037 | |
3038 | state->m_flags= flags; |
3039 | state->m_file= reinterpret_cast<PSI_file*> (pfs_file); |
3040 | state->m_operation= op; |
3041 | state->m_name= NULL; |
3042 | state->m_class= klass; |
3043 | return reinterpret_cast<PSI_file_locker*> (state); |
3044 | } |
3045 | |
3046 | /** Socket locker */ |
3047 | |
3048 | static PSI_socket_locker* |
3049 | start_socket_wait_v1(PSI_socket_locker_state *state, |
3050 | PSI_socket *socket, |
3051 | PSI_socket_operation op, |
3052 | size_t count, |
3053 | const char *src_file, uint src_line) |
3054 | { |
3055 | DBUG_ASSERT(static_cast<int> (op) >= 0); |
3056 | DBUG_ASSERT(static_cast<uint> (op) < array_elements(socket_operation_map)); |
3057 | DBUG_ASSERT(state != NULL); |
3058 | PFS_socket *pfs_socket= reinterpret_cast<PFS_socket*> (socket); |
3059 | |
3060 | DBUG_ASSERT(pfs_socket != NULL); |
3061 | DBUG_ASSERT(pfs_socket->m_class != NULL); |
3062 | |
3063 | if (!pfs_socket->m_enabled || pfs_socket->m_idle) |
3064 | return NULL; |
3065 | |
3066 | uint flags= 0; |
3067 | ulonglong timer_start= 0; |
3068 | |
3069 | if (flag_thread_instrumentation) |
3070 | { |
3071 | /* |
3072 | Do not use pfs_socket->m_thread_owner here, |
3073 | as different threads may use concurrently the same socket, |
3074 | for example during a KILL. |
3075 | */ |
3076 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
3077 | |
3078 | if (unlikely(pfs_thread == NULL)) |
3079 | return NULL; |
3080 | |
3081 | if (!pfs_thread->m_enabled) |
3082 | return NULL; |
3083 | |
3084 | state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); |
3085 | flags= STATE_FLAG_THREAD; |
3086 | |
3087 | if (pfs_socket->m_timed) |
3088 | { |
3089 | timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); |
3090 | state->m_timer_start= timer_start; |
3091 | flags|= STATE_FLAG_TIMED; |
3092 | } |
3093 | |
3094 | if (flag_events_waits_current) |
3095 | { |
3096 | if (unlikely(pfs_thread->m_events_waits_current >= |
3097 | & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) |
3098 | { |
3099 | locker_lost++; |
3100 | return NULL; |
3101 | } |
3102 | PFS_events_waits *wait= pfs_thread->m_events_waits_current; |
3103 | state->m_wait= wait; |
3104 | flags|= STATE_FLAG_EVENT; |
3105 | |
3106 | PFS_events_waits *parent_event= wait - 1; |
3107 | wait->m_event_type= EVENT_TYPE_WAIT; |
3108 | wait->m_nesting_event_id= parent_event->m_event_id; |
3109 | wait->m_nesting_event_type= parent_event->m_event_type; |
3110 | wait->m_thread= pfs_thread; |
3111 | wait->m_class= pfs_socket->m_class; |
3112 | wait->m_timer_start= timer_start; |
3113 | wait->m_timer_end= 0; |
3114 | wait->m_object_instance_addr= pfs_socket->m_identity; |
3115 | wait->m_weak_socket= pfs_socket; |
3116 | wait->m_weak_version= pfs_socket->get_version(); |
3117 | wait->m_event_id= pfs_thread->m_event_id++; |
3118 | wait->m_end_event_id= 0; |
3119 | wait->m_operation= socket_operation_map[static_cast<int>(op)]; |
3120 | wait->m_source_file= src_file; |
3121 | wait->m_source_line= src_line; |
3122 | wait->m_number_of_bytes= count; |
3123 | wait->m_wait_class= WAIT_CLASS_SOCKET; |
3124 | |
3125 | pfs_thread->m_events_waits_current++; |
3126 | } |
3127 | } |
3128 | else |
3129 | { |
3130 | if (pfs_socket->m_timed) |
3131 | { |
3132 | timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); |
3133 | state->m_timer_start= timer_start; |
3134 | flags= STATE_FLAG_TIMED; |
3135 | } |
3136 | else |
3137 | { |
3138 | /* |
3139 | Even if timing is disabled, end_socket_wait() still needs a locker to |
3140 | capture the number of bytes sent or received by the socket operation. |
3141 | For operations that do not have a byte count, then just increment the |
3142 | event counter and return a NULL locker. |
3143 | */ |
3144 | switch (op) |
3145 | { |
3146 | case PSI_SOCKET_CONNECT: |
3147 | case PSI_SOCKET_CREATE: |
3148 | case PSI_SOCKET_BIND: |
3149 | case PSI_SOCKET_SEEK: |
3150 | case PSI_SOCKET_OPT: |
3151 | case PSI_SOCKET_STAT: |
3152 | case PSI_SOCKET_SHUTDOWN: |
3153 | case PSI_SOCKET_CLOSE: |
3154 | case PSI_SOCKET_SELECT: |
3155 | pfs_socket->m_socket_stat.m_io_stat.m_misc.aggregate_counted(); |
3156 | return NULL; |
3157 | default: |
3158 | break; |
3159 | } |
3160 | } |
3161 | } |
3162 | |
3163 | state->m_flags= flags; |
3164 | state->m_socket= socket; |
3165 | state->m_operation= op; |
3166 | return reinterpret_cast<PSI_socket_locker*> (state); |
3167 | } |
3168 | |
3169 | /** |
3170 | Implementation of the mutex instrumentation interface. |
3171 | @sa PSI_v1::unlock_mutex. |
3172 | */ |
3173 | static void unlock_mutex_v1(PSI_mutex *mutex) |
3174 | { |
3175 | PFS_mutex *pfs_mutex= reinterpret_cast<PFS_mutex*> (mutex); |
3176 | |
3177 | DBUG_ASSERT(pfs_mutex != NULL); |
3178 | |
3179 | /* |
3180 | Note that this code is still protected by the instrumented mutex, |
3181 | and therefore is thread safe. See inline_mysql_mutex_unlock(). |
3182 | */ |
3183 | |
3184 | /* Always update the instrumented state */ |
3185 | pfs_mutex->m_owner= NULL; |
3186 | pfs_mutex->m_last_locked= 0; |
3187 | |
3188 | #ifdef LATER_WL2333 |
3189 | /* |
3190 | See WL#2333: SHOW ENGINE ... LOCK STATUS. |
3191 | PFS_mutex::m_lock_stat is not exposed in user visible tables |
3192 | currently, so there is no point spending time computing it. |
3193 | */ |
3194 | if (! pfs_mutex->m_enabled) |
3195 | return; |
3196 | |
3197 | if (! pfs_mutex->m_timed) |
3198 | return; |
3199 | |
3200 | ulonglong locked_time; |
3201 | locked_time= get_timer_pico_value(wait_timer) - pfs_mutex->m_last_locked; |
3202 | pfs_mutex->m_mutex_stat.m_lock_stat.aggregate_value(locked_time); |
3203 | #endif |
3204 | } |
3205 | |
3206 | /** |
3207 | Implementation of the rwlock instrumentation interface. |
3208 | @sa PSI_v1::unlock_rwlock. |
3209 | */ |
3210 | static void unlock_rwlock_v1(PSI_rwlock *rwlock) |
3211 | { |
3212 | PFS_rwlock *pfs_rwlock= reinterpret_cast<PFS_rwlock*> (rwlock); |
3213 | DBUG_ASSERT(pfs_rwlock != NULL); |
3214 | DBUG_ASSERT(pfs_rwlock == sanitize_rwlock(pfs_rwlock)); |
3215 | DBUG_ASSERT(pfs_rwlock->m_class != NULL); |
3216 | DBUG_ASSERT(pfs_rwlock->m_lock.is_populated()); |
3217 | |
3218 | bool last_writer= false; |
3219 | bool last_reader= false; |
3220 | |
3221 | /* |
3222 | Note that this code is still protected by the instrumented rwlock, |
3223 | and therefore is: |
3224 | - thread safe for write locks |
3225 | - almost thread safe for read locks (pfs_rwlock->m_readers is unsafe). |
3226 | See inline_mysql_rwlock_unlock() |
3227 | */ |
3228 | |
3229 | /* Always update the instrumented state */ |
3230 | if (pfs_rwlock->m_writer != NULL) |
3231 | { |
3232 | /* Nominal case, a writer is unlocking. */ |
3233 | last_writer= true; |
3234 | pfs_rwlock->m_writer= NULL; |
3235 | /* Reset the readers stats, they could be off */ |
3236 | pfs_rwlock->m_readers= 0; |
3237 | } |
3238 | else if (likely(pfs_rwlock->m_readers > 0)) |
3239 | { |
3240 | /* Nominal case, a reader is unlocking. */ |
3241 | if (--(pfs_rwlock->m_readers) == 0) |
3242 | last_reader= true; |
3243 | } |
3244 | else |
3245 | { |
3246 | /* |
3247 | Edge case, we have no writer and no readers, |
3248 | on an unlock event. |
3249 | This is possible for: |
3250 | - partial instrumentation |
3251 | - instrumentation disabled at runtime, |
3252 | see when get_thread_rwlock_locker_v1() returns NULL |
3253 | No further action is taken here, the next |
3254 | write lock will put the statistics is a valid state. |
3255 | */ |
3256 | } |
3257 | |
3258 | #ifdef LATER_WL2333 |
3259 | /* See WL#2333: SHOW ENGINE ... LOCK STATUS. */ |
3260 | |
3261 | if (! pfs_rwlock->m_enabled) |
3262 | return; |
3263 | |
3264 | if (! pfs_rwlock->m_timed) |
3265 | return; |
3266 | |
3267 | ulonglong locked_time; |
3268 | if (last_writer) |
3269 | { |
3270 | locked_time= get_timer_pico_value(wait_timer) - pfs_rwlock->m_last_written; |
3271 | pfs_rwlock->m_rwlock_stat.m_write_lock_stat.aggregate_value(locked_time); |
3272 | } |
3273 | else if (last_reader) |
3274 | { |
3275 | locked_time= get_timer_pico_value(wait_timer) - pfs_rwlock->m_last_read; |
3276 | pfs_rwlock->m_rwlock_stat.m_read_lock_stat.aggregate_value(locked_time); |
3277 | } |
3278 | #else |
3279 | (void) last_reader; |
3280 | (void) last_writer; |
3281 | #endif |
3282 | } |
3283 | |
3284 | /** |
3285 | Implementation of the cond instrumentation interface. |
3286 | @sa PSI_v1::signal_cond. |
3287 | */ |
3288 | static void signal_cond_v1(PSI_cond* cond) |
3289 | { |
3290 | PFS_cond *pfs_cond= reinterpret_cast<PFS_cond*> (cond); |
3291 | |
3292 | DBUG_ASSERT(pfs_cond != NULL); |
3293 | |
3294 | pfs_cond->m_cond_stat.m_signal_count++; |
3295 | } |
3296 | |
3297 | /** |
3298 | Implementation of the cond instrumentation interface. |
3299 | @sa PSI_v1::broadcast_cond. |
3300 | */ |
3301 | static void broadcast_cond_v1(PSI_cond* cond) |
3302 | { |
3303 | PFS_cond *pfs_cond= reinterpret_cast<PFS_cond*> (cond); |
3304 | |
3305 | DBUG_ASSERT(pfs_cond != NULL); |
3306 | |
3307 | pfs_cond->m_cond_stat.m_broadcast_count++; |
3308 | } |
3309 | |
3310 | /** |
3311 | Implementation of the idle instrumentation interface. |
3312 | @sa PSI_v1::start_idle_wait. |
3313 | */ |
3314 | static PSI_idle_locker* |
3315 | start_idle_wait_v1(PSI_idle_locker_state* state, const char *src_file, uint src_line) |
3316 | { |
3317 | DBUG_ASSERT(state != NULL); |
3318 | |
3319 | if (psi_unlikely(! flag_global_instrumentation)) |
3320 | return NULL; |
3321 | |
3322 | if (!global_idle_class.m_enabled) |
3323 | return NULL; |
3324 | |
3325 | uint flags= 0; |
3326 | ulonglong timer_start= 0; |
3327 | |
3328 | if (flag_thread_instrumentation) |
3329 | { |
3330 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
3331 | if (unlikely(pfs_thread == NULL)) |
3332 | return NULL; |
3333 | if (!pfs_thread->m_enabled) |
3334 | return NULL; |
3335 | state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); |
3336 | flags= STATE_FLAG_THREAD; |
3337 | |
3338 | DBUG_ASSERT(pfs_thread->m_events_statements_count == 0); |
3339 | |
3340 | if (global_idle_class.m_timed) |
3341 | { |
3342 | timer_start= get_timer_raw_value_and_function(idle_timer, &state->m_timer); |
3343 | state->m_timer_start= timer_start; |
3344 | flags|= STATE_FLAG_TIMED; |
3345 | } |
3346 | |
3347 | if (flag_events_waits_current) |
3348 | { |
3349 | if (unlikely(pfs_thread->m_events_waits_current >= |
3350 | & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) |
3351 | { |
3352 | locker_lost++; |
3353 | return NULL; |
3354 | } |
3355 | PFS_events_waits *wait= pfs_thread->m_events_waits_current; |
3356 | state->m_wait= wait; |
3357 | flags|= STATE_FLAG_EVENT; |
3358 | |
3359 | wait->m_event_type= EVENT_TYPE_WAIT; |
3360 | /* |
3361 | IDLE events are waits, but by definition we know that |
3362 | such waits happen outside of any STAGE and STATEMENT, |
3363 | so they have no parents. |
3364 | */ |
3365 | wait->m_nesting_event_id= 0; |
3366 | /* no need to set wait->m_nesting_event_type */ |
3367 | |
3368 | wait->m_thread= pfs_thread; |
3369 | wait->m_class= &global_idle_class; |
3370 | wait->m_timer_start= timer_start; |
3371 | wait->m_timer_end= 0; |
3372 | wait->m_event_id= pfs_thread->m_event_id++; |
3373 | wait->m_end_event_id= 0; |
3374 | wait->m_operation= OPERATION_TYPE_IDLE; |
3375 | wait->m_source_file= src_file; |
3376 | wait->m_source_line= src_line; |
3377 | wait->m_wait_class= WAIT_CLASS_IDLE; |
3378 | |
3379 | pfs_thread->m_events_waits_current++; |
3380 | } |
3381 | } |
3382 | else |
3383 | { |
3384 | if (global_idle_class.m_timed) |
3385 | { |
3386 | timer_start= get_timer_raw_value_and_function(idle_timer, &state->m_timer); |
3387 | state->m_timer_start= timer_start; |
3388 | flags= STATE_FLAG_TIMED; |
3389 | } |
3390 | } |
3391 | |
3392 | state->m_flags= flags; |
3393 | return reinterpret_cast<PSI_idle_locker*> (state); |
3394 | } |
3395 | |
3396 | /** |
3397 | Implementation of the mutex instrumentation interface. |
3398 | @sa PSI_v1::end_idle_wait. |
3399 | */ |
3400 | static void end_idle_wait_v1(PSI_idle_locker* locker) |
3401 | { |
3402 | PSI_idle_locker_state *state= reinterpret_cast<PSI_idle_locker_state*> (locker); |
3403 | DBUG_ASSERT(state != NULL); |
3404 | ulonglong timer_end= 0; |
3405 | ulonglong wait_time= 0; |
3406 | |
3407 | uint flags= state->m_flags; |
3408 | |
3409 | if (flags & STATE_FLAG_TIMED) |
3410 | { |
3411 | timer_end= state->m_timer(); |
3412 | wait_time= timer_end - state->m_timer_start; |
3413 | } |
3414 | |
3415 | if (flags & STATE_FLAG_THREAD) |
3416 | { |
3417 | PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); |
3418 | PFS_single_stat *event_name_array; |
3419 | event_name_array= thread->m_instr_class_waits_stats; |
3420 | |
3421 | if (flags & STATE_FLAG_TIMED) |
3422 | { |
3423 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ |
3424 | event_name_array[GLOBAL_IDLE_EVENT_INDEX].aggregate_value(wait_time); |
3425 | } |
3426 | else |
3427 | { |
3428 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ |
3429 | event_name_array[GLOBAL_IDLE_EVENT_INDEX].aggregate_counted(); |
3430 | } |
3431 | |
3432 | if (flags & STATE_FLAG_EVENT) |
3433 | { |
3434 | PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); |
3435 | DBUG_ASSERT(wait != NULL); |
3436 | |
3437 | wait->m_timer_end= timer_end; |
3438 | wait->m_end_event_id= thread->m_event_id; |
3439 | if (flag_events_waits_history) |
3440 | insert_events_waits_history(thread, wait); |
3441 | if (flag_events_waits_history_long) |
3442 | insert_events_waits_history_long(wait); |
3443 | thread->m_events_waits_current--; |
3444 | |
3445 | DBUG_ASSERT(wait == thread->m_events_waits_current); |
3446 | } |
3447 | } |
3448 | |
3449 | if (flags & STATE_FLAG_TIMED) |
3450 | { |
3451 | /* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME (timed) */ |
3452 | global_idle_stat.aggregate_value(wait_time); |
3453 | } |
3454 | else |
3455 | { |
3456 | /* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME (counted) */ |
3457 | global_idle_stat.aggregate_counted(); |
3458 | } |
3459 | } |
3460 | |
3461 | /** |
3462 | Implementation of the mutex instrumentation interface. |
3463 | @sa PSI_v1::end_mutex_wait. |
3464 | */ |
3465 | static void end_mutex_wait_v1(PSI_mutex_locker* locker, int rc) |
3466 | { |
3467 | PSI_mutex_locker_state *state= reinterpret_cast<PSI_mutex_locker_state*> (locker); |
3468 | DBUG_ASSERT(state != NULL); |
3469 | |
3470 | ulonglong timer_end= 0; |
3471 | ulonglong wait_time= 0; |
3472 | |
3473 | PFS_mutex *mutex= reinterpret_cast<PFS_mutex *> (state->m_mutex); |
3474 | DBUG_ASSERT(mutex != NULL); |
3475 | PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); |
3476 | |
3477 | uint flags= state->m_flags; |
3478 | |
3479 | if (flags & STATE_FLAG_TIMED) |
3480 | { |
3481 | timer_end= state->m_timer(); |
3482 | wait_time= timer_end - state->m_timer_start; |
3483 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */ |
3484 | mutex->m_mutex_stat.m_wait_stat.aggregate_value(wait_time); |
3485 | } |
3486 | else |
3487 | { |
3488 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ |
3489 | mutex->m_mutex_stat.m_wait_stat.aggregate_counted(); |
3490 | } |
3491 | |
3492 | if (likely(rc == 0)) |
3493 | { |
3494 | mutex->m_owner= thread; |
3495 | mutex->m_last_locked= timer_end; |
3496 | } |
3497 | |
3498 | if (flags & STATE_FLAG_THREAD) |
3499 | { |
3500 | PFS_single_stat *event_name_array; |
3501 | event_name_array= thread->m_instr_class_waits_stats; |
3502 | uint index= mutex->m_class->m_event_name_index; |
3503 | |
3504 | if (flags & STATE_FLAG_TIMED) |
3505 | { |
3506 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ |
3507 | event_name_array[index].aggregate_value(wait_time); |
3508 | } |
3509 | else |
3510 | { |
3511 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ |
3512 | event_name_array[index].aggregate_counted(); |
3513 | } |
3514 | |
3515 | if (flags & STATE_FLAG_EVENT) |
3516 | { |
3517 | PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); |
3518 | DBUG_ASSERT(wait != NULL); |
3519 | |
3520 | wait->m_timer_end= timer_end; |
3521 | wait->m_end_event_id= thread->m_event_id; |
3522 | if (flag_events_waits_history) |
3523 | insert_events_waits_history(thread, wait); |
3524 | if (flag_events_waits_history_long) |
3525 | insert_events_waits_history_long(wait); |
3526 | thread->m_events_waits_current--; |
3527 | |
3528 | DBUG_ASSERT(wait == thread->m_events_waits_current); |
3529 | } |
3530 | } |
3531 | } |
3532 | |
3533 | /** |
3534 | Implementation of the rwlock instrumentation interface. |
3535 | @sa PSI_v1::end_rwlock_rdwait. |
3536 | */ |
3537 | static void end_rwlock_rdwait_v1(PSI_rwlock_locker* locker, int rc) |
3538 | { |
3539 | PSI_rwlock_locker_state *state= reinterpret_cast<PSI_rwlock_locker_state*> (locker); |
3540 | DBUG_ASSERT(state != NULL); |
3541 | |
3542 | ulonglong timer_end= 0; |
3543 | ulonglong wait_time= 0; |
3544 | |
3545 | PFS_rwlock *rwlock= reinterpret_cast<PFS_rwlock *> (state->m_rwlock); |
3546 | DBUG_ASSERT(rwlock != NULL); |
3547 | |
3548 | if (state->m_flags & STATE_FLAG_TIMED) |
3549 | { |
3550 | timer_end= state->m_timer(); |
3551 | wait_time= timer_end - state->m_timer_start; |
3552 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */ |
3553 | rwlock->m_rwlock_stat.m_wait_stat.aggregate_value(wait_time); |
3554 | } |
3555 | else |
3556 | { |
3557 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ |
3558 | rwlock->m_rwlock_stat.m_wait_stat.aggregate_counted(); |
3559 | } |
3560 | |
3561 | if (rc == 0) |
3562 | { |
3563 | /* |
3564 | Warning: |
3565 | Multiple threads can execute this section concurrently |
3566 | (since multiple readers can execute in parallel). |
3567 | The statistics generated are not safe, which is why they are |
3568 | just statistics, not facts. |
3569 | */ |
3570 | if (rwlock->m_readers == 0) |
3571 | rwlock->m_last_read= timer_end; |
3572 | rwlock->m_writer= NULL; |
3573 | rwlock->m_readers++; |
3574 | } |
3575 | |
3576 | if (state->m_flags & STATE_FLAG_THREAD) |
3577 | { |
3578 | PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); |
3579 | DBUG_ASSERT(thread != NULL); |
3580 | |
3581 | PFS_single_stat *event_name_array; |
3582 | event_name_array= thread->m_instr_class_waits_stats; |
3583 | uint index= rwlock->m_class->m_event_name_index; |
3584 | |
3585 | if (state->m_flags & STATE_FLAG_TIMED) |
3586 | { |
3587 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ |
3588 | event_name_array[index].aggregate_value(wait_time); |
3589 | } |
3590 | else |
3591 | { |
3592 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ |
3593 | event_name_array[index].aggregate_counted(); |
3594 | } |
3595 | |
3596 | if (state->m_flags & STATE_FLAG_EVENT) |
3597 | { |
3598 | PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); |
3599 | DBUG_ASSERT(wait != NULL); |
3600 | |
3601 | wait->m_timer_end= timer_end; |
3602 | wait->m_end_event_id= thread->m_event_id; |
3603 | if (flag_events_waits_history) |
3604 | insert_events_waits_history(thread, wait); |
3605 | if (flag_events_waits_history_long) |
3606 | insert_events_waits_history_long(wait); |
3607 | thread->m_events_waits_current--; |
3608 | |
3609 | DBUG_ASSERT(wait == thread->m_events_waits_current); |
3610 | } |
3611 | } |
3612 | } |
3613 | |
3614 | /** |
3615 | Implementation of the rwlock instrumentation interface. |
3616 | @sa PSI_v1::end_rwlock_wrwait. |
3617 | */ |
3618 | static void end_rwlock_wrwait_v1(PSI_rwlock_locker* locker, int rc) |
3619 | { |
3620 | PSI_rwlock_locker_state *state= reinterpret_cast<PSI_rwlock_locker_state*> (locker); |
3621 | DBUG_ASSERT(state != NULL); |
3622 | |
3623 | ulonglong timer_end= 0; |
3624 | ulonglong wait_time= 0; |
3625 | |
3626 | PFS_rwlock *rwlock= reinterpret_cast<PFS_rwlock *> (state->m_rwlock); |
3627 | DBUG_ASSERT(rwlock != NULL); |
3628 | PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); |
3629 | |
3630 | if (state->m_flags & STATE_FLAG_TIMED) |
3631 | { |
3632 | timer_end= state->m_timer(); |
3633 | wait_time= timer_end - state->m_timer_start; |
3634 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */ |
3635 | rwlock->m_rwlock_stat.m_wait_stat.aggregate_value(wait_time); |
3636 | } |
3637 | else |
3638 | { |
3639 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ |
3640 | rwlock->m_rwlock_stat.m_wait_stat.aggregate_counted(); |
3641 | } |
3642 | |
3643 | if (likely(rc == 0)) |
3644 | { |
3645 | /* Thread safe : we are protected by the instrumented rwlock */ |
3646 | rwlock->m_writer= thread; |
3647 | rwlock->m_last_written= timer_end; |
3648 | /* Reset the readers stats, they could be off */ |
3649 | rwlock->m_readers= 0; |
3650 | rwlock->m_last_read= 0; |
3651 | } |
3652 | |
3653 | if (state->m_flags & STATE_FLAG_THREAD) |
3654 | { |
3655 | PFS_single_stat *event_name_array; |
3656 | event_name_array= thread->m_instr_class_waits_stats; |
3657 | uint index= rwlock->m_class->m_event_name_index; |
3658 | |
3659 | if (state->m_flags & STATE_FLAG_TIMED) |
3660 | { |
3661 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ |
3662 | event_name_array[index].aggregate_value(wait_time); |
3663 | } |
3664 | else |
3665 | { |
3666 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ |
3667 | event_name_array[index].aggregate_counted(); |
3668 | } |
3669 | |
3670 | if (state->m_flags & STATE_FLAG_EVENT) |
3671 | { |
3672 | PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); |
3673 | DBUG_ASSERT(wait != NULL); |
3674 | |
3675 | wait->m_timer_end= timer_end; |
3676 | wait->m_end_event_id= thread->m_event_id; |
3677 | if (flag_events_waits_history) |
3678 | insert_events_waits_history(thread, wait); |
3679 | if (flag_events_waits_history_long) |
3680 | insert_events_waits_history_long(wait); |
3681 | thread->m_events_waits_current--; |
3682 | |
3683 | DBUG_ASSERT(wait == thread->m_events_waits_current); |
3684 | } |
3685 | } |
3686 | } |
3687 | |
3688 | /** |
3689 | Implementation of the cond instrumentation interface. |
3690 | @sa PSI_v1::end_cond_wait. |
3691 | */ |
3692 | static void end_cond_wait_v1(PSI_cond_locker* locker, int rc) |
3693 | { |
3694 | PSI_cond_locker_state *state= reinterpret_cast<PSI_cond_locker_state*> (locker); |
3695 | DBUG_ASSERT(state != NULL); |
3696 | |
3697 | ulonglong timer_end= 0; |
3698 | ulonglong wait_time= 0; |
3699 | |
3700 | PFS_cond *cond= reinterpret_cast<PFS_cond *> (state->m_cond); |
3701 | /* PFS_mutex *mutex= reinterpret_cast<PFS_mutex *> (state->m_mutex); */ |
3702 | |
3703 | if (state->m_flags & STATE_FLAG_TIMED) |
3704 | { |
3705 | timer_end= state->m_timer(); |
3706 | wait_time= timer_end - state->m_timer_start; |
3707 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */ |
3708 | cond->m_cond_stat.m_wait_stat.aggregate_value(wait_time); |
3709 | } |
3710 | else |
3711 | { |
3712 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ |
3713 | cond->m_cond_stat.m_wait_stat.aggregate_counted(); |
3714 | } |
3715 | |
3716 | if (state->m_flags & STATE_FLAG_THREAD) |
3717 | { |
3718 | PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); |
3719 | DBUG_ASSERT(thread != NULL); |
3720 | |
3721 | PFS_single_stat *event_name_array; |
3722 | event_name_array= thread->m_instr_class_waits_stats; |
3723 | uint index= cond->m_class->m_event_name_index; |
3724 | |
3725 | if (state->m_flags & STATE_FLAG_TIMED) |
3726 | { |
3727 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ |
3728 | event_name_array[index].aggregate_value(wait_time); |
3729 | } |
3730 | else |
3731 | { |
3732 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ |
3733 | event_name_array[index].aggregate_counted(); |
3734 | } |
3735 | |
3736 | if (state->m_flags & STATE_FLAG_EVENT) |
3737 | { |
3738 | PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); |
3739 | DBUG_ASSERT(wait != NULL); |
3740 | |
3741 | wait->m_timer_end= timer_end; |
3742 | wait->m_end_event_id= thread->m_event_id; |
3743 | if (flag_events_waits_history) |
3744 | insert_events_waits_history(thread, wait); |
3745 | if (flag_events_waits_history_long) |
3746 | insert_events_waits_history_long(wait); |
3747 | thread->m_events_waits_current--; |
3748 | |
3749 | DBUG_ASSERT(wait == thread->m_events_waits_current); |
3750 | } |
3751 | } |
3752 | } |
3753 | |
3754 | /** |
3755 | Implementation of the table instrumentation interface. |
3756 | @sa PSI_v1::end_table_io_wait. |
3757 | */ |
3758 | static void end_table_io_wait_v1(PSI_table_locker* locker) |
3759 | { |
3760 | PSI_table_locker_state *state= reinterpret_cast<PSI_table_locker_state*> (locker); |
3761 | DBUG_ASSERT(state != NULL); |
3762 | |
3763 | ulonglong timer_end= 0; |
3764 | ulonglong wait_time= 0; |
3765 | |
3766 | PFS_table *table= reinterpret_cast<PFS_table *> (state->m_table); |
3767 | DBUG_ASSERT(table != NULL); |
3768 | |
3769 | PFS_single_stat *stat; |
3770 | PFS_table_io_stat *table_io_stat; |
3771 | |
3772 | DBUG_ASSERT((state->m_index < table->m_share->m_key_count) || |
3773 | (state->m_index == MAX_INDEXES)); |
3774 | |
3775 | table_io_stat= & table->m_table_stat.m_index_stat[state->m_index]; |
3776 | table_io_stat->m_has_data= true; |
3777 | |
3778 | switch (state->m_io_operation) |
3779 | { |
3780 | case PSI_TABLE_FETCH_ROW: |
3781 | stat= & table_io_stat->m_fetch; |
3782 | break; |
3783 | case PSI_TABLE_WRITE_ROW: |
3784 | stat= & table_io_stat->m_insert; |
3785 | break; |
3786 | case PSI_TABLE_UPDATE_ROW: |
3787 | stat= & table_io_stat->m_update; |
3788 | break; |
3789 | case PSI_TABLE_DELETE_ROW: |
3790 | stat= & table_io_stat->m_delete; |
3791 | break; |
3792 | default: |
3793 | DBUG_ASSERT(false); |
3794 | stat= NULL; |
3795 | break; |
3796 | } |
3797 | |
3798 | uint flags= state->m_flags; |
3799 | |
3800 | if (flags & STATE_FLAG_TIMED) |
3801 | { |
3802 | timer_end= state->m_timer(); |
3803 | wait_time= timer_end - state->m_timer_start; |
3804 | stat->aggregate_value(wait_time); |
3805 | } |
3806 | else |
3807 | { |
3808 | stat->aggregate_counted(); |
3809 | } |
3810 | |
3811 | if (flags & STATE_FLAG_THREAD) |
3812 | { |
3813 | PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); |
3814 | DBUG_ASSERT(thread != NULL); |
3815 | |
3816 | PFS_single_stat *event_name_array; |
3817 | event_name_array= thread->m_instr_class_waits_stats; |
3818 | |
3819 | /* |
3820 | Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME |
3821 | (for wait/io/table/sql/handler) |
3822 | */ |
3823 | if (flags & STATE_FLAG_TIMED) |
3824 | { |
3825 | event_name_array[GLOBAL_TABLE_IO_EVENT_INDEX].aggregate_value(wait_time); |
3826 | } |
3827 | else |
3828 | { |
3829 | event_name_array[GLOBAL_TABLE_IO_EVENT_INDEX].aggregate_counted(); |
3830 | } |
3831 | |
3832 | if (flags & STATE_FLAG_EVENT) |
3833 | { |
3834 | PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); |
3835 | DBUG_ASSERT(wait != NULL); |
3836 | |
3837 | wait->m_timer_end= timer_end; |
3838 | wait->m_end_event_id= thread->m_event_id; |
3839 | if (flag_events_waits_history) |
3840 | insert_events_waits_history(thread, wait); |
3841 | if (flag_events_waits_history_long) |
3842 | insert_events_waits_history_long(wait); |
3843 | thread->m_events_waits_current--; |
3844 | |
3845 | DBUG_ASSERT(wait == thread->m_events_waits_current); |
3846 | } |
3847 | } |
3848 | |
3849 | table->m_has_io_stats= true; |
3850 | } |
3851 | |
3852 | /** |
3853 | Implementation of the table instrumentation interface. |
3854 | @sa PSI_v1::end_table_lock_wait. |
3855 | */ |
3856 | static void end_table_lock_wait_v1(PSI_table_locker* locker) |
3857 | { |
3858 | PSI_table_locker_state *state= reinterpret_cast<PSI_table_locker_state*> (locker); |
3859 | DBUG_ASSERT(state != NULL); |
3860 | |
3861 | ulonglong timer_end= 0; |
3862 | ulonglong wait_time= 0; |
3863 | |
3864 | PFS_table *table= reinterpret_cast<PFS_table *> (state->m_table); |
3865 | DBUG_ASSERT(table != NULL); |
3866 | |
3867 | PFS_single_stat *stat= & table->m_table_stat.m_lock_stat.m_stat[state->m_index]; |
3868 | |
3869 | uint flags= state->m_flags; |
3870 | |
3871 | if (flags & STATE_FLAG_TIMED) |
3872 | { |
3873 | timer_end= state->m_timer(); |
3874 | wait_time= timer_end - state->m_timer_start; |
3875 | stat->aggregate_value(wait_time); |
3876 | } |
3877 | else |
3878 | { |
3879 | stat->aggregate_counted(); |
3880 | } |
3881 | |
3882 | if (flags & STATE_FLAG_THREAD) |
3883 | { |
3884 | PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); |
3885 | DBUG_ASSERT(thread != NULL); |
3886 | |
3887 | PFS_single_stat *event_name_array; |
3888 | event_name_array= thread->m_instr_class_waits_stats; |
3889 | |
3890 | /* |
3891 | Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME |
3892 | (for wait/lock/table/sql/handler) |
3893 | */ |
3894 | if (flags & STATE_FLAG_TIMED) |
3895 | { |
3896 | event_name_array[GLOBAL_TABLE_LOCK_EVENT_INDEX].aggregate_value(wait_time); |
3897 | } |
3898 | else |
3899 | { |
3900 | event_name_array[GLOBAL_TABLE_LOCK_EVENT_INDEX].aggregate_counted(); |
3901 | } |
3902 | |
3903 | if (flags & STATE_FLAG_EVENT) |
3904 | { |
3905 | PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); |
3906 | DBUG_ASSERT(wait != NULL); |
3907 | |
3908 | wait->m_timer_end= timer_end; |
3909 | wait->m_end_event_id= thread->m_event_id; |
3910 | if (flag_events_waits_history) |
3911 | insert_events_waits_history(thread, wait); |
3912 | if (flag_events_waits_history_long) |
3913 | insert_events_waits_history_long(wait); |
3914 | thread->m_events_waits_current--; |
3915 | |
3916 | DBUG_ASSERT(wait == thread->m_events_waits_current); |
3917 | } |
3918 | } |
3919 | |
3920 | table->m_has_lock_stats= true; |
3921 | } |
3922 | |
3923 | static void start_file_wait_v1(PSI_file_locker *locker, |
3924 | size_t count, |
3925 | const char *src_file, |
3926 | uint src_line); |
3927 | |
3928 | static void end_file_wait_v1(PSI_file_locker *locker, |
3929 | size_t count); |
3930 | |
3931 | /** |
3932 | Implementation of the file instrumentation interface. |
3933 | @sa PSI_v1::start_file_open_wait. |
3934 | */ |
3935 | static void start_file_open_wait_v1(PSI_file_locker *locker, |
3936 | const char *src_file, |
3937 | uint src_line) |
3938 | { |
3939 | start_file_wait_v1(locker, 0, src_file, src_line); |
3940 | |
3941 | return; |
3942 | } |
3943 | |
3944 | /** |
3945 | Implementation of the file instrumentation interface. |
3946 | @sa PSI_v1::end_file_open_wait. |
3947 | */ |
3948 | static PSI_file* end_file_open_wait_v1(PSI_file_locker *locker, |
3949 | void *result) |
3950 | { |
3951 | PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); |
3952 | DBUG_ASSERT(state != NULL); |
3953 | |
3954 | switch (state->m_operation) |
3955 | { |
3956 | case PSI_FILE_STAT: |
3957 | case PSI_FILE_RENAME: |
3958 | break; |
3959 | case PSI_FILE_STREAM_OPEN: |
3960 | case PSI_FILE_CREATE: |
3961 | case PSI_FILE_OPEN: |
3962 | if (result != NULL) |
3963 | { |
3964 | PFS_file_class *klass= reinterpret_cast<PFS_file_class*> (state->m_class); |
3965 | PFS_thread *thread= reinterpret_cast<PFS_thread*> (state->m_thread); |
3966 | const char *name= state->m_name; |
3967 | uint len= (uint)strlen(name); |
3968 | PFS_file *pfs_file= find_or_create_file(thread, klass, name, len, true); |
3969 | state->m_file= reinterpret_cast<PSI_file*> (pfs_file); |
3970 | } |
3971 | break; |
3972 | default: |
3973 | DBUG_ASSERT(false); |
3974 | break; |
3975 | } |
3976 | |
3977 | end_file_wait_v1(locker, 0); |
3978 | |
3979 | return state->m_file; |
3980 | } |
3981 | |
3982 | /** |
3983 | Implementation of the file instrumentation interface. |
3984 | @sa PSI_v1::end_file_open_wait_and_bind_to_descriptor. |
3985 | */ |
3986 | static void end_file_open_wait_and_bind_to_descriptor_v1 |
3987 | (PSI_file_locker *locker, File file) |
3988 | { |
3989 | PFS_file *pfs_file= NULL; |
3990 | int index= (int) file; |
3991 | PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); |
3992 | DBUG_ASSERT(state != NULL); |
3993 | |
3994 | if (index >= 0) |
3995 | { |
3996 | PFS_file_class *klass= reinterpret_cast<PFS_file_class*> (state->m_class); |
3997 | PFS_thread *thread= reinterpret_cast<PFS_thread*> (state->m_thread); |
3998 | const char *name= state->m_name; |
3999 | uint len= (uint)strlen(name); |
4000 | pfs_file= find_or_create_file(thread, klass, name, len, true); |
4001 | state->m_file= reinterpret_cast<PSI_file*> (pfs_file); |
4002 | } |
4003 | |
4004 | end_file_wait_v1(locker, 0); |
4005 | |
4006 | if (likely(index >= 0)) |
4007 | { |
4008 | if (likely(index < file_handle_max)) |
4009 | file_handle_array[index]= pfs_file; |
4010 | else |
4011 | { |
4012 | if (pfs_file != NULL) |
4013 | release_file(pfs_file); |
4014 | file_handle_lost++; |
4015 | } |
4016 | } |
4017 | } |
4018 | |
4019 | /** |
4020 | Implementation of the file instrumentation interface. |
4021 | @sa PSI_v1::start_file_wait. |
4022 | */ |
4023 | static void start_file_wait_v1(PSI_file_locker *locker, |
4024 | size_t count, |
4025 | const char *src_file, |
4026 | uint src_line) |
4027 | { |
4028 | ulonglong timer_start= 0; |
4029 | PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); |
4030 | DBUG_ASSERT(state != NULL); |
4031 | |
4032 | uint flags= state->m_flags; |
4033 | |
4034 | if (flags & STATE_FLAG_TIMED) |
4035 | { |
4036 | timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); |
4037 | state->m_timer_start= timer_start; |
4038 | } |
4039 | |
4040 | if (flags & STATE_FLAG_EVENT) |
4041 | { |
4042 | PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); |
4043 | DBUG_ASSERT(wait != NULL); |
4044 | |
4045 | wait->m_timer_start= timer_start; |
4046 | wait->m_source_file= src_file; |
4047 | wait->m_source_line= src_line; |
4048 | wait->m_number_of_bytes= count; |
4049 | } |
4050 | } |
4051 | |
4052 | /** |
4053 | Implementation of the file instrumentation interface. |
4054 | @sa PSI_v1::end_file_wait. |
4055 | */ |
4056 | static void end_file_wait_v1(PSI_file_locker *locker, |
4057 | size_t byte_count) |
4058 | { |
4059 | PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); |
4060 | DBUG_ASSERT(state != NULL); |
4061 | PFS_file *file= reinterpret_cast<PFS_file *> (state->m_file); |
4062 | PFS_file_class *klass= reinterpret_cast<PFS_file_class *> (state->m_class); |
4063 | PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); |
4064 | |
4065 | ulonglong timer_end= 0; |
4066 | ulonglong wait_time= 0; |
4067 | PFS_byte_stat *byte_stat; |
4068 | uint flags= state->m_flags; |
4069 | size_t bytes= ((int)byte_count > -1 ? byte_count : 0); |
4070 | |
4071 | PFS_file_stat *file_stat; |
4072 | |
4073 | if (file != NULL) |
4074 | { |
4075 | file_stat= & file->m_file_stat; |
4076 | } |
4077 | else |
4078 | { |
4079 | file_stat= & klass->m_file_stat; |
4080 | } |
4081 | |
4082 | switch (state->m_operation) |
4083 | { |
4084 | /* Group read operations */ |
4085 | case PSI_FILE_READ: |
4086 | byte_stat= &file_stat->m_io_stat.m_read; |
4087 | break; |
4088 | /* Group write operations */ |
4089 | case PSI_FILE_WRITE: |
4090 | byte_stat= &file_stat->m_io_stat.m_write; |
4091 | break; |
4092 | /* Group remaining operations as miscellaneous */ |
4093 | case PSI_FILE_CREATE: |
4094 | case PSI_FILE_CREATE_TMP: |
4095 | case PSI_FILE_OPEN: |
4096 | case PSI_FILE_STREAM_OPEN: |
4097 | case PSI_FILE_STREAM_CLOSE: |
4098 | case PSI_FILE_SEEK: |
4099 | case PSI_FILE_TELL: |
4100 | case PSI_FILE_FLUSH: |
4101 | case PSI_FILE_FSTAT: |
4102 | case PSI_FILE_CHSIZE: |
4103 | case PSI_FILE_DELETE: |
4104 | case PSI_FILE_RENAME: |
4105 | case PSI_FILE_SYNC: |
4106 | case PSI_FILE_STAT: |
4107 | case PSI_FILE_CLOSE: |
4108 | byte_stat= &file_stat->m_io_stat.m_misc; |
4109 | break; |
4110 | default: |
4111 | DBUG_ASSERT(false); |
4112 | byte_stat= NULL; |
4113 | break; |
4114 | } |
4115 | |
4116 | /* Aggregation for EVENTS_WAITS_SUMMARY_BY_INSTANCE */ |
4117 | if (flags & STATE_FLAG_TIMED) |
4118 | { |
4119 | timer_end= state->m_timer(); |
4120 | wait_time= timer_end - state->m_timer_start; |
4121 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */ |
4122 | byte_stat->aggregate(wait_time, bytes); |
4123 | } |
4124 | else |
4125 | { |
4126 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ |
4127 | byte_stat->aggregate_counted(bytes); |
4128 | } |
4129 | |
4130 | if (flags & STATE_FLAG_THREAD) |
4131 | { |
4132 | DBUG_ASSERT(thread != NULL); |
4133 | |
4134 | PFS_single_stat *event_name_array; |
4135 | event_name_array= thread->m_instr_class_waits_stats; |
4136 | uint index= klass->m_event_name_index; |
4137 | |
4138 | if (flags & STATE_FLAG_TIMED) |
4139 | { |
4140 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ |
4141 | event_name_array[index].aggregate_value(wait_time); |
4142 | } |
4143 | else |
4144 | { |
4145 | /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ |
4146 | event_name_array[index].aggregate_counted(); |
4147 | } |
4148 | |
4149 | if (state->m_flags & STATE_FLAG_EVENT) |
4150 | { |
4151 | PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); |
4152 | DBUG_ASSERT(wait != NULL); |
4153 | |
4154 | wait->m_timer_end= timer_end; |
4155 | wait->m_number_of_bytes= bytes; |
4156 | wait->m_end_event_id= thread->m_event_id; |
4157 | wait->m_object_instance_addr= file; |
4158 | wait->m_weak_file= file; |
4159 | wait->m_weak_version= (file ? file->get_version() : 0); |
4160 | |
4161 | if (flag_events_waits_history) |
4162 | insert_events_waits_history(thread, wait); |
4163 | if (flag_events_waits_history_long) |
4164 | insert_events_waits_history_long(wait); |
4165 | thread->m_events_waits_current--; |
4166 | |
4167 | DBUG_ASSERT(wait == thread->m_events_waits_current); |
4168 | } |
4169 | } |
4170 | } |
4171 | |
4172 | /** |
4173 | Implementation of the file instrumentation interface. |
4174 | @sa PSI_v1::start_file_close_wait. |
4175 | */ |
4176 | static void start_file_close_wait_v1(PSI_file_locker *locker, |
4177 | const char *src_file, |
4178 | uint src_line) |
4179 | { |
4180 | PFS_thread *thread; |
4181 | const char *name; |
4182 | uint len; |
4183 | PFS_file *pfs_file; |
4184 | PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); |
4185 | DBUG_ASSERT(state != NULL); |
4186 | |
4187 | switch (state->m_operation) |
4188 | { |
4189 | case PSI_FILE_DELETE: |
4190 | thread= reinterpret_cast<PFS_thread*> (state->m_thread); |
4191 | name= state->m_name; |
4192 | len= (uint)strlen(name); |
4193 | pfs_file= find_or_create_file(thread, NULL, name, len, false); |
4194 | state->m_file= reinterpret_cast<PSI_file*> (pfs_file); |
4195 | break; |
4196 | case PSI_FILE_STREAM_CLOSE: |
4197 | case PSI_FILE_CLOSE: |
4198 | break; |
4199 | default: |
4200 | DBUG_ASSERT(false); |
4201 | break; |
4202 | } |
4203 | |
4204 | start_file_wait_v1(locker, 0, src_file, src_line); |
4205 | |
4206 | return; |
4207 | } |
4208 | |
4209 | /** |
4210 | Implementation of the file instrumentation interface. |
4211 | @sa PSI_v1::end_file_close_wait. |
4212 | */ |
4213 | static void end_file_close_wait_v1(PSI_file_locker *locker, int rc) |
4214 | { |
4215 | PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); |
4216 | DBUG_ASSERT(state != NULL); |
4217 | |
4218 | end_file_wait_v1(locker, 0); |
4219 | |
4220 | if (rc == 0) |
4221 | { |
4222 | PFS_thread *thread= reinterpret_cast<PFS_thread*> (state->m_thread); |
4223 | PFS_file *file= reinterpret_cast<PFS_file*> (state->m_file); |
4224 | |
4225 | /* Release or destroy the file if necessary */ |
4226 | switch(state->m_operation) |
4227 | { |
4228 | case PSI_FILE_CLOSE: |
4229 | case PSI_FILE_STREAM_CLOSE: |
4230 | if (file != NULL) |
4231 | release_file(file); |
4232 | break; |
4233 | case PSI_FILE_DELETE: |
4234 | if (file != NULL) |
4235 | destroy_file(thread, file); |
4236 | break; |
4237 | default: |
4238 | DBUG_ASSERT(false); |
4239 | break; |
4240 | } |
4241 | } |
4242 | return; |
4243 | } |
4244 | |
4245 | static void start_stage_v1(PSI_stage_key key, const char *src_file, int src_line) |
4246 | { |
4247 | ulonglong timer_value= 0; |
4248 | |
4249 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
4250 | if (unlikely(pfs_thread == NULL)) |
4251 | return; |
4252 | |
4253 | /* Always update column threads.processlist_state. */ |
4254 | pfs_thread->m_stage= key; |
4255 | |
4256 | if (psi_unlikely(! flag_global_instrumentation)) |
4257 | return; |
4258 | |
4259 | if (flag_thread_instrumentation && ! pfs_thread->m_enabled) |
4260 | return; |
4261 | |
4262 | PFS_events_stages *pfs= & pfs_thread->m_stage_current; |
4263 | PFS_events_waits *child_wait= & pfs_thread->m_events_waits_stack[0]; |
4264 | PFS_events_statements *parent_statement= & pfs_thread->m_statement_stack[0]; |
4265 | |
4266 | PFS_instr_class *old_class= pfs->m_class; |
4267 | if (old_class != NULL) |
4268 | { |
4269 | PFS_stage_stat *event_name_array; |
4270 | event_name_array= pfs_thread->m_instr_class_stages_stats; |
4271 | uint index= old_class->m_event_name_index; |
4272 | |
4273 | /* Finish old event */ |
4274 | if (old_class->m_timed) |
4275 | { |
4276 | timer_value= get_timer_raw_value(stage_timer);; |
4277 | pfs->m_timer_end= timer_value; |
4278 | |
4279 | /* Aggregate to EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ |
4280 | ulonglong stage_time= timer_value - pfs->m_timer_start; |
4281 | event_name_array[index].aggregate_value(stage_time); |
4282 | } |
4283 | else |
4284 | { |
4285 | /* Aggregate to EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ |
4286 | event_name_array[index].aggregate_counted(); |
4287 | } |
4288 | |
4289 | if (flag_events_stages_current) |
4290 | { |
4291 | pfs->m_end_event_id= pfs_thread->m_event_id; |
4292 | if (flag_events_stages_history) |
4293 | insert_events_stages_history(pfs_thread, pfs); |
4294 | if (flag_events_stages_history_long) |
4295 | insert_events_stages_history_long(pfs); |
4296 | } |
4297 | |
4298 | /* This stage event is now complete. */ |
4299 | pfs->m_class= NULL; |
4300 | |
4301 | /* New waits will now be attached directly to the parent statement. */ |
4302 | child_wait->m_event_id= parent_statement->m_event_id; |
4303 | child_wait->m_event_type= parent_statement->m_event_type; |
4304 | /* See below for new stages, that may overwrite this. */ |
4305 | } |
4306 | |
4307 | /* Start new event */ |
4308 | |
4309 | PFS_stage_class *new_klass= find_stage_class(key); |
4310 | if (unlikely(new_klass == NULL)) |
4311 | return; |
4312 | |
4313 | if (! new_klass->m_enabled) |
4314 | return; |
4315 | |
4316 | pfs->m_class= new_klass; |
4317 | if (new_klass->m_timed) |
4318 | { |
4319 | /* |
4320 | Do not call the timer again if we have a |
4321 | TIMER_END for the previous stage already. |
4322 | */ |
4323 | if (timer_value == 0) |
4324 | timer_value= get_timer_raw_value(stage_timer); |
4325 | pfs->m_timer_start= timer_value; |
4326 | } |
4327 | else |
4328 | pfs->m_timer_start= 0; |
4329 | pfs->m_timer_end= 0; |
4330 | |
4331 | if (flag_events_stages_current) |
4332 | { |
4333 | /* m_thread_internal_id is immutable and already set */ |
4334 | DBUG_ASSERT(pfs->m_thread_internal_id == pfs_thread->m_thread_internal_id); |
4335 | pfs->m_event_id= pfs_thread->m_event_id++; |
4336 | pfs->m_end_event_id= 0; |
4337 | pfs->m_source_file= src_file; |
4338 | pfs->m_source_line= src_line; |
4339 | |
4340 | /* New wait events will have this new stage as parent. */ |
4341 | child_wait->m_event_id= pfs->m_event_id; |
4342 | child_wait->m_event_type= EVENT_TYPE_STAGE; |
4343 | } |
4344 | } |
4345 | |
4346 | static void end_stage_v1() |
4347 | { |
4348 | ulonglong timer_value= 0; |
4349 | |
4350 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
4351 | if (unlikely(pfs_thread == NULL)) |
4352 | return; |
4353 | |
4354 | pfs_thread->m_stage= 0; |
4355 | |
4356 | if (psi_unlikely(! flag_global_instrumentation)) |
4357 | return; |
4358 | |
4359 | if (flag_thread_instrumentation && ! pfs_thread->m_enabled) |
4360 | return; |
4361 | |
4362 | PFS_events_stages *pfs= & pfs_thread->m_stage_current; |
4363 | |
4364 | PFS_instr_class *old_class= pfs->m_class; |
4365 | if (old_class != NULL) |
4366 | { |
4367 | PFS_stage_stat *event_name_array; |
4368 | event_name_array= pfs_thread->m_instr_class_stages_stats; |
4369 | uint index= old_class->m_event_name_index; |
4370 | |
4371 | /* Finish old event */ |
4372 | if (old_class->m_timed) |
4373 | { |
4374 | timer_value= get_timer_raw_value(stage_timer);; |
4375 | pfs->m_timer_end= timer_value; |
4376 | |
4377 | /* Aggregate to EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ |
4378 | ulonglong stage_time= timer_value - pfs->m_timer_start; |
4379 | event_name_array[index].aggregate_value(stage_time); |
4380 | } |
4381 | else |
4382 | { |
4383 | /* Aggregate to EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ |
4384 | event_name_array[index].aggregate_counted(); |
4385 | } |
4386 | |
4387 | if (flag_events_stages_current) |
4388 | { |
4389 | pfs->m_end_event_id= pfs_thread->m_event_id; |
4390 | if (flag_events_stages_history) |
4391 | insert_events_stages_history(pfs_thread, pfs); |
4392 | if (flag_events_stages_history_long) |
4393 | insert_events_stages_history_long(pfs); |
4394 | } |
4395 | |
4396 | /* New waits will now be attached directly to the parent statement. */ |
4397 | PFS_events_waits *child_wait= & pfs_thread->m_events_waits_stack[0]; |
4398 | PFS_events_statements *parent_statement= & pfs_thread->m_statement_stack[0]; |
4399 | child_wait->m_event_id= parent_statement->m_event_id; |
4400 | child_wait->m_event_type= parent_statement->m_event_type; |
4401 | |
4402 | /* This stage is completed */ |
4403 | pfs->m_class= NULL; |
4404 | } |
4405 | } |
4406 | |
4407 | static PSI_statement_locker* |
4408 | get_thread_statement_locker_v1(PSI_statement_locker_state *state, |
4409 | PSI_statement_key key, |
4410 | const void *charset) |
4411 | { |
4412 | DBUG_ASSERT(state != NULL); |
4413 | DBUG_ASSERT(charset != NULL); |
4414 | |
4415 | if (psi_unlikely(! flag_global_instrumentation)) |
4416 | return NULL; |
4417 | PFS_statement_class *klass= find_statement_class(key); |
4418 | if (unlikely(klass == NULL)) |
4419 | return NULL; |
4420 | if (! klass->m_enabled) |
4421 | return NULL; |
4422 | |
4423 | uint flags; |
4424 | |
4425 | if (flag_thread_instrumentation) |
4426 | { |
4427 | PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
4428 | if (unlikely(pfs_thread == NULL)) |
4429 | return NULL; |
4430 | if (! pfs_thread->m_enabled) |
4431 | return NULL; |
4432 | state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); |
4433 | flags= STATE_FLAG_THREAD; |
4434 | |
4435 | if (klass->m_timed) |
4436 | flags|= STATE_FLAG_TIMED; |
4437 | |
4438 | if (flag_events_statements_current) |
4439 | { |
4440 | ulonglong event_id= pfs_thread->m_event_id++; |
4441 | |
4442 | if (pfs_thread->m_events_statements_count >= statement_stack_max) |
4443 | { |
4444 | return NULL; |
4445 | } |
4446 | |
4447 | pfs_thread->m_stmt_lock.allocated_to_dirty(); |
4448 | PFS_events_statements *pfs= & pfs_thread->m_statement_stack[pfs_thread->m_events_statements_count]; |
4449 | /* m_thread_internal_id is immutable and already set */ |
4450 | DBUG_ASSERT(pfs->m_thread_internal_id == pfs_thread->m_thread_internal_id); |
4451 | pfs->m_event_id= event_id; |
4452 | pfs->m_end_event_id= 0; |
4453 | pfs->m_class= klass; |
4454 | pfs->m_timer_start= 0; |
4455 | pfs->m_timer_end= 0; |
4456 | pfs->m_lock_time= 0; |
4457 | pfs->m_current_schema_name_length= 0; |
4458 | pfs->m_sqltext_length= 0; |
4459 | pfs->m_sqltext_truncated= false; |
4460 | pfs->m_sqltext_cs_number= system_charset_info->number; /* default */ |
4461 | |
4462 | pfs->m_message_text[0]= '\0'; |
4463 | pfs->m_sql_errno= 0; |
4464 | pfs->m_sqlstate[0]= '\0'; |
4465 | pfs->m_error_count= 0; |
4466 | pfs->m_warning_count= 0; |
4467 | pfs->m_rows_affected= 0; |
4468 | |
4469 | pfs->m_rows_sent= 0; |
4470 | pfs->m_rows_examined= 0; |
4471 | pfs->m_created_tmp_disk_tables= 0; |
4472 | pfs->m_created_tmp_tables= 0; |
4473 | pfs->m_select_full_join= 0; |
4474 | pfs->m_select_full_range_join= 0; |
4475 | pfs->m_select_range= 0; |
4476 | pfs->m_select_range_check= 0; |
4477 | pfs->m_select_scan= 0; |
4478 | pfs->m_sort_merge_passes= 0; |
4479 | pfs->m_sort_range= 0; |
4480 | pfs->m_sort_rows= 0; |
4481 | pfs->m_sort_scan= 0; |
4482 | pfs->m_no_index_used= 0; |
4483 | pfs->m_no_good_index_used= 0; |
4484 | pfs->m_digest_storage.reset(); |
4485 | |
4486 | /* New stages will have this statement as parent */ |
4487 | PFS_events_stages *child_stage= & pfs_thread->m_stage_current; |
4488 | child_stage->m_nesting_event_id= event_id; |
4489 | child_stage->m_nesting_event_type= EVENT_TYPE_STATEMENT; |
4490 | |
4491 | /* New waits will have this statement as parent, if no stage is instrumented */ |
4492 | PFS_events_waits *child_wait= & pfs_thread->m_events_waits_stack[0]; |
4493 | child_wait->m_nesting_event_id= event_id; |
4494 | child_wait->m_nesting_event_type= EVENT_TYPE_STATEMENT; |
4495 | |
4496 | state->m_statement= pfs; |
4497 | flags|= STATE_FLAG_EVENT; |
4498 | |
4499 | pfs_thread->m_events_statements_count++; |
4500 | pfs_thread->m_stmt_lock.dirty_to_allocated(); |
4501 | } |
4502 | } |
4503 | else |
4504 | { |
4505 | if (klass->m_timed) |
4506 | flags= STATE_FLAG_TIMED; |
4507 | else |
4508 | flags= 0; |
4509 | } |
4510 | |
4511 | if (flag_statements_digest) |
4512 | { |
4513 | flags|= STATE_FLAG_DIGEST; |
4514 | } |
4515 | |
4516 | state->m_discarded= false; |
4517 | state->m_class= klass; |
4518 | state->m_flags= flags; |
4519 | |
4520 | state->m_lock_time= 0; |
4521 | state->m_rows_sent= 0; |
4522 | state->m_rows_examined= 0; |
4523 | state->m_created_tmp_disk_tables= 0; |
4524 | state->m_created_tmp_tables= 0; |
4525 | state->m_select_full_join= 0; |
4526 | state->m_select_full_range_join= 0; |
4527 | state->m_select_range= 0; |
4528 | state->m_select_range_check= 0; |
4529 | state->m_select_scan= 0; |
4530 | state->m_sort_merge_passes= 0; |
4531 | state->m_sort_range= 0; |
4532 | state->m_sort_rows= 0; |
4533 | state->m_sort_scan= 0; |
4534 | state->m_no_index_used= 0; |
4535 | state->m_no_good_index_used= 0; |
4536 | |
4537 | state->m_digest= NULL; |
4538 | |
4539 | state->m_schema_name_length= 0; |
4540 | state->m_cs_number= ((CHARSET_INFO *)charset)->number; |
4541 | |
4542 | return reinterpret_cast<PSI_statement_locker*> (state); |
4543 | } |
4544 | |
4545 | static PSI_statement_locker* |
4546 | refine_statement_v1(PSI_statement_locker *locker, |
4547 | PSI_statement_key key) |
4548 | { |
4549 | PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker); |
4550 | if (state == NULL) |
4551 | return NULL; |
4552 | DBUG_ASSERT(state->m_class != NULL); |
4553 | PFS_statement_class *klass; |
4554 | /* Only refine statements for mutable instrumentation */ |
4555 | klass= reinterpret_cast<PFS_statement_class*> (state->m_class); |
4556 | DBUG_ASSERT(klass->is_mutable()); |
4557 | klass= find_statement_class(key); |
4558 | |
4559 | uint flags= state->m_flags; |
4560 | |
4561 | if (unlikely(klass == NULL) || !klass->m_enabled) |
4562 | { |
4563 | /* pop statement stack */ |
4564 | if (flags & STATE_FLAG_THREAD) |
4565 | { |
4566 | PFS_thread *pfs_thread= reinterpret_cast<PFS_thread *> (state->m_thread); |
4567 | DBUG_ASSERT(pfs_thread != NULL); |
4568 | if (pfs_thread->m_events_statements_count > 0) |
4569 | pfs_thread->m_events_statements_count--; |
4570 | } |
4571 | |
4572 | state->m_discarded= true; |
4573 | return NULL; |
4574 | } |
4575 | |
4576 | if ((flags & STATE_FLAG_TIMED) && ! klass->m_timed) |
4577 | flags= flags & ~STATE_FLAG_TIMED; |
4578 | |
4579 | if (flags & STATE_FLAG_EVENT) |
4580 | { |
4581 | PFS_events_statements *pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement); |
4582 | DBUG_ASSERT(pfs != NULL); |
4583 | |
4584 | /* mutate EVENTS_STATEMENTS_CURRENT.EVENT_NAME */ |
4585 | pfs->m_class= klass; |
4586 | } |
4587 | |
4588 | state->m_class= klass; |
4589 | state->m_flags= flags; |
4590 | return reinterpret_cast<PSI_statement_locker*> (state); |
4591 | } |
4592 | |
4593 | static void start_statement_v1(PSI_statement_locker *locker, |
4594 | const char *db, uint db_len, |
4595 | const char *src_file, uint src_line) |
4596 | { |
4597 | PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker); |
4598 | DBUG_ASSERT(state != NULL); |
4599 | |
4600 | uint flags= state->m_flags; |
4601 | ulonglong timer_start= 0; |
4602 | |
4603 | if (flags & STATE_FLAG_TIMED) |
4604 | { |
4605 | timer_start= get_timer_raw_value_and_function(statement_timer, & state->m_timer); |
4606 | state->m_timer_start= timer_start; |
4607 | } |
4608 | |
4609 | compile_time_assert(PSI_SCHEMA_NAME_LEN == NAME_LEN); |
4610 | DBUG_ASSERT(db_len <= sizeof(state->m_schema_name)); |
4611 | |
4612 | if (db_len > 0) |
4613 | memcpy(state->m_schema_name, db, db_len); |
4614 | state->m_schema_name_length= db_len; |
4615 | |
4616 | if (flags & STATE_FLAG_EVENT) |
4617 | { |
4618 | PFS_events_statements *pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement); |
4619 | DBUG_ASSERT(pfs != NULL); |
4620 | |
4621 | pfs->m_timer_start= timer_start; |
4622 | pfs->m_source_file= src_file; |
4623 | pfs->m_source_line= src_line; |
4624 | |
4625 | DBUG_ASSERT(db_len <= sizeof(pfs->m_current_schema_name)); |
4626 | if (db_len > 0) |
4627 | memcpy(pfs->m_current_schema_name, db, db_len); |
4628 | pfs->m_current_schema_name_length= db_len; |
4629 | } |
4630 | } |
4631 | |
4632 | static void set_statement_text_v1(PSI_statement_locker *locker, |
4633 | const char *text, uint text_len) |
4634 | { |
4635 | PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker); |
4636 | DBUG_ASSERT(state != NULL); |
4637 | |
4638 | if (state->m_discarded) |
4639 | return; |
4640 | |
4641 | if (state->m_flags & STATE_FLAG_EVENT) |
4642 | { |
4643 | PFS_events_statements *pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement); |
4644 | DBUG_ASSERT(pfs != NULL); |
4645 | if (text_len > sizeof (pfs->m_sqltext)) |
4646 | { |
4647 | text_len= sizeof(pfs->m_sqltext); |
4648 | pfs->m_sqltext_truncated= true; |
4649 | } |
4650 | if (text_len) |
4651 | memcpy(pfs->m_sqltext, text, text_len); |
4652 | pfs->m_sqltext_length= text_len; |
4653 | pfs->m_sqltext_cs_number= state->m_cs_number; |
4654 | } |
4655 | |
4656 | return; |
4657 | } |
4658 | |
4659 | #define SET_STATEMENT_ATTR_BODY(LOCKER, ATTR, VALUE) \ |
4660 | PSI_statement_locker_state *state; \ |
4661 | state= reinterpret_cast<PSI_statement_locker_state*> (LOCKER); \ |
4662 | if (unlikely(state == NULL)) \ |
4663 | return; \ |
4664 | if (state->m_discarded) \ |
4665 | return; \ |
4666 | state->ATTR= VALUE; \ |
4667 | if (state->m_flags & STATE_FLAG_EVENT) \ |
4668 | { \ |
4669 | PFS_events_statements *pfs; \ |
4670 | pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement); \ |
4671 | DBUG_ASSERT(pfs != NULL); \ |
4672 | pfs->ATTR= VALUE; \ |
4673 | } \ |
4674 | return; |
4675 | |
4676 | #define INC_STATEMENT_ATTR_BODY(LOCKER, ATTR, VALUE) \ |
4677 | PSI_statement_locker_state *state; \ |
4678 | state= reinterpret_cast<PSI_statement_locker_state*> (LOCKER); \ |
4679 | if (unlikely(state == NULL)) \ |
4680 | return; \ |
4681 | if (state->m_discarded) \ |
4682 | return; \ |
4683 | state->ATTR+= VALUE; \ |
4684 | if (state->m_flags & STATE_FLAG_EVENT) \ |
4685 | { \ |
4686 | PFS_events_statements *pfs; \ |
4687 | pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement); \ |
4688 | DBUG_ASSERT(pfs != NULL); \ |
4689 | pfs->ATTR+= VALUE; \ |
4690 | } \ |
4691 | return; |
4692 | |
4693 | static void set_statement_lock_time_v1(PSI_statement_locker *locker, |
4694 | ulonglong count) |
4695 | { |
4696 | SET_STATEMENT_ATTR_BODY(locker, m_lock_time, count); |
4697 | } |
4698 | |
4699 | static void set_statement_rows_sent_v1(PSI_statement_locker *locker, |
4700 | ulonglong count) |
4701 | { |
4702 | SET_STATEMENT_ATTR_BODY(locker, m_rows_sent, count); |
4703 | } |
4704 | |
4705 | static void set_statement_rows_examined_v1(PSI_statement_locker *locker, |
4706 | ulonglong count) |
4707 | { |
4708 | SET_STATEMENT_ATTR_BODY(locker, m_rows_examined, count); |
4709 | } |
4710 | |
4711 | static void inc_statement_created_tmp_disk_tables_v1(PSI_statement_locker *locker, |
4712 | ulong count) |
4713 | { |
4714 | INC_STATEMENT_ATTR_BODY(locker, m_created_tmp_disk_tables, count); |
4715 | } |
4716 | |
4717 | static void inc_statement_created_tmp_tables_v1(PSI_statement_locker *locker, |
4718 | ulong count) |
4719 | { |
4720 | INC_STATEMENT_ATTR_BODY(locker, m_created_tmp_tables, count); |
4721 | } |
4722 | |
4723 | static void inc_statement_select_full_join_v1(PSI_statement_locker *locker, |
4724 | ulong count) |
4725 | { |
4726 | INC_STATEMENT_ATTR_BODY(locker, m_select_full_join, count); |
4727 | } |
4728 | |
4729 | static void inc_statement_select_full_range_join_v1(PSI_statement_locker *locker, |
4730 | ulong count) |
4731 | { |
4732 | INC_STATEMENT_ATTR_BODY(locker, m_select_full_range_join, count); |
4733 | } |
4734 | |
4735 | static void inc_statement_select_range_v1(PSI_statement_locker *locker, |
4736 | ulong count) |
4737 | { |
4738 | INC_STATEMENT_ATTR_BODY(locker, m_select_range, count); |
4739 | } |
4740 | |
4741 | static void inc_statement_select_range_check_v1(PSI_statement_locker *locker, |
4742 | ulong count) |
4743 | { |
4744 | INC_STATEMENT_ATTR_BODY(locker, m_select_range_check, count); |
4745 | } |
4746 | |
4747 | static void inc_statement_select_scan_v1(PSI_statement_locker *locker, |
4748 | ulong count) |
4749 | { |
4750 | INC_STATEMENT_ATTR_BODY(locker, m_select_scan, count); |
4751 | } |
4752 | |
4753 | static void inc_statement_sort_merge_passes_v1(PSI_statement_locker *locker, |
4754 | ulong count) |
4755 | { |
4756 | INC_STATEMENT_ATTR_BODY(locker, m_sort_merge_passes, count); |
4757 | } |
4758 | |
4759 | static void inc_statement_sort_range_v1(PSI_statement_locker *locker, |
4760 | ulong count) |
4761 | { |
4762 | INC_STATEMENT_ATTR_BODY(locker, m_sort_range, count); |
4763 | } |
4764 | |
4765 | static void inc_statement_sort_rows_v1(PSI_statement_locker *locker, |
4766 | ulong count) |
4767 | { |
4768 | INC_STATEMENT_ATTR_BODY(locker, m_sort_rows, count); |
4769 | } |
4770 | |
4771 | static void inc_statement_sort_scan_v1(PSI_statement_locker *locker, |
4772 | ulong count) |
4773 | { |
4774 | INC_STATEMENT_ATTR_BODY(locker, m_sort_scan, count); |
4775 | } |
4776 | |
4777 | static void set_statement_no_index_used_v1(PSI_statement_locker *locker) |
4778 | { |
4779 | SET_STATEMENT_ATTR_BODY(locker, m_no_index_used, 1); |
4780 | } |
4781 | |
4782 | static void set_statement_no_good_index_used_v1(PSI_statement_locker *locker) |
4783 | { |
4784 | SET_STATEMENT_ATTR_BODY(locker, m_no_good_index_used, 1); |
4785 | } |
4786 | |
4787 | static void end_statement_v1(PSI_statement_locker *locker, void *stmt_da) |
4788 | { |
4789 | PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker); |
4790 | Diagnostics_area *da= reinterpret_cast<Diagnostics_area*> (stmt_da); |
4791 | DBUG_ASSERT(state != NULL); |
4792 | DBUG_ASSERT(da != NULL); |
4793 | |
4794 | if (state->m_discarded) |
4795 | return; |
4796 | |
4797 | PFS_statement_class *klass= reinterpret_cast<PFS_statement_class *> (state->m_class); |
4798 | DBUG_ASSERT(klass != NULL); |
4799 | |
4800 | ulonglong timer_end= 0; |
4801 | ulonglong wait_time= 0; |
4802 | uint flags= state->m_flags; |
4803 | |
4804 | if (flags & STATE_FLAG_TIMED) |
4805 | { |
4806 | timer_end= state->m_timer(); |
4807 | wait_time= timer_end - state->m_timer_start; |
4808 | } |
4809 | |
4810 | PFS_statement_stat *event_name_array; |
4811 | uint index= klass->m_event_name_index; |
4812 | PFS_statement_stat *stat; |
4813 | |
4814 | /* |
4815 | Capture statement stats by digest. |
4816 | */ |
4817 | const sql_digest_storage *digest_storage= NULL; |
4818 | PFS_statement_stat *digest_stat= NULL; |
4819 | |
4820 | if (flags & STATE_FLAG_THREAD) |
4821 | { |
4822 | PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); |
4823 | DBUG_ASSERT(thread != NULL); |
4824 | event_name_array= thread->m_instr_class_statements_stats; |
4825 | /* Aggregate to EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME */ |
4826 | stat= & event_name_array[index]; |
4827 | |
4828 | if (flags & STATE_FLAG_DIGEST) |
4829 | { |
4830 | digest_storage= state->m_digest; |
4831 | |
4832 | if (digest_storage != NULL) |
4833 | { |
4834 | /* Populate PFS_statements_digest_stat with computed digest information.*/ |
4835 | digest_stat= find_or_create_digest(thread, digest_storage, |
4836 | state->m_schema_name, |
4837 | state->m_schema_name_length); |
4838 | } |
4839 | } |
4840 | |
4841 | if (flags & STATE_FLAG_EVENT) |
4842 | { |
4843 | PFS_events_statements *pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement); |
4844 | DBUG_ASSERT(pfs != NULL); |
4845 | |
4846 | thread->m_stmt_lock.allocated_to_dirty(); |
4847 | |
4848 | switch(da->status()) |
4849 | { |
4850 | case Diagnostics_area::DA_OK_BULK: |
4851 | case Diagnostics_area::DA_EMPTY: |
4852 | break; |
4853 | case Diagnostics_area::DA_OK: |
4854 | memcpy(pfs->m_message_text, da->message(), MYSQL_ERRMSG_SIZE); |
4855 | pfs->m_message_text[MYSQL_ERRMSG_SIZE]= 0; |
4856 | pfs->m_rows_affected= da->affected_rows(); |
4857 | pfs->m_warning_count= da->statement_warn_count(); |
4858 | memcpy(pfs->m_sqlstate, "00000" , SQLSTATE_LENGTH); |
4859 | break; |
4860 | case Diagnostics_area::DA_EOF: |
4861 | pfs->m_warning_count= da->statement_warn_count(); |
4862 | break; |
4863 | case Diagnostics_area::DA_ERROR: |
4864 | memcpy(pfs->m_message_text, da->message(), MYSQL_ERRMSG_SIZE); |
4865 | pfs->m_message_text[MYSQL_ERRMSG_SIZE]= 0; |
4866 | pfs->m_sql_errno= da->sql_errno(); |
4867 | pfs->m_error_count++; |
4868 | memcpy(pfs->m_sqlstate, da->get_sqlstate(), SQLSTATE_LENGTH); |
4869 | break; |
4870 | case Diagnostics_area::DA_DISABLED: |
4871 | break; |
4872 | } |
4873 | |
4874 | pfs->m_timer_end= timer_end; |
4875 | pfs->m_end_event_id= thread->m_event_id; |
4876 | |
4877 | if (digest_storage != NULL) |
4878 | { |
4879 | /* |
4880 | The following columns in events_statement_current: |
4881 | - DIGEST, |
4882 | - DIGEST_TEXT |
4883 | are computed from the digest storage. |
4884 | */ |
4885 | pfs->m_digest_storage.copy(digest_storage); |
4886 | } |
4887 | |
4888 | if (flag_events_statements_history) |
4889 | insert_events_statements_history(thread, pfs); |
4890 | if (flag_events_statements_history_long) |
4891 | insert_events_statements_history_long(pfs); |
4892 | |
4893 | DBUG_ASSERT(thread->m_events_statements_count > 0); |
4894 | thread->m_events_statements_count--; |
4895 | thread->m_stmt_lock.dirty_to_allocated(); |
4896 | } |
4897 | } |
4898 | else |
4899 | { |
4900 | if (flags & STATE_FLAG_DIGEST) |
4901 | { |
4902 | PFS_thread *thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
4903 | |
4904 | /* An instrumented thread is required, for LF_PINS. */ |
4905 | if (thread != NULL) |
4906 | { |
4907 | /* Set digest stat. */ |
4908 | digest_storage= state->m_digest; |
4909 | |
4910 | if (digest_storage != NULL) |
4911 | { |
4912 | /* Populate statements_digest_stat with computed digest information. */ |
4913 | digest_stat= find_or_create_digest(thread, digest_storage, |
4914 | state->m_schema_name, |
4915 | state->m_schema_name_length); |
4916 | } |
4917 | } |
4918 | } |
4919 | |
4920 | event_name_array= global_instr_class_statements_array; |
4921 | /* Aggregate to EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME */ |
4922 | stat= & event_name_array[index]; |
4923 | } |
4924 | |
4925 | if (flags & STATE_FLAG_TIMED) |
4926 | { |
4927 | /* Aggregate to EVENTS_STATEMENTS_SUMMARY_..._BY_EVENT_NAME (timed) */ |
4928 | stat->aggregate_value(wait_time); |
4929 | } |
4930 | else |
4931 | { |
4932 | /* Aggregate to EVENTS_STATEMENTS_SUMMARY_..._BY_EVENT_NAME (counted) */ |
4933 | stat->aggregate_counted(); |
4934 | } |
4935 | |
4936 | stat->m_lock_time+= state->m_lock_time; |
4937 | stat->m_rows_sent+= state->m_rows_sent; |
4938 | stat->m_rows_examined+= state->m_rows_examined; |
4939 | stat->m_created_tmp_disk_tables+= state->m_created_tmp_disk_tables; |
4940 | stat->m_created_tmp_tables+= state->m_created_tmp_tables; |
4941 | stat->m_select_full_join+= state->m_select_full_join; |
4942 | stat->m_select_full_range_join+= state->m_select_full_range_join; |
4943 | stat->m_select_range+= state->m_select_range; |
4944 | stat->m_select_range_check+= state->m_select_range_check; |
4945 | stat->m_select_scan+= state->m_select_scan; |
4946 | stat->m_sort_merge_passes+= state->m_sort_merge_passes; |
4947 | stat->m_sort_range+= state->m_sort_range; |
4948 | stat->m_sort_rows+= state->m_sort_rows; |
4949 | stat->m_sort_scan+= state->m_sort_scan; |
4950 | stat->m_no_index_used+= state->m_no_index_used; |
4951 | stat->m_no_good_index_used+= state->m_no_good_index_used; |
4952 | |
4953 | if (digest_stat != NULL) |
4954 | { |
4955 | if (flags & STATE_FLAG_TIMED) |
4956 | { |
4957 | digest_stat->aggregate_value(wait_time); |
4958 | } |
4959 | else |
4960 | { |
4961 | digest_stat->aggregate_counted(); |
4962 | } |
4963 | |
4964 | digest_stat->m_lock_time+= state->m_lock_time; |
4965 | digest_stat->m_rows_sent+= state->m_rows_sent; |
4966 | digest_stat->m_rows_examined+= state->m_rows_examined; |
4967 | digest_stat->m_created_tmp_disk_tables+= state->m_created_tmp_disk_tables; |
4968 | digest_stat->m_created_tmp_tables+= state->m_created_tmp_tables; |
4969 | digest_stat->m_select_full_join+= state->m_select_full_join; |
4970 | digest_stat->m_select_full_range_join+= state->m_select_full_range_join; |
4971 | digest_stat->m_select_range+= state->m_select_range; |
4972 | digest_stat->m_select_range_check+= state->m_select_range_check; |
4973 | digest_stat->m_select_scan+= state->m_select_scan; |
4974 | digest_stat->m_sort_merge_passes+= state->m_sort_merge_passes; |
4975 | digest_stat->m_sort_range+= state->m_sort_range; |
4976 | digest_stat->m_sort_rows+= state->m_sort_rows; |
4977 | digest_stat->m_sort_scan+= state->m_sort_scan; |
4978 | digest_stat->m_no_index_used+= state->m_no_index_used; |
4979 | digest_stat->m_no_good_index_used+= state->m_no_good_index_used; |
4980 | } |
4981 | |
4982 | switch (da->status()) |
4983 | { |
4984 | case Diagnostics_area::DA_OK_BULK: |
4985 | case Diagnostics_area::DA_EMPTY: |
4986 | break; |
4987 | case Diagnostics_area::DA_OK: |
4988 | stat->m_rows_affected+= da->affected_rows(); |
4989 | stat->m_warning_count+= da->statement_warn_count(); |
4990 | if (digest_stat != NULL) |
4991 | { |
4992 | digest_stat->m_rows_affected+= da->affected_rows(); |
4993 | digest_stat->m_warning_count+= da->statement_warn_count(); |
4994 | } |
4995 | break; |
4996 | case Diagnostics_area::DA_EOF: |
4997 | stat->m_warning_count+= da->statement_warn_count(); |
4998 | if (digest_stat != NULL) |
4999 | { |
5000 | digest_stat->m_warning_count+= da->statement_warn_count(); |
5001 | } |
5002 | break; |
5003 | case Diagnostics_area::DA_ERROR: |
5004 | stat->m_error_count++; |
5005 | if (digest_stat != NULL) |
5006 | { |
5007 | digest_stat->m_error_count++; |
5008 | } |
5009 | break; |
5010 | case Diagnostics_area::DA_DISABLED: |
5011 | break; |
5012 | } |
5013 | } |
5014 | |
5015 | /** |
5016 | Implementation of the socket instrumentation interface. |
5017 | @sa PSI_v1::end_socket_wait. |
5018 | */ |
5019 | static void end_socket_wait_v1(PSI_socket_locker *locker, size_t byte_count) |
5020 | { |
5021 | PSI_socket_locker_state *state= reinterpret_cast<PSI_socket_locker_state*> (locker); |
5022 | DBUG_ASSERT(state != NULL); |
5023 | |
5024 | PFS_socket *socket= reinterpret_cast<PFS_socket *>(state->m_socket); |
5025 | DBUG_ASSERT(socket != NULL); |
5026 | |
5027 | ulonglong timer_end= 0; |
5028 | ulonglong wait_time= 0; |
5029 | PFS_byte_stat *byte_stat; |
5030 | uint flags= state->m_flags; |
5031 | size_t bytes= ((int)byte_count > -1 ? byte_count : 0); |
5032 | |
5033 | switch (state->m_operation) |
5034 | { |
5035 | /* Group read operations */ |
5036 | case PSI_SOCKET_RECV: |
5037 | case PSI_SOCKET_RECVFROM: |
5038 | case PSI_SOCKET_RECVMSG: |
5039 | byte_stat= &socket->m_socket_stat.m_io_stat.m_read; |
5040 | break; |
5041 | /* Group write operations */ |
5042 | case PSI_SOCKET_SEND: |
5043 | case PSI_SOCKET_SENDTO: |
5044 | case PSI_SOCKET_SENDMSG: |
5045 | byte_stat= &socket->m_socket_stat.m_io_stat.m_write; |
5046 | break; |
5047 | /* Group remaining operations as miscellaneous */ |
5048 | case PSI_SOCKET_CONNECT: |
5049 | case PSI_SOCKET_CREATE: |
5050 | case PSI_SOCKET_BIND: |
5051 | case PSI_SOCKET_SEEK: |
5052 | case PSI_SOCKET_OPT: |
5053 | case PSI_SOCKET_STAT: |
5054 | case PSI_SOCKET_SHUTDOWN: |
5055 | case PSI_SOCKET_SELECT: |
5056 | case PSI_SOCKET_CLOSE: |
5057 | byte_stat= &socket->m_socket_stat.m_io_stat.m_misc; |
5058 | break; |
5059 | default: |
5060 | DBUG_ASSERT(false); |
5061 | byte_stat= NULL; |
5062 | break; |
5063 | } |
5064 | |
5065 | /* Aggregation for EVENTS_WAITS_SUMMARY_BY_INSTANCE */ |
5066 | if (flags & STATE_FLAG_TIMED) |
5067 | { |
5068 | timer_end= state->m_timer(); |
5069 | wait_time= timer_end - state->m_timer_start; |
5070 | |
5071 | /* Aggregate to the socket instrument for now (timed) */ |
5072 | byte_stat->aggregate(wait_time, bytes); |
5073 | } |
5074 | else |
5075 | { |
5076 | /* Aggregate to the socket instrument (event count and byte count) */ |
5077 | byte_stat->aggregate_counted(bytes); |
5078 | } |
5079 | |
5080 | /* Aggregate to EVENTS_WAITS_HISTORY and EVENTS_WAITS_HISTORY_LONG */ |
5081 | if (flags & STATE_FLAG_EVENT) |
5082 | { |
5083 | PFS_thread *thread= reinterpret_cast<PFS_thread *>(state->m_thread); |
5084 | DBUG_ASSERT(thread != NULL); |
5085 | PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); |
5086 | DBUG_ASSERT(wait != NULL); |
5087 | |
5088 | wait->m_timer_end= timer_end; |
5089 | wait->m_end_event_id= thread->m_event_id; |
5090 | wait->m_number_of_bytes= bytes; |
5091 | |
5092 | if (flag_events_waits_history) |
5093 | insert_events_waits_history(thread, wait); |
5094 | if (flag_events_waits_history_long) |
5095 | insert_events_waits_history_long(wait); |
5096 | thread->m_events_waits_current--; |
5097 | |
5098 | DBUG_ASSERT(wait == thread->m_events_waits_current); |
5099 | } |
5100 | } |
5101 | |
5102 | static void set_socket_state_v1(PSI_socket *socket, PSI_socket_state state) |
5103 | { |
5104 | DBUG_ASSERT((state == PSI_SOCKET_STATE_IDLE) || (state == PSI_SOCKET_STATE_ACTIVE)); |
5105 | PFS_socket *pfs= reinterpret_cast<PFS_socket*>(socket); |
5106 | DBUG_ASSERT(pfs != NULL); |
5107 | DBUG_ASSERT(pfs->m_idle || (state == PSI_SOCKET_STATE_IDLE)); |
5108 | DBUG_ASSERT(!pfs->m_idle || (state == PSI_SOCKET_STATE_ACTIVE)); |
5109 | pfs->m_idle= (state == PSI_SOCKET_STATE_IDLE); |
5110 | } |
5111 | |
5112 | /** |
5113 | Set socket descriptor and address info. |
5114 | */ |
5115 | static void set_socket_info_v1(PSI_socket *socket, |
5116 | const my_socket *fd, |
5117 | const struct sockaddr *addr, |
5118 | socklen_t addr_len) |
5119 | { |
5120 | PFS_socket *pfs= reinterpret_cast<PFS_socket*>(socket); |
5121 | DBUG_ASSERT(pfs != NULL); |
5122 | |
5123 | /** Set socket descriptor */ |
5124 | if (fd != NULL) |
5125 | pfs->m_fd= (uint)*fd; |
5126 | |
5127 | /** Set raw socket address and length */ |
5128 | if (likely(addr != NULL && addr_len > 0)) |
5129 | { |
5130 | pfs->m_addr_len= addr_len; |
5131 | |
5132 | /** Restrict address length to size of struct */ |
5133 | if (unlikely(pfs->m_addr_len > sizeof(sockaddr_storage))) |
5134 | pfs->m_addr_len= sizeof(struct sockaddr_storage); |
5135 | |
5136 | memcpy(&pfs->m_sock_addr, addr, pfs->m_addr_len); |
5137 | } |
5138 | } |
5139 | |
5140 | /** |
5141 | Implementation of the socket instrumentation interface. |
5142 | @sa PSI_v1::set_socket_info. |
5143 | */ |
5144 | static void set_socket_thread_owner_v1(PSI_socket *socket) |
5145 | { |
5146 | PFS_socket *pfs_socket= reinterpret_cast<PFS_socket*>(socket); |
5147 | DBUG_ASSERT(pfs_socket != NULL); |
5148 | pfs_socket->m_thread_owner= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
5149 | } |
5150 | |
5151 | struct PSI_digest_locker* |
5152 | pfs_digest_start_v1(PSI_statement_locker *locker) |
5153 | { |
5154 | PSI_statement_locker_state *statement_state; |
5155 | statement_state= reinterpret_cast<PSI_statement_locker_state*> (locker); |
5156 | DBUG_ASSERT(statement_state != NULL); |
5157 | |
5158 | if (statement_state->m_discarded) |
5159 | return NULL; |
5160 | |
5161 | if (statement_state->m_flags & STATE_FLAG_DIGEST) |
5162 | { |
5163 | return reinterpret_cast<PSI_digest_locker*> (locker); |
5164 | } |
5165 | |
5166 | return NULL; |
5167 | } |
5168 | |
5169 | void pfs_digest_end_v1(PSI_digest_locker *locker, const sql_digest_storage *digest) |
5170 | { |
5171 | PSI_statement_locker_state *statement_state; |
5172 | statement_state= reinterpret_cast<PSI_statement_locker_state*> (locker); |
5173 | DBUG_ASSERT(statement_state != NULL); |
5174 | DBUG_ASSERT(digest != NULL); |
5175 | |
5176 | if (statement_state->m_discarded) |
5177 | return; |
5178 | |
5179 | if (statement_state->m_flags & STATE_FLAG_DIGEST) |
5180 | { |
5181 | statement_state->m_digest= digest; |
5182 | } |
5183 | } |
5184 | |
5185 | /** |
5186 | Implementation of the thread attribute connection interface |
5187 | @sa PSI_v1::set_thread_connect_attr. |
5188 | */ |
5189 | static int set_thread_connect_attrs_v1(const char *buffer, uint length, |
5190 | const void *from_cs) |
5191 | { |
5192 | |
5193 | PFS_thread *thd= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); |
5194 | |
5195 | DBUG_ASSERT(buffer != NULL); |
5196 | |
5197 | if (likely(thd != NULL) && session_connect_attrs_size_per_thread > 0) |
5198 | { |
5199 | const CHARSET_INFO *cs = static_cast<const CHARSET_INFO *> (from_cs); |
5200 | |
5201 | /* copy from the input buffer as much as we can fit */ |
5202 | uint copy_size= (uint)(length < session_connect_attrs_size_per_thread ? |
5203 | length : session_connect_attrs_size_per_thread); |
5204 | thd->m_session_lock.allocated_to_dirty(); |
5205 | memcpy(thd->m_session_connect_attrs, buffer, copy_size); |
5206 | thd->m_session_connect_attrs_length= copy_size; |
5207 | thd->m_session_connect_attrs_cs_number= cs->number; |
5208 | thd->m_session_lock.dirty_to_allocated(); |
5209 | |
5210 | if (copy_size == length) |
5211 | return 0; |
5212 | |
5213 | session_connect_attrs_lost++; |
5214 | return 1; |
5215 | } |
5216 | return 0; |
5217 | } |
5218 | |
5219 | |
5220 | /** |
5221 | Implementation of the instrumentation interface. |
5222 | @sa PSI_v1. |
5223 | */ |
5224 | PSI_v1 PFS_v1= |
5225 | { |
5226 | register_mutex_v1, |
5227 | register_rwlock_v1, |
5228 | register_cond_v1, |
5229 | register_thread_v1, |
5230 | register_file_v1, |
5231 | register_stage_v1, |
5232 | register_statement_v1, |
5233 | register_socket_v1, |
5234 | init_mutex_v1, |
5235 | destroy_mutex_v1, |
5236 | init_rwlock_v1, |
5237 | destroy_rwlock_v1, |
5238 | init_cond_v1, |
5239 | destroy_cond_v1, |
5240 | init_socket_v1, |
5241 | destroy_socket_v1, |
5242 | get_table_share_v1, |
5243 | release_table_share_v1, |
5244 | drop_table_share_v1, |
5245 | open_table_v1, |
5246 | unbind_table_v1, |
5247 | rebind_table_v1, |
5248 | close_table_v1, |
5249 | create_file_v1, |
5250 | spawn_thread_v1, |
5251 | new_thread_v1, |
5252 | set_thread_id_v1, |
5253 | get_thread_v1, |
5254 | set_thread_user_v1, |
5255 | set_thread_account_v1, |
5256 | set_thread_db_v1, |
5257 | set_thread_command_v1, |
5258 | set_thread_start_time_v1, |
5259 | set_thread_state_v1, |
5260 | set_thread_info_v1, |
5261 | set_thread_v1, |
5262 | delete_current_thread_v1, |
5263 | delete_thread_v1, |
5264 | get_thread_file_name_locker_v1, |
5265 | get_thread_file_stream_locker_v1, |
5266 | get_thread_file_descriptor_locker_v1, |
5267 | unlock_mutex_v1, |
5268 | unlock_rwlock_v1, |
5269 | signal_cond_v1, |
5270 | broadcast_cond_v1, |
5271 | start_idle_wait_v1, |
5272 | end_idle_wait_v1, |
5273 | start_mutex_wait_v1, |
5274 | end_mutex_wait_v1, |
5275 | start_rwlock_wait_v1, /* read */ |
5276 | end_rwlock_rdwait_v1, |
5277 | start_rwlock_wait_v1, /* write */ |
5278 | end_rwlock_wrwait_v1, |
5279 | start_cond_wait_v1, |
5280 | end_cond_wait_v1, |
5281 | start_table_io_wait_v1, |
5282 | end_table_io_wait_v1, |
5283 | start_table_lock_wait_v1, |
5284 | end_table_lock_wait_v1, |
5285 | start_file_open_wait_v1, |
5286 | end_file_open_wait_v1, |
5287 | end_file_open_wait_and_bind_to_descriptor_v1, |
5288 | start_file_wait_v1, |
5289 | end_file_wait_v1, |
5290 | start_file_close_wait_v1, |
5291 | end_file_close_wait_v1, |
5292 | start_stage_v1, |
5293 | end_stage_v1, |
5294 | get_thread_statement_locker_v1, |
5295 | refine_statement_v1, |
5296 | start_statement_v1, |
5297 | set_statement_text_v1, |
5298 | set_statement_lock_time_v1, |
5299 | set_statement_rows_sent_v1, |
5300 | set_statement_rows_examined_v1, |
5301 | inc_statement_created_tmp_disk_tables_v1, |
5302 | inc_statement_created_tmp_tables_v1, |
5303 | inc_statement_select_full_join_v1, |
5304 | inc_statement_select_full_range_join_v1, |
5305 | inc_statement_select_range_v1, |
5306 | inc_statement_select_range_check_v1, |
5307 | inc_statement_select_scan_v1, |
5308 | inc_statement_sort_merge_passes_v1, |
5309 | inc_statement_sort_range_v1, |
5310 | inc_statement_sort_rows_v1, |
5311 | inc_statement_sort_scan_v1, |
5312 | set_statement_no_index_used_v1, |
5313 | set_statement_no_good_index_used_v1, |
5314 | end_statement_v1, |
5315 | start_socket_wait_v1, |
5316 | end_socket_wait_v1, |
5317 | set_socket_state_v1, |
5318 | set_socket_info_v1, |
5319 | set_socket_thread_owner_v1, |
5320 | pfs_digest_start_v1, |
5321 | pfs_digest_end_v1, |
5322 | set_thread_connect_attrs_v1, |
5323 | }; |
5324 | |
5325 | static void* get_interface(int version) |
5326 | { |
5327 | switch (version) |
5328 | { |
5329 | case PSI_VERSION_1: |
5330 | return &PFS_v1; |
5331 | default: |
5332 | return NULL; |
5333 | } |
5334 | } |
5335 | |
5336 | C_MODE_END |
5337 | |
5338 | struct PSI_bootstrap PFS_bootstrap= |
5339 | { |
5340 | get_interface |
5341 | }; |
5342 | |