1 /* Copyright (c) 2008, 2012, 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_instr.cc
18 Performance schema instruments (implementation).
19 */
20
21 #include <my_global.h>
22 #include <string.h>
23
24 #include "my_sys.h"
25 #include "pfs.h"
26 #include "pfs_stat.h"
27 #include "pfs_instr.h"
28 #include "pfs_global.h"
29
30 /**
31 @addtogroup Performance_schema_buffers
32 @{
33 */
34
35 /** Size of the mutex instances array. @sa mutex_array */
36 ulong mutex_max;
37 /** Number of mutexes instance lost. @sa mutex_array */
38 ulong mutex_lost;
39 /** Size of the rwlock instances array. @sa rwlock_array */
40 ulong rwlock_max;
41 /** Number or rwlock instances lost. @sa rwlock_array */
42 ulong rwlock_lost;
43 /** Size of the conditions instances array. @sa cond_array */
44 ulong cond_max;
45 /** Number of conditions instances lost. @sa cond_array */
46 ulong cond_lost;
47 /** Size of the thread instances array. @sa thread_array */
48 ulong thread_max;
49 /** Number or thread instances lost. @sa thread_array */
50 ulong thread_lost;
51 /** Size of the file instances array. @sa file_array */
52 ulong file_max;
53 /** Number of file instances lost. @sa file_array */
54 ulong file_lost;
55 /**
56 Size of the file handle array. @sa file_handle_array.
57 Signed value, for easier comparisons with a file descriptor number.
58 */
59 long file_handle_max;
60 /** Number of file handle lost. @sa file_handle_array */
61 ulong file_handle_lost;
62 /** Size of the table instances array. @sa table_array */
63 ulong table_max;
64 /** Number of table instances lost. @sa table_array */
65 ulong table_lost;
66 /** Number of EVENTS_WAITS_HISTORY records per thread. */
67 ulong events_waits_history_per_thread;
68 /** Number of instruments class per thread. */
69 ulong instr_class_per_thread;
70 /** Number of locker lost. @sa LOCKER_STACK_SIZE. */
71 ulong locker_lost= 0;
72
73 /**
74 Mutex instrumentation instances array.
75 @sa mutex_max
76 @sa mutex_lost
77 */
78 PFS_mutex *mutex_array= NULL;
79
80 /**
81 RWLock instrumentation instances array.
82 @sa rwlock_max
83 @sa rwlock_lost
84 */
85 PFS_rwlock *rwlock_array= NULL;
86
87 /**
88 Condition instrumentation instances array.
89 @sa cond_max
90 @sa cond_lost
91 */
92 PFS_cond *cond_array= NULL;
93
94 /**
95 Thread instrumentation instances array.
96 @sa thread_max
97 @sa thread_lost
98 */
99 PFS_thread *thread_array= NULL;
100
101 /**
102 File instrumentation instances array.
103 @sa file_max
104 @sa file_lost
105 @sa filename_hash
106 */
107 PFS_file *file_array= NULL;
108
109 /**
110 File instrumentation handle array.
111 @sa file_handle_max
112 @sa file_handle_lost
113 */
114 PFS_file **file_handle_array= NULL;
115
116 /**
117 Table instrumentation instances array.
118 @sa table_max
119 @sa table_lost
120 */
121 PFS_table *table_array= NULL;
122
123 static volatile uint32 thread_internal_id_counter= 0;
124
125 static uint per_thread_rwlock_class_start;
126 static uint per_thread_cond_class_start;
127 static uint per_thread_file_class_start;
128 static uint thread_instr_class_waits_sizing;
129 static PFS_single_stat_chain *thread_instr_class_waits_array= NULL;
130
131 static PFS_events_waits *thread_history_array= NULL;
132
133 /** Hash table for instrumented files. */
134 static LF_HASH filename_hash;
135 /** True if filename_hash is initialized. */
136 static bool filename_hash_inited= false;
137 C_MODE_START
138 /** Get hash table key for instrumented files. */
139 static uchar *filename_hash_get_key(const uchar *, size_t *, my_bool);
140 C_MODE_END
141
142 /**
143 Initialize all the instruments instance buffers.
144 @param param sizing parameters
145 @return 0 on success
146 */
init_instruments(const PFS_global_param * param)147 int init_instruments(const PFS_global_param *param)
148 {
149 uint thread_history_sizing;
150 uint index;
151
152 mutex_max= param->m_mutex_sizing;
153 mutex_lost= 0;
154 rwlock_max= param->m_rwlock_sizing;
155 rwlock_lost= 0;
156 cond_max= param->m_cond_sizing;
157 cond_lost= 0;
158 file_max= param->m_file_sizing;
159 file_lost= 0;
160 file_handle_max= param->m_file_handle_sizing;
161 file_handle_lost= 0;
162 table_max= param->m_table_sizing;
163 table_lost= 0;
164 thread_max= param->m_thread_sizing;
165 thread_lost= 0;
166
167 events_waits_history_per_thread= param->m_events_waits_history_sizing;
168 thread_history_sizing= param->m_thread_sizing
169 * events_waits_history_per_thread;
170
171 per_thread_rwlock_class_start= param->m_mutex_class_sizing;
172 per_thread_cond_class_start= per_thread_rwlock_class_start
173 + param->m_rwlock_class_sizing;
174 per_thread_file_class_start= per_thread_cond_class_start
175 + param->m_cond_class_sizing;
176 instr_class_per_thread= per_thread_file_class_start
177 + param->m_file_class_sizing;
178
179 thread_instr_class_waits_sizing= param->m_thread_sizing
180 * instr_class_per_thread;
181
182 mutex_array= NULL;
183 rwlock_array= NULL;
184 cond_array= NULL;
185 file_array= NULL;
186 file_handle_array= NULL;
187 table_array= NULL;
188 thread_array= NULL;
189 thread_history_array= NULL;
190 thread_instr_class_waits_array= NULL;
191 thread_internal_id_counter= 0;
192
193 if (mutex_max > 0)
194 {
195 mutex_array= PFS_MALLOC_ARRAY(mutex_max, PFS_mutex, MYF(MY_ZEROFILL));
196 if (unlikely(mutex_array == NULL))
197 return 1;
198 }
199
200 if (rwlock_max > 0)
201 {
202 rwlock_array= PFS_MALLOC_ARRAY(rwlock_max, PFS_rwlock, MYF(MY_ZEROFILL));
203 if (unlikely(rwlock_array == NULL))
204 return 1;
205 }
206
207 if (cond_max > 0)
208 {
209 cond_array= PFS_MALLOC_ARRAY(cond_max, PFS_cond, MYF(MY_ZEROFILL));
210 if (unlikely(cond_array == NULL))
211 return 1;
212 }
213
214 if (file_max > 0)
215 {
216 file_array= PFS_MALLOC_ARRAY(file_max, PFS_file, MYF(MY_ZEROFILL));
217 if (unlikely(file_array == NULL))
218 return 1;
219 }
220
221 if (file_handle_max > 0)
222 {
223 file_handle_array= PFS_MALLOC_ARRAY(file_handle_max, PFS_file*, MYF(MY_ZEROFILL));
224 if (unlikely(file_handle_array == NULL))
225 return 1;
226 }
227
228 if (table_max > 0)
229 {
230 table_array= PFS_MALLOC_ARRAY(table_max, PFS_table, MYF(MY_ZEROFILL));
231 if (unlikely(table_array == NULL))
232 return 1;
233 }
234
235 if (thread_max > 0)
236 {
237 thread_array= PFS_MALLOC_ARRAY(thread_max, PFS_thread, MYF(MY_ZEROFILL));
238 if (unlikely(thread_array == NULL))
239 return 1;
240 }
241
242 if (thread_history_sizing > 0)
243 {
244 thread_history_array=
245 PFS_MALLOC_ARRAY(thread_history_sizing, PFS_events_waits,
246 MYF(MY_ZEROFILL));
247 if (unlikely(thread_history_array == NULL))
248 return 1;
249 }
250
251 if (thread_instr_class_waits_sizing > 0)
252 {
253 thread_instr_class_waits_array=
254 PFS_MALLOC_ARRAY(thread_instr_class_waits_sizing,
255 PFS_single_stat_chain, MYF(MY_ZEROFILL));
256 if (unlikely(thread_instr_class_waits_array == NULL))
257 return 1;
258 }
259
260 for (index= 0; index < thread_instr_class_waits_sizing; index++)
261 {
262 /*
263 Currently, this chain is of length 1,
264 but it's still implemented as a stat chain,
265 since more aggregations are planned to be implemented in m_parent.
266 */
267 thread_instr_class_waits_array[index].m_control_flag=
268 &flag_events_waits_summary_by_thread_by_event_name;
269 thread_instr_class_waits_array[index].m_parent= NULL;
270 }
271
272 for (index= 0; index < thread_max; index++)
273 {
274 thread_array[index].m_waits_history=
275 &thread_history_array[index * events_waits_history_per_thread];
276 thread_array[index].m_instr_class_wait_stats=
277 &thread_instr_class_waits_array[index * instr_class_per_thread];
278 }
279
280 return 0;
281 }
282
283 /**
284 Find the per-thread wait statistics for a mutex class.
285 @param thread input thread
286 @param klass mutex class
287 @return the per thread per mutex class wait stat
288 */
289 PFS_single_stat_chain *
find_per_thread_mutex_class_wait_stat(PFS_thread * thread,PFS_mutex_class * klass)290 find_per_thread_mutex_class_wait_stat(PFS_thread *thread,
291 PFS_mutex_class *klass)
292 {
293 PFS_single_stat_chain *stat;
294 uint index;
295
296 DBUG_ASSERT(thread != NULL);
297 DBUG_ASSERT(klass != NULL);
298 index= klass->m_index;
299 DBUG_ASSERT(index < mutex_class_max);
300
301 stat= &(thread->m_instr_class_wait_stats[index]);
302 return stat;
303 }
304
305 /**
306 Find the per-thread wait statistics for a rwlock class.
307 @param thread input thread
308 @param klass rwlock class
309 @return the per thread per rwlock class wait stat
310 */
311 PFS_single_stat_chain *
find_per_thread_rwlock_class_wait_stat(PFS_thread * thread,PFS_rwlock_class * klass)312 find_per_thread_rwlock_class_wait_stat(PFS_thread *thread,
313 PFS_rwlock_class *klass)
314 {
315 PFS_single_stat_chain *stat;
316 uint index;
317
318 DBUG_ASSERT(thread != NULL);
319 DBUG_ASSERT(klass != NULL);
320 index= klass->m_index;
321 DBUG_ASSERT(index < rwlock_class_max);
322
323 stat= &(thread->m_instr_class_wait_stats
324 [per_thread_rwlock_class_start + index]);
325 return stat;
326 }
327
328 /**
329 Find the per-thread wait statistics for a condition class.
330 @param thread input thread
331 @param klass condition class
332 @return the per thread per condition class wait stat
333 */
334 PFS_single_stat_chain *
find_per_thread_cond_class_wait_stat(PFS_thread * thread,PFS_cond_class * klass)335 find_per_thread_cond_class_wait_stat(PFS_thread *thread,
336 PFS_cond_class *klass)
337 {
338 PFS_single_stat_chain *stat;
339 uint index;
340
341 DBUG_ASSERT(thread != NULL);
342 DBUG_ASSERT(klass != NULL);
343 index= klass->m_index;
344 DBUG_ASSERT(index < cond_class_max);
345
346 stat= &(thread->m_instr_class_wait_stats
347 [per_thread_cond_class_start + index]);
348 return stat;
349 }
350
351 /**
352 Find the per-thread wait statistics for a file class.
353 @param thread input thread
354 @param klass file class
355 @return the per thread per file class wait stat
356 */
357 PFS_single_stat_chain *
find_per_thread_file_class_wait_stat(PFS_thread * thread,PFS_file_class * klass)358 find_per_thread_file_class_wait_stat(PFS_thread *thread,
359 PFS_file_class *klass)
360 {
361 PFS_single_stat_chain *stat;
362 uint index;
363
364 DBUG_ASSERT(thread != NULL);
365 DBUG_ASSERT(klass != NULL);
366 index= klass->m_index;
367 DBUG_ASSERT(index < file_class_max);
368
369 stat= &(thread->m_instr_class_wait_stats
370 [per_thread_file_class_start + index]);
371 return stat;
372 }
373
374 /** Reset the wait statistics per thread. */
reset_per_thread_wait_stat(void)375 void reset_per_thread_wait_stat(void)
376 {
377 PFS_single_stat_chain *stat= thread_instr_class_waits_array;
378 PFS_single_stat_chain *stat_last= stat + thread_instr_class_waits_sizing;
379
380 for ( ; stat < stat_last; stat++)
381 reset_single_stat_link(stat);
382 }
383
384 /** Cleanup all the instruments buffers. */
cleanup_instruments(void)385 void cleanup_instruments(void)
386 {
387 pfs_free(mutex_array);
388 mutex_array= NULL;
389 mutex_max= 0;
390 pfs_free(rwlock_array);
391 rwlock_array= NULL;
392 rwlock_max= 0;
393 pfs_free(cond_array);
394 cond_array= NULL;
395 cond_max= 0;
396 pfs_free(file_array);
397 file_array= NULL;
398 file_max= 0;
399 pfs_free(file_handle_array);
400 file_handle_array= NULL;
401 file_handle_max= 0;
402 pfs_free(table_array);
403 table_array= NULL;
404 table_max= 0;
405 pfs_free(thread_array);
406 thread_array= NULL;
407 thread_max= 0;
408 pfs_free(thread_history_array);
409 thread_history_array= NULL;
410 pfs_free(thread_instr_class_waits_array);
411 thread_instr_class_waits_array= NULL;
412 }
413
414 extern "C"
415 {
filename_hash_get_key(const uchar * entry,size_t * length,my_bool)416 static uchar *filename_hash_get_key(const uchar *entry, size_t *length,
417 my_bool)
418 {
419 const PFS_file * const *typed_entry;
420 const PFS_file *file;
421 const void *result;
422 typed_entry= reinterpret_cast<const PFS_file* const *> (entry);
423 DBUG_ASSERT(typed_entry != NULL);
424 file= *typed_entry;
425 DBUG_ASSERT(file != NULL);
426 *length= file->m_filename_length;
427 result= file->m_filename;
428 return const_cast<uchar*> (reinterpret_cast<const uchar*> (result));
429 }
430 }
431
432 /**
433 Initialize the file name hash.
434 @return 0 on success
435 */
init_file_hash(void)436 int init_file_hash(void)
437 {
438 if (! filename_hash_inited)
439 {
440 lf_hash_init(&filename_hash, sizeof(PFS_file*), LF_HASH_UNIQUE,
441 0, 0, filename_hash_get_key, &my_charset_bin);
442 filename_hash_inited= true;
443 }
444 return 0;
445 }
446
447 /** Cleanup the file name hash. */
cleanup_file_hash(void)448 void cleanup_file_hash(void)
449 {
450 if (filename_hash_inited)
451 {
452 lf_hash_destroy(&filename_hash);
453 filename_hash_inited= false;
454 }
455 }
456
init(uint random,uint max_size)457 void PFS_scan::init(uint random, uint max_size)
458 {
459 m_pass= 0;
460
461 if (max_size == 0)
462 {
463 /* Degenerated case, no buffer */
464 m_pass_max= 0;
465 return;
466 }
467
468 DBUG_ASSERT(random < max_size);
469
470 if (PFS_MAX_ALLOC_RETRY < max_size)
471 {
472 /*
473 The buffer is big compared to PFS_MAX_ALLOC_RETRY,
474 scan it only partially.
475 */
476 if (random + PFS_MAX_ALLOC_RETRY < max_size)
477 {
478 /*
479 Pass 1: [random, random + PFS_MAX_ALLOC_RETRY - 1]
480 Pass 2: not used.
481 */
482 m_pass_max= 1;
483 m_first[0]= random;
484 m_last[0]= random + PFS_MAX_ALLOC_RETRY;
485 m_first[1]= 0;
486 m_last[1]= 0;
487 }
488 else
489 {
490 /*
491 Pass 1: [random, max_size - 1]
492 Pass 2: [0, ...]
493 The combined length of pass 1 and 2 is PFS_MAX_ALLOC_RETRY.
494 */
495 m_pass_max= 2;
496 m_first[0]= random;
497 m_last[0]= max_size;
498 m_first[1]= 0;
499 m_last[1]= PFS_MAX_ALLOC_RETRY - (max_size - random);
500 }
501 }
502 else
503 {
504 /*
505 The buffer is small compared to PFS_MAX_ALLOC_RETRY,
506 scan it in full in two passes.
507 Pass 1: [random, max_size - 1]
508 Pass 2: [0, random - 1]
509 */
510 m_pass_max= 2;
511 m_first[0]= random;
512 m_last[0]= max_size;
513 m_first[1]= 0;
514 m_last[1]= random;
515 }
516
517 DBUG_ASSERT(m_first[0] < max_size);
518 DBUG_ASSERT(m_first[1] < max_size);
519 DBUG_ASSERT(m_last[1] <= max_size);
520 DBUG_ASSERT(m_last[1] <= max_size);
521 /* The combined length of all passes should not exceed PFS_MAX_ALLOC_RETRY. */
522 DBUG_ASSERT((m_last[0] - m_first[0]) +
523 (m_last[1] - m_first[1]) <= PFS_MAX_ALLOC_RETRY);
524 }
525
526 /**
527 Create instrumentation for a mutex instance.
528 @param klass the mutex class
529 @param identity the mutex address
530 @return a mutex instance, or NULL
531 */
create_mutex(PFS_mutex_class * klass,const void * identity)532 PFS_mutex* create_mutex(PFS_mutex_class *klass, const void *identity)
533 {
534 PFS_scan scan;
535 uint random= randomized_index(identity, mutex_max);
536
537 for (scan.init(random, mutex_max);
538 scan.has_pass();
539 scan.next_pass())
540 {
541 PFS_mutex *pfs= mutex_array + scan.first();
542 PFS_mutex *pfs_last= mutex_array + scan.last();
543 for ( ; pfs < pfs_last; pfs++)
544 {
545 if (pfs->m_lock.is_free())
546 {
547 if (pfs->m_lock.free_to_dirty())
548 {
549 pfs->m_identity= identity;
550 pfs->m_class= klass;
551 pfs->m_wait_stat.m_control_flag=
552 &flag_events_waits_summary_by_instance;
553 pfs->m_wait_stat.m_parent= &klass->m_wait_stat;
554 reset_single_stat_link(&pfs->m_wait_stat);
555 pfs->m_lock_stat.m_control_flag=
556 &flag_events_locks_summary_by_instance;
557 pfs->m_lock_stat.m_parent= &klass->m_lock_stat;
558 reset_single_stat_link(&pfs->m_lock_stat);
559 pfs->m_owner= NULL;
560 pfs->m_last_locked= 0;
561 pfs->m_lock.dirty_to_allocated();
562 return pfs;
563 }
564 }
565 }
566 }
567
568 mutex_lost++;
569 return NULL;
570 }
571
572 /**
573 Destroy instrumentation for a mutex instance.
574 @param pfs the mutex to destroy
575 */
destroy_mutex(PFS_mutex * pfs)576 void destroy_mutex(PFS_mutex *pfs)
577 {
578 DBUG_ASSERT(pfs != NULL);
579 pfs->m_lock.allocated_to_free();
580 }
581
582 /**
583 Create instrumentation for a rwlock instance.
584 @param klass the rwlock class
585 @param identity the rwlock address
586 @return a rwlock instance, or NULL
587 */
create_rwlock(PFS_rwlock_class * klass,const void * identity)588 PFS_rwlock* create_rwlock(PFS_rwlock_class *klass, const void *identity)
589 {
590 PFS_scan scan;
591 uint random= randomized_index(identity, rwlock_max);
592
593 for (scan.init(random, rwlock_max);
594 scan.has_pass();
595 scan.next_pass())
596 {
597 PFS_rwlock *pfs= rwlock_array + scan.first();
598 PFS_rwlock *pfs_last= rwlock_array + scan.last();
599 for ( ; pfs < pfs_last; pfs++)
600 {
601 if (pfs->m_lock.is_free())
602 {
603 if (pfs->m_lock.free_to_dirty())
604 {
605 pfs->m_identity= identity;
606 pfs->m_class= klass;
607 pfs->m_wait_stat.m_control_flag=
608 &flag_events_waits_summary_by_instance;
609 pfs->m_wait_stat.m_parent= &klass->m_wait_stat;
610 reset_single_stat_link(&pfs->m_wait_stat);
611 pfs->m_lock.dirty_to_allocated();
612 pfs->m_read_lock_stat.m_control_flag=
613 &flag_events_locks_summary_by_instance;
614 pfs->m_read_lock_stat.m_parent= &klass->m_read_lock_stat;
615 reset_single_stat_link(&pfs->m_read_lock_stat);
616 pfs->m_write_lock_stat.m_control_flag=
617 &flag_events_locks_summary_by_instance;
618 pfs->m_write_lock_stat.m_parent= &klass->m_write_lock_stat;
619 reset_single_stat_link(&pfs->m_write_lock_stat);
620 pfs->m_writer= NULL;
621 pfs->m_readers= 0;
622 pfs->m_last_written= 0;
623 pfs->m_last_read= 0;
624 return pfs;
625 }
626 }
627 }
628 }
629
630 rwlock_lost++;
631 return NULL;
632 }
633
634 /**
635 Destroy instrumentation for a rwlock instance.
636 @param pfs the rwlock to destroy
637 */
destroy_rwlock(PFS_rwlock * pfs)638 void destroy_rwlock(PFS_rwlock *pfs)
639 {
640 DBUG_ASSERT(pfs != NULL);
641 pfs->m_lock.allocated_to_free();
642 }
643
644 /**
645 Create instrumentation for a condition instance.
646 @param klass the condition class
647 @param identity the condition address
648 @return a condition instance, or NULL
649 */
create_cond(PFS_cond_class * klass,const void * identity)650 PFS_cond* create_cond(PFS_cond_class *klass, const void *identity)
651 {
652 PFS_scan scan;
653 uint random= randomized_index(identity, cond_max);
654
655 for (scan.init(random, cond_max);
656 scan.has_pass();
657 scan.next_pass())
658 {
659 PFS_cond *pfs= cond_array + scan.first();
660 PFS_cond *pfs_last= cond_array + scan.last();
661 for ( ; pfs < pfs_last; pfs++)
662 {
663 if (pfs->m_lock.is_free())
664 {
665 if (pfs->m_lock.free_to_dirty())
666 {
667 pfs->m_identity= identity;
668 pfs->m_class= klass;
669 pfs->m_cond_stat.m_signal_count= 0;
670 pfs->m_cond_stat.m_broadcast_count= 0;
671 pfs->m_wait_stat.m_control_flag=
672 &flag_events_waits_summary_by_instance;
673 pfs->m_wait_stat.m_parent= &klass->m_wait_stat;
674 reset_single_stat_link(&pfs->m_wait_stat);
675 pfs->m_lock.dirty_to_allocated();
676 return pfs;
677 }
678 }
679 }
680 }
681
682 cond_lost++;
683 return NULL;
684 }
685
686 /**
687 Destroy instrumentation for a condition instance.
688 @param pfs the condition to destroy
689 */
destroy_cond(PFS_cond * pfs)690 void destroy_cond(PFS_cond *pfs)
691 {
692 DBUG_ASSERT(pfs != NULL);
693 pfs->m_lock.allocated_to_free();
694 }
695
696 /**
697 Create instrumentation for a thread instance.
698 @param klass the thread class
699 @param identity the thread address,
700 or a value characteristic of this thread
701 @param thread_id the PROCESSLIST thread id,
702 or 0 if unknown
703 @return a thread instance, or NULL
704 */
create_thread(PFS_thread_class * klass,const void * identity,ulong thread_id)705 PFS_thread* create_thread(PFS_thread_class *klass, const void *identity,
706 ulong thread_id)
707 {
708 PFS_scan scan;
709 uint random= randomized_index(identity, thread_max);
710
711 for (scan.init(random, thread_max);
712 scan.has_pass();
713 scan.next_pass())
714 {
715 PFS_thread *pfs= thread_array + scan.first();
716 PFS_thread *pfs_last= thread_array + scan.last();
717 for ( ; pfs < pfs_last; pfs++)
718 {
719 if (pfs->m_lock.is_free())
720 {
721 if (pfs->m_lock.free_to_dirty())
722 {
723 pfs->m_thread_internal_id=
724 PFS_atomic::add_u32(&thread_internal_id_counter, 1);
725 pfs->m_thread_id= thread_id;
726 pfs->m_event_id= 1;
727 pfs->m_enabled= true;
728 pfs->m_class= klass;
729 pfs->m_wait_locker_count= 0;
730 pfs->m_waits_history_full= false;
731 pfs->m_waits_history_index= 0;
732
733 PFS_single_stat_chain *stat= pfs->m_instr_class_wait_stats;
734 PFS_single_stat_chain *stat_last= stat + instr_class_per_thread;
735 for ( ; stat < stat_last; stat++)
736 reset_single_stat_link(stat);
737 pfs->m_filename_hash_pins= NULL;
738 pfs->m_table_share_hash_pins= NULL;
739 pfs->m_lock.dirty_to_allocated();
740 return pfs;
741 }
742 }
743 }
744 }
745
746 thread_lost++;
747 return NULL;
748 }
749
750 /**
751 Sanitize a PFS_thread pointer.
752 Validate that the PFS_thread is part of thread_array.
753 Sanitizing data is required when the data can be
754 damaged with expected race conditions, for example
755 involving EVENTS_WAITS_HISTORY_LONG.
756 @param unsafe the pointer to sanitize
757 @return a valid pointer, or NULL
758 */
sanitize_thread(PFS_thread * unsafe)759 PFS_thread *sanitize_thread(PFS_thread *unsafe)
760 {
761 SANITIZE_ARRAY_BODY(PFS_thread, thread_array, thread_max, unsafe);
762 }
763
sanitize_file_name(const char * unsafe)764 const char *sanitize_file_name(const char *unsafe)
765 {
766 intptr ptr= (intptr) unsafe;
767 intptr first= (intptr) &file_array[0];
768 intptr last= (intptr) &file_array[file_max];
769
770 /* Check if unsafe points inside file_array[] */
771 if (likely((first <= ptr) && (ptr < last)))
772 {
773 /* Check if unsafe points to PFS_file::m_filename */
774 intptr offset= (ptr - first) % sizeof(PFS_file);
775 intptr valid_offset= my_offsetof(PFS_file, m_filename[0]);
776 if (likely(offset == valid_offset))
777 {
778 return unsafe;
779 }
780 }
781 return NULL;
782 }
783
784 /**
785 Destroy instrumentation for a thread instance.
786 @param pfs the thread to destroy
787 */
destroy_thread(PFS_thread * pfs)788 void destroy_thread(PFS_thread *pfs)
789 {
790 DBUG_ASSERT(pfs != NULL);
791 if (pfs->m_filename_hash_pins)
792 {
793 lf_hash_put_pins(pfs->m_filename_hash_pins);
794 pfs->m_filename_hash_pins= NULL;
795 }
796 if (pfs->m_table_share_hash_pins)
797 {
798 lf_hash_put_pins(pfs->m_table_share_hash_pins);
799 pfs->m_table_share_hash_pins= NULL;
800 }
801 pfs->m_lock.allocated_to_free();
802 }
803
804 /**
805 Get the hash pins for @filename_hash.
806 @param thread The running thread.
807 @returns The LF_HASH pins for the thread.
808 */
get_filename_hash_pins(PFS_thread * thread)809 LF_PINS* get_filename_hash_pins(PFS_thread *thread)
810 {
811 if (unlikely(thread->m_filename_hash_pins == NULL))
812 {
813 if (! filename_hash_inited)
814 return NULL;
815 thread->m_filename_hash_pins= lf_hash_get_pins(&filename_hash);
816 }
817 return thread->m_filename_hash_pins;
818 }
819
820 /**
821 Find or create instrumentation for a file instance by file name.
822 @param thread the executing instrumented thread
823 @param klass the file class
824 @param filename the file name
825 @param len the length in bytes of filename
826 @return a file instance, or NULL
827 */
828 PFS_file*
find_or_create_file(PFS_thread * thread,PFS_file_class * klass,const char * filename,uint len)829 find_or_create_file(PFS_thread *thread, PFS_file_class *klass,
830 const char *filename, uint len)
831 {
832 PFS_file *pfs;
833 PFS_scan scan;
834
835 LF_PINS *pins= get_filename_hash_pins(thread);
836 if (unlikely(pins == NULL))
837 {
838 file_lost++;
839 return NULL;
840 }
841
842 char safe_buffer[FN_REFLEN];
843 const char *safe_filename;
844
845 if (len >= FN_REFLEN)
846 {
847 /*
848 The instrumented code uses file names that exceeds FN_REFLEN.
849 This could be legal for instrumentation on non mysys APIs,
850 so we support it.
851 Truncate the file name so that:
852 - it fits into pfs->m_filename
853 - it is safe to use mysys apis to normalize the file name.
854 */
855 memcpy(safe_buffer, filename, FN_REFLEN - 1);
856 safe_buffer[FN_REFLEN - 1]= 0;
857 safe_filename= safe_buffer;
858 }
859 else
860 safe_filename= filename;
861
862 /*
863 Normalize the file name to avoid duplicates when using aliases:
864 - absolute or relative paths
865 - symbolic links
866 Names are resolved as follows:
867 - /real/path/to/real_file ==> same
868 - /path/with/link/to/real_file ==> /real/path/to/real_file
869 - real_file ==> /real/path/to/real_file
870 - ./real_file ==> /real/path/to/real_file
871 - /real/path/to/sym_link ==> same
872 - /path/with/link/to/sym_link ==> /real/path/to/sym_link
873 - sym_link ==> /real/path/to/sym_link
874 - ./sym_link ==> /real/path/to/sym_link
875 When the last component of a file is a symbolic link,
876 the last component is *not* resolved, so that all file io
877 operations on a link (create, read, write, delete) are counted
878 against the link itself, not the target file.
879 Resolving the name would lead to create counted against the link,
880 and read/write/delete counted against the target, leading to
881 incoherent results and instrumentation leaks.
882 Also note that, when creating files, this name resolution
883 works properly for files that do not exist (yet) on the file system.
884 */
885 char buffer[FN_REFLEN];
886 char dirbuffer[FN_REFLEN];
887 size_t dirlen;
888 const char *normalized_filename;
889 int normalized_length;
890
891 dirlen= dirname_length(safe_filename);
892 if (dirlen == 0)
893 {
894 dirbuffer[0]= FN_CURLIB;
895 dirbuffer[1]= FN_LIBCHAR;
896 dirbuffer[2]= '\0';
897 }
898 else
899 {
900 memcpy(dirbuffer, safe_filename, dirlen);
901 dirbuffer[dirlen]= '\0';
902 }
903
904 if (my_realpath(buffer, dirbuffer, MYF(0)) != 0)
905 {
906 file_lost++;
907 return NULL;
908 }
909
910 /* Append the unresolved file name to the resolved path */
911 char *ptr= buffer + strlen(buffer);
912 char *buf_end= &buffer[sizeof(buffer)-1];
913 if ((buf_end > ptr) && (*(ptr-1) != FN_LIBCHAR))
914 *ptr++= FN_LIBCHAR;
915 if (buf_end > ptr)
916 strncpy(ptr, safe_filename + dirlen, buf_end - ptr);
917 *buf_end= '\0';
918
919 normalized_filename= buffer;
920 normalized_length= strlen(normalized_filename);
921
922 PFS_file **entry;
923 uint retry_count= 0;
924 const uint retry_max= 3;
925 search:
926 entry= reinterpret_cast<PFS_file**>
927 (lf_hash_search(&filename_hash, pins,
928 normalized_filename, normalized_length));
929 if (entry && (entry != MY_ERRPTR))
930 {
931 pfs= *entry;
932 pfs->m_file_stat.m_open_count++;
933 lf_hash_search_unpin(pins);
934 return pfs;
935 }
936
937 lf_hash_search_unpin(pins);
938
939 /* filename is not constant, just using it for noise on create */
940 uint random= randomized_index(filename, file_max);
941
942 for (scan.init(random, file_max);
943 scan.has_pass();
944 scan.next_pass())
945 {
946 pfs= file_array + scan.first();
947 PFS_file *pfs_last= file_array + scan.last();
948 for ( ; pfs < pfs_last; pfs++)
949 {
950 if (pfs->m_lock.is_free())
951 {
952 if (pfs->m_lock.free_to_dirty())
953 {
954 pfs->m_class= klass;
955 strncpy(pfs->m_filename, normalized_filename, normalized_length);
956 pfs->m_filename[normalized_length]= '\0';
957 pfs->m_filename_length= normalized_length;
958 pfs->m_file_stat.m_open_count= 1;
959 pfs->m_wait_stat.m_control_flag=
960 &flag_events_waits_summary_by_instance;
961 pfs->m_wait_stat.m_parent= &klass->m_wait_stat;
962 reset_single_stat_link(&pfs->m_wait_stat);
963
964 int res;
965 res= lf_hash_insert(&filename_hash, pins,
966 &pfs);
967 if (likely(res == 0))
968 {
969 pfs->m_lock.dirty_to_allocated();
970 return pfs;
971 }
972
973 pfs->m_lock.dirty_to_free();
974
975 if (res > 0)
976 {
977 /* Duplicate insert by another thread */
978 if (++retry_count > retry_max)
979 {
980 /* Avoid infinite loops */
981 file_lost++;
982 return NULL;
983 }
984 goto search;
985 }
986
987 /* OOM in lf_hash_insert */
988 file_lost++;
989 return NULL;
990 }
991 }
992 }
993 }
994
995 file_lost++;
996 return NULL;
997 }
998
999 /**
1000 Release instrumentation for a file instance.
1001 @param pfs the file to release
1002 */
release_file(PFS_file * pfs)1003 void release_file(PFS_file *pfs)
1004 {
1005 DBUG_ASSERT(pfs != NULL);
1006 pfs->m_file_stat.m_open_count--;
1007 }
1008
1009 /**
1010 Destroy instrumentation for a file instance.
1011 @param thread the executing thread instrumentation
1012 @param pfs the file to destroy
1013 */
destroy_file(PFS_thread * thread,PFS_file * pfs)1014 void destroy_file(PFS_thread *thread, PFS_file *pfs)
1015 {
1016 DBUG_ASSERT(thread != NULL);
1017 DBUG_ASSERT(pfs != NULL);
1018
1019 LF_PINS *pins= get_filename_hash_pins(thread);
1020 DBUG_ASSERT(pins != NULL);
1021
1022 lf_hash_delete(&filename_hash, pins,
1023 pfs->m_filename, pfs->m_filename_length);
1024 pfs->m_lock.allocated_to_free();
1025 }
1026
1027 /**
1028 Create instrumentation for a table instance.
1029 @param share the table share
1030 @param identity the table address
1031 @return a table instance, or NULL
1032 */
create_table(PFS_table_share * share,const void * identity)1033 PFS_table* create_table(PFS_table_share *share, const void *identity)
1034 {
1035 PFS_scan scan;
1036 uint random= randomized_index(identity, table_max);
1037
1038 for (scan.init(random, table_max);
1039 scan.has_pass();
1040 scan.next_pass())
1041 {
1042 PFS_table *pfs= table_array + scan.first();
1043 PFS_table *pfs_last= table_array + scan.last();
1044 for ( ; pfs < pfs_last; pfs++)
1045 {
1046 if (pfs->m_lock.is_free())
1047 {
1048 if (pfs->m_lock.free_to_dirty())
1049 {
1050 pfs->m_identity= identity;
1051 pfs->m_share= share;
1052 pfs->m_wait_stat.m_control_flag=
1053 &flag_events_waits_summary_by_instance;
1054 pfs->m_wait_stat.m_parent= &share->m_wait_stat;
1055 reset_single_stat_link(&pfs->m_wait_stat);
1056 pfs->m_lock.dirty_to_allocated();
1057 return pfs;
1058 }
1059 }
1060 }
1061 }
1062
1063 table_lost++;
1064 return NULL;
1065 }
1066
1067 /**
1068 Destroy instrumentation for a table instance.
1069 @param pfs the table to destroy
1070 */
destroy_table(PFS_table * pfs)1071 void destroy_table(PFS_table *pfs)
1072 {
1073 DBUG_ASSERT(pfs != NULL);
1074 pfs->m_lock.allocated_to_free();
1075 }
1076
reset_mutex_waits_by_instance(void)1077 static void reset_mutex_waits_by_instance(void)
1078 {
1079 PFS_mutex *pfs= mutex_array;
1080 PFS_mutex *pfs_last= mutex_array + mutex_max;
1081
1082 for ( ; pfs < pfs_last; pfs++)
1083 reset_single_stat_link(&pfs->m_wait_stat);
1084 }
1085
reset_rwlock_waits_by_instance(void)1086 static void reset_rwlock_waits_by_instance(void)
1087 {
1088 PFS_rwlock *pfs= rwlock_array;
1089 PFS_rwlock *pfs_last= rwlock_array + rwlock_max;
1090
1091 for ( ; pfs < pfs_last; pfs++)
1092 reset_single_stat_link(&pfs->m_wait_stat);
1093 }
1094
reset_cond_waits_by_instance(void)1095 static void reset_cond_waits_by_instance(void)
1096 {
1097 PFS_cond *pfs= cond_array;
1098 PFS_cond *pfs_last= cond_array + cond_max;
1099
1100 for ( ; pfs < pfs_last; pfs++)
1101 reset_single_stat_link(&pfs->m_wait_stat);
1102 }
1103
reset_file_waits_by_instance(void)1104 static void reset_file_waits_by_instance(void)
1105 {
1106 PFS_file *pfs= file_array;
1107 PFS_file *pfs_last= file_array + file_max;
1108
1109 for ( ; pfs < pfs_last; pfs++)
1110 reset_single_stat_link(&pfs->m_wait_stat);
1111 }
1112
1113 /** Reset the wait statistics per object instance. */
reset_events_waits_by_instance(void)1114 void reset_events_waits_by_instance(void)
1115 {
1116 reset_mutex_waits_by_instance();
1117 reset_rwlock_waits_by_instance();
1118 reset_cond_waits_by_instance();
1119 reset_file_waits_by_instance();
1120 }
1121
1122 /** Reset the io statistics per file instance. */
reset_file_instance_io(void)1123 void reset_file_instance_io(void)
1124 {
1125 PFS_file *pfs= file_array;
1126 PFS_file *pfs_last= file_array + file_max;
1127
1128 for ( ; pfs < pfs_last; pfs++)
1129 reset_file_stat(&pfs->m_file_stat);
1130 }
1131
1132 /** @} */
1133