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