summaryrefslogtreecommitdiff
path: root/storage/perfschema/pfs_instr.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/perfschema/pfs_instr.cc')
-rw-r--r--storage/perfschema/pfs_instr.cc962
1 files changed, 962 insertions, 0 deletions
diff --git a/storage/perfschema/pfs_instr.cc b/storage/perfschema/pfs_instr.cc
new file mode 100644
index 00000000000..2ce3a844290
--- /dev/null
+++ b/storage/perfschema/pfs_instr.cc
@@ -0,0 +1,962 @@
+/* Copyright (C) 2008-2009 Sun Microsystems, Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @file storage/perfschema/pfs_instr.cc
+ Performance schema instruments (implementation).
+*/
+
+#include "my_global.h"
+#include "mysql_priv.h"
+#include "my_sys.h"
+#include "pfs_stat.h"
+#include "pfs_instr.h"
+#include "pfs_global.h"
+
+/**
+ @addtogroup Performance_schema_buffers
+ @{
+*/
+
+/** Size of the mutex instances array. @sa mutex_array */
+ulong mutex_max;
+/** Number of mutexes instance lost. @sa mutex_array */
+ulong mutex_lost;
+/** Size of the rwlock instances array. @sa rwlock_array */
+ulong rwlock_max;
+/** Number or rwlock instances lost. @sa rwlock_array */
+ulong rwlock_lost;
+/** Size of the conditions instances array. @sa cond_array */
+ulong cond_max;
+/** Number of conditions instances lost. @sa cond_array */
+ulong cond_lost;
+/** Size of the thread instances array. @sa thread_array */
+ulong thread_max;
+/** Number or thread instances lost. @sa thread_array */
+ulong thread_lost;
+/** Size of the file instances array. @sa file_array */
+ulong file_max;
+/** Number of file instances lost. @sa file_array */
+ulong file_lost;
+/**
+ Size of the file handle array. @sa file_handle_array.
+ Signed value, for easier comparisons with a file descriptor number.
+*/
+long file_handle_max;
+/** Number of file handle lost. @sa file_handle_array */
+ulong file_handle_lost;
+/** Size of the table instances array. @sa table_array */
+ulong table_max;
+/** Number of table instances lost. @sa table_array */
+ulong table_lost;
+/** Number of EVENTS_WAITS_HISTORY records per thread. */
+ulong events_waits_history_per_thread;
+/** Number of instruments class per thread. */
+ulong instr_class_per_thread;
+/** Number of locker lost. @sa LOCKER_STACK_SIZE. */
+ulong locker_lost;
+
+/**
+ Mutex instrumentation instances array.
+ @sa mutex_max
+ @sa mutex_lost
+*/
+PFS_mutex *mutex_array= NULL;
+
+/**
+ RWLock instrumentation instances array.
+ @sa rwlock_max
+ @sa rwlock_lost
+*/
+PFS_rwlock *rwlock_array= NULL;
+
+/**
+ Condition instrumentation instances array.
+ @sa cond_max
+ @sa cond_lost
+*/
+PFS_cond *cond_array= NULL;
+
+/**
+ Thread instrumentation instances array.
+ @sa thread_max
+ @sa thread_lost
+*/
+PFS_thread *thread_array= NULL;
+
+/**
+ File instrumentation instances array.
+ @sa file_max
+ @sa file_lost
+ @sa filename_hash
+*/
+PFS_file *file_array= NULL;
+
+/**
+ File instrumentation handle array.
+ @sa file_handle_max
+ @sa file_handle_lost
+*/
+PFS_file **file_handle_array= NULL;
+
+/**
+ Table instrumentation instances array.
+ @sa table_max
+ @sa table_lost
+*/
+PFS_table *table_array= NULL;
+
+static volatile uint32 thread_internal_id_counter= 0;
+
+static uint per_thread_rwlock_class_start;
+static uint per_thread_cond_class_start;
+static uint per_thread_file_class_start;
+static uint thread_instr_class_waits_sizing;
+static PFS_single_stat_chain *thread_instr_class_waits_array= NULL;
+
+static PFS_events_waits *thread_history_array= NULL;
+
+/** Hash table for instrumented files. */
+static LF_HASH filename_hash;
+/** True if filename_hash is initialized. */
+static bool filename_hash_inited= false;
+
+/**
+ Initialize all the instruments instance buffers.
+ @param param sizing parameters
+ @return 0 on success
+*/
+int init_instruments(const PFS_global_param *param)
+{
+ uint thread_history_sizing;
+ uint index;
+
+ mutex_max= param->m_mutex_sizing;
+ mutex_lost= 0;
+ rwlock_max= param->m_rwlock_sizing;
+ rwlock_lost= 0;
+ cond_max= param->m_cond_sizing;
+ cond_lost= 0;
+ file_max= param->m_file_sizing;
+ file_lost= 0;
+ file_handle_max= param->m_file_handle_sizing;
+ file_handle_lost= 0;
+ table_max= param->m_table_sizing;
+ table_lost= 0;
+ thread_max= param->m_thread_sizing;
+ thread_lost= 0;
+
+ events_waits_history_per_thread= param->m_events_waits_history_sizing;
+ thread_history_sizing= param->m_thread_sizing
+ * events_waits_history_per_thread;
+
+ per_thread_rwlock_class_start= param->m_mutex_class_sizing;
+ per_thread_cond_class_start= per_thread_rwlock_class_start
+ + param->m_rwlock_class_sizing;
+ per_thread_file_class_start= per_thread_cond_class_start
+ + param->m_cond_class_sizing;
+ instr_class_per_thread= per_thread_file_class_start
+ + param->m_file_class_sizing;
+
+ thread_instr_class_waits_sizing= param->m_thread_sizing
+ * instr_class_per_thread;
+
+ mutex_array= NULL;
+ rwlock_array= NULL;
+ cond_array= NULL;
+ file_array= NULL;
+ file_handle_array= NULL;
+ table_array= NULL;
+ thread_array= NULL;
+ thread_history_array= NULL;
+ thread_instr_class_waits_array= NULL;
+ thread_internal_id_counter= 0;
+
+ if (mutex_max > 0)
+ {
+ mutex_array= PFS_MALLOC_ARRAY(mutex_max, PFS_mutex, MYF(MY_ZEROFILL));
+ if (unlikely(mutex_array == NULL))
+ return 1;
+ }
+
+ if (rwlock_max > 0)
+ {
+ rwlock_array= PFS_MALLOC_ARRAY(rwlock_max, PFS_rwlock, MYF(MY_ZEROFILL));
+ if (unlikely(rwlock_array == NULL))
+ return 1;
+ }
+
+ if (cond_max > 0)
+ {
+ cond_array= PFS_MALLOC_ARRAY(cond_max, PFS_cond, MYF(MY_ZEROFILL));
+ if (unlikely(cond_array == NULL))
+ return 1;
+ }
+
+ if (file_max > 0)
+ {
+ file_array= PFS_MALLOC_ARRAY(file_max, PFS_file, MYF(MY_ZEROFILL));
+ if (unlikely(file_array == NULL))
+ return 1;
+ }
+
+ if (file_handle_max > 0)
+ {
+ file_handle_array= PFS_MALLOC_ARRAY(file_handle_max, PFS_file*, MYF(MY_ZEROFILL));
+ if (unlikely(file_handle_array == NULL))
+ return 1;
+ }
+
+ if (table_max > 0)
+ {
+ table_array= PFS_MALLOC_ARRAY(table_max, PFS_table, MYF(MY_ZEROFILL));
+ if (unlikely(table_array == NULL))
+ return 1;
+ }
+
+ if (thread_max > 0)
+ {
+ thread_array= PFS_MALLOC_ARRAY(thread_max, PFS_thread, MYF(MY_ZEROFILL));
+ if (unlikely(thread_array == NULL))
+ return 1;
+ }
+
+ if (thread_history_sizing > 0)
+ {
+ thread_history_array=
+ PFS_MALLOC_ARRAY(thread_history_sizing, PFS_events_waits,
+ MYF(MY_ZEROFILL));
+ if (unlikely(thread_history_array == NULL))
+ return 1;
+ }
+
+ if (thread_instr_class_waits_sizing > 0)
+ {
+ thread_instr_class_waits_array=
+ PFS_MALLOC_ARRAY(thread_instr_class_waits_sizing,
+ PFS_single_stat_chain, MYF(MY_ZEROFILL));
+ if (unlikely(thread_instr_class_waits_array == NULL))
+ return 1;
+ }
+
+ for (index= 0; index < thread_instr_class_waits_sizing; index++)
+ {
+ /*
+ Currently, this chain is of length 1,
+ but it's still implemented as a stat chain,
+ since more aggregations are planned to be implemented in m_parent.
+ */
+ thread_instr_class_waits_array[index].m_control_flag=
+ &flag_events_waits_summary_by_thread_by_event_name;
+ thread_instr_class_waits_array[index].m_parent= NULL;
+ }
+
+ for (index= 0; index < thread_max; index++)
+ {
+ thread_array[index].m_waits_history=
+ &thread_history_array[index * events_waits_history_per_thread];
+ thread_array[index].m_instr_class_wait_stats=
+ &thread_instr_class_waits_array[index * instr_class_per_thread];
+ }
+
+ return 0;
+}
+
+/**
+ Find the per-thread wait statistics for a mutex class.
+ @param thread input thread
+ @param klass mutex class
+ @return the per thread per mutex class wait stat
+*/
+PFS_single_stat_chain *
+find_per_thread_mutex_class_wait_stat(PFS_thread *thread,
+ PFS_mutex_class *klass)
+{
+ PFS_single_stat_chain *stat;
+ uint index;
+
+ DBUG_ASSERT(thread != NULL);
+ DBUG_ASSERT(klass != NULL);
+ index= klass->m_index;
+ DBUG_ASSERT(index < mutex_class_max);
+
+ stat= &(thread->m_instr_class_wait_stats[index]);
+ return stat;
+}
+
+/**
+ Find the per-thread wait statistics for a rwlock class.
+ @param thread input thread
+ @param klass rwlock class
+ @return the per thread per rwlock class wait stat
+*/
+PFS_single_stat_chain *
+find_per_thread_rwlock_class_wait_stat(PFS_thread *thread,
+ PFS_rwlock_class *klass)
+{
+ PFS_single_stat_chain *stat;
+ uint index;
+
+ DBUG_ASSERT(thread != NULL);
+ DBUG_ASSERT(klass != NULL);
+ index= klass->m_index;
+ DBUG_ASSERT(index < rwlock_class_max);
+
+ stat= &(thread->m_instr_class_wait_stats
+ [per_thread_rwlock_class_start + index]);
+ return stat;
+}
+
+/**
+ Find the per-thread wait statistics for a condition class.
+ @param thread input thread
+ @param klass condition class
+ @return the per thread per condition class wait stat
+*/
+PFS_single_stat_chain *
+find_per_thread_cond_class_wait_stat(PFS_thread *thread,
+ PFS_cond_class *klass)
+{
+ PFS_single_stat_chain *stat;
+ uint index;
+
+ DBUG_ASSERT(thread != NULL);
+ DBUG_ASSERT(klass != NULL);
+ index= klass->m_index;
+ DBUG_ASSERT(index < cond_class_max);
+
+ stat= &(thread->m_instr_class_wait_stats
+ [per_thread_cond_class_start + index]);
+ return stat;
+}
+
+/**
+ Find the per-thread wait statistics for a file class.
+ @param thread input thread
+ @param klass file class
+ @return the per thread per file class wait stat
+*/
+PFS_single_stat_chain *
+find_per_thread_file_class_wait_stat(PFS_thread *thread,
+ PFS_file_class *klass)
+{
+ PFS_single_stat_chain *stat;
+ uint index;
+
+ DBUG_ASSERT(thread != NULL);
+ DBUG_ASSERT(klass != NULL);
+ index= klass->m_index;
+ DBUG_ASSERT(index < file_class_max);
+
+ stat= &(thread->m_instr_class_wait_stats
+ [per_thread_file_class_start + index]);
+ return stat;
+}
+
+/** Reset the wait statistics per thread. */
+void reset_per_thread_wait_stat(void)
+{
+ PFS_single_stat_chain *stat= thread_instr_class_waits_array;
+ PFS_single_stat_chain *stat_last= stat + thread_instr_class_waits_sizing;
+
+ for ( ; stat < stat_last; stat++)
+ reset_single_stat_link(stat);
+}
+
+/** Cleanup all the instruments buffers. */
+void cleanup_instruments(void)
+{
+ pfs_free(mutex_array);
+ mutex_array= NULL;
+ mutex_max= 0;
+ pfs_free(rwlock_array);
+ rwlock_array= NULL;
+ rwlock_max= 0;
+ pfs_free(cond_array);
+ cond_array= NULL;
+ cond_max= 0;
+ pfs_free(file_array);
+ file_array= NULL;
+ file_max= 0;
+ pfs_free(file_handle_array);
+ file_handle_array= NULL;
+ file_handle_max= 0;
+ pfs_free(table_array);
+ table_array= NULL;
+ table_max= 0;
+ pfs_free(thread_array);
+ thread_array= NULL;
+ thread_max= 0;
+ pfs_free(thread_history_array);
+ thread_history_array= NULL;
+ pfs_free(thread_instr_class_waits_array);
+ thread_instr_class_waits_array= NULL;
+}
+
+static uchar *filename_hash_get_key(const uchar *entry, size_t *length,
+ my_bool)
+{
+ const PFS_file * const *typed_entry;
+ const PFS_file *file;
+ const void *result;
+ typed_entry= reinterpret_cast<const PFS_file* const *> (entry);
+ DBUG_ASSERT(typed_entry != NULL);
+ file= *typed_entry;
+ DBUG_ASSERT(file != NULL);
+ *length= file->m_filename_length;
+ result= file->m_filename;
+ return const_cast<uchar*> (reinterpret_cast<const uchar*> (result));
+}
+
+/**
+ Initialize the file name hash.
+ @return 0 on success
+*/
+int init_file_hash(void)
+{
+ if (! filename_hash_inited)
+ {
+ lf_hash_init(&filename_hash, sizeof(PFS_file*), LF_HASH_UNIQUE,
+ 0, 0, filename_hash_get_key, &my_charset_bin);
+ filename_hash_inited= true;
+ }
+ return 0;
+}
+
+/** Cleanup the file name hash. */
+void cleanup_file_hash(void)
+{
+ if (filename_hash_inited)
+ {
+ lf_hash_destroy(&filename_hash);
+ filename_hash_inited= false;
+ }
+}
+
+/**
+ Create instrumentation for a mutex instance.
+ @param klass the mutex class
+ @param identity the mutex address
+ @return a mutex instance, or NULL
+*/
+PFS_mutex* create_mutex(PFS_mutex_class *klass, const void *identity)
+{
+ int pass;
+ uint i= randomized_index(identity, mutex_max);
+
+ /*
+ Pass 1: [random, mutex_max - 1]
+ Pass 2: [0, mutex_max - 1]
+ */
+ for (pass= 1; pass <= 2; i=0, pass++)
+ {
+ PFS_mutex *pfs= mutex_array + i;
+ PFS_mutex *pfs_last= mutex_array + mutex_max;
+ for ( ; pfs < pfs_last; pfs++)
+ {
+ if (pfs->m_lock.is_free())
+ {
+ if (pfs->m_lock.free_to_dirty())
+ {
+ pfs->m_identity= identity;
+ pfs->m_class= klass;
+ pfs->m_wait_stat.m_control_flag=
+ &flag_events_waits_summary_by_instance;
+ pfs->m_wait_stat.m_parent= &klass->m_wait_stat;
+ reset_single_stat_link(&pfs->m_wait_stat);
+ pfs->m_lock_stat.m_control_flag=
+ &flag_events_locks_summary_by_instance;
+ pfs->m_lock_stat.m_parent= &klass->m_lock_stat;
+ reset_single_stat_link(&pfs->m_lock_stat);
+ pfs->m_owner= NULL;
+ pfs->m_last_locked= 0;
+ pfs->m_lock.dirty_to_allocated();
+ return pfs;
+ }
+ }
+ }
+ }
+
+ mutex_lost++;
+ return NULL;
+}
+
+/**
+ Destroy instrumentation for a mutex instance.
+ @param pfs the mutex to destroy
+*/
+void destroy_mutex(PFS_mutex *pfs)
+{
+ DBUG_ASSERT(pfs != NULL);
+ pfs->m_lock.allocated_to_free();
+}
+
+/**
+ Create instrumentation for a rwlock instance.
+ @param klass the rwlock class
+ @param identity the rwlock address
+ @return a rwlock instance, or NULL
+*/
+PFS_rwlock* create_rwlock(PFS_rwlock_class *klass, const void *identity)
+{
+ int pass;
+ uint i= randomized_index(identity, rwlock_max);
+
+ /*
+ Pass 1: [random, rwlock_max - 1]
+ Pass 2: [0, rwlock_max - 1]
+ */
+ for (pass= 1; pass <= 2; i=0, pass++)
+ {
+ PFS_rwlock *pfs= rwlock_array + i;
+ PFS_rwlock *pfs_last= rwlock_array + rwlock_max;
+ for ( ; pfs < pfs_last; pfs++)
+ {
+ if (pfs->m_lock.is_free())
+ {
+ if (pfs->m_lock.free_to_dirty())
+ {
+ pfs->m_identity= identity;
+ pfs->m_class= klass;
+ pfs->m_wait_stat.m_control_flag=
+ &flag_events_waits_summary_by_instance;
+ pfs->m_wait_stat.m_parent= &klass->m_wait_stat;
+ reset_single_stat_link(&pfs->m_wait_stat);
+ pfs->m_lock.dirty_to_allocated();
+ pfs->m_read_lock_stat.m_control_flag=
+ &flag_events_locks_summary_by_instance;
+ pfs->m_read_lock_stat.m_parent= &klass->m_read_lock_stat;
+ reset_single_stat_link(&pfs->m_read_lock_stat);
+ pfs->m_write_lock_stat.m_control_flag=
+ &flag_events_locks_summary_by_instance;
+ pfs->m_write_lock_stat.m_parent= &klass->m_write_lock_stat;
+ reset_single_stat_link(&pfs->m_write_lock_stat);
+ pfs->m_writer= NULL;
+ pfs->m_readers= 0;
+ pfs->m_last_written= 0;
+ pfs->m_last_read= 0;
+ return pfs;
+ }
+ }
+ }
+ }
+
+ rwlock_lost++;
+ return NULL;
+}
+
+/**
+ Destroy instrumentation for a rwlock instance.
+ @param pfs the rwlock to destroy
+*/
+void destroy_rwlock(PFS_rwlock *pfs)
+{
+ DBUG_ASSERT(pfs != NULL);
+ pfs->m_lock.allocated_to_free();
+}
+
+/**
+ Create instrumentation for a condition instance.
+ @param klass the condition class
+ @param identity the condition address
+ @return a condition instance, or NULL
+*/
+PFS_cond* create_cond(PFS_cond_class *klass, const void *identity)
+{
+ int pass;
+ uint i= randomized_index(identity, cond_max);
+
+ /*
+ Pass 1: [random, cond_max - 1]
+ Pass 2: [0, cond_max - 1]
+ */
+ for (pass= 1; pass <= 2; i=0, pass++)
+ {
+ PFS_cond *pfs= cond_array + i;
+ PFS_cond *pfs_last= cond_array + cond_max;
+ for ( ; pfs < pfs_last; pfs++)
+ {
+ if (pfs->m_lock.is_free())
+ {
+ if (pfs->m_lock.free_to_dirty())
+ {
+ pfs->m_identity= identity;
+ pfs->m_class= klass;
+ pfs->m_cond_stat.m_signal_count= 0;
+ pfs->m_cond_stat.m_broadcast_count= 0;
+ pfs->m_wait_stat.m_control_flag=
+ &flag_events_waits_summary_by_instance;
+ pfs->m_wait_stat.m_parent= &klass->m_wait_stat;
+ reset_single_stat_link(&pfs->m_wait_stat);
+ pfs->m_lock.dirty_to_allocated();
+ return pfs;
+ }
+ }
+ }
+ }
+
+ cond_lost++;
+ return NULL;
+}
+
+/**
+ Destroy instrumentation for a condition instance.
+ @param pfs the condition to destroy
+*/
+void destroy_cond(PFS_cond *pfs)
+{
+ DBUG_ASSERT(pfs != NULL);
+ pfs->m_lock.allocated_to_free();
+}
+
+/**
+ Create instrumentation for a thread instance.
+ @param klass the thread class
+ @param identity the thread address,
+ or a value characteristic of this thread
+ @param thread_id the PROCESSLIST thread id,
+ or 0 if unknown
+ @return a thread instance, or NULL
+*/
+PFS_thread* create_thread(PFS_thread_class *klass, const void *identity,
+ ulong thread_id)
+{
+ int pass;
+ uint i= randomized_index(identity, thread_max);
+
+ /*
+ Pass 1: [random, thread_max - 1]
+ Pass 2: [0, thread_max - 1]
+ */
+ for (pass= 1; pass <= 2; i=0, pass++)
+ {
+ PFS_thread *pfs= thread_array + i;
+ PFS_thread *pfs_last= thread_array + thread_max;
+ for ( ; pfs < pfs_last; pfs++)
+ {
+ if (pfs->m_lock.is_free())
+ {
+ if (pfs->m_lock.free_to_dirty())
+ {
+ pfs->m_thread_internal_id=
+ PFS_atomic::add_u32(&thread_internal_id_counter, 1);
+ pfs->m_thread_id= thread_id;
+ pfs->m_event_id= 1;
+ pfs->m_enabled= true;
+ pfs->m_class= klass;
+ pfs->m_wait_locker_count= 0;
+ pfs->m_waits_history_full= false;
+ pfs->m_waits_history_index= 0;
+
+ PFS_single_stat_chain *stat= pfs->m_instr_class_wait_stats;
+ PFS_single_stat_chain *stat_last= stat + instr_class_per_thread;
+ for ( ; stat < stat_last; stat++)
+ reset_single_stat_link(stat);
+ pfs->m_filename_hash_pins= NULL;
+ pfs->m_table_share_hash_pins= NULL;
+ pfs->m_lock.dirty_to_allocated();
+ return pfs;
+ }
+ }
+ }
+ }
+
+ thread_lost++;
+ return NULL;
+}
+
+/**
+ Sanitize a PFS_thread pointer.
+ Validate that the PFS_thread is part of thread_array.
+ Sanitizing data is required when the data can be
+ damaged with expected race conditions, for example
+ involving EVENTS_WAITS_HISTORY_LONG.
+ @param unsafe the pointer to sanitize
+ @return a valid pointer, or NULL
+*/
+PFS_thread *sanitize_thread(PFS_thread *unsafe)
+{
+ if ((&thread_array[0] <= unsafe) &&
+ (unsafe < &thread_array[thread_max]))
+ return unsafe;
+ return NULL;
+}
+
+/**
+ Destroy instrumentation for a thread instance.
+ @param pfs the thread to destroy
+*/
+void destroy_thread(PFS_thread *pfs)
+{
+ DBUG_ASSERT(pfs != NULL);
+ if (pfs->m_filename_hash_pins)
+ {
+ lf_hash_put_pins(pfs->m_filename_hash_pins);
+ pfs->m_filename_hash_pins= NULL;
+ }
+ if (pfs->m_table_share_hash_pins)
+ {
+ lf_hash_put_pins(pfs->m_table_share_hash_pins);
+ pfs->m_table_share_hash_pins= NULL;
+ }
+ pfs->m_lock.allocated_to_free();
+}
+
+/**
+ Find or create instrumentation for a file instance by file name.
+ @param thread the executing instrumented thread
+ @param klass the file class
+ @param filename the file name
+ @param len the length in bytes of filename
+ @return a file instance, or NULL
+*/
+PFS_file*
+find_or_create_file(PFS_thread *thread, PFS_file_class *klass,
+ const char *filename, uint len)
+{
+ PFS_file *pfs;
+ int pass;
+
+ if (! filename_hash_inited)
+ {
+ /* File instrumentation can be turned off. */
+ file_lost++;
+ return NULL;
+ }
+
+ if (unlikely(thread->m_filename_hash_pins == NULL))
+ {
+ thread->m_filename_hash_pins= lf_hash_get_pins(&filename_hash);
+ if (unlikely(thread->m_filename_hash_pins == NULL))
+ {
+ file_lost++;
+ return NULL;
+ }
+ }
+
+ if (len >= sizeof(pfs->m_filename))
+ len= sizeof(pfs->m_filename) - 1;
+
+ PFS_file **entry;
+ uint retry_count= 0;
+ const uint retry_max= 3;
+search:
+ entry= reinterpret_cast<PFS_file**>
+ (lf_hash_search(&filename_hash, thread->m_filename_hash_pins,
+ filename, len));
+ if (entry && (entry != MY_ERRPTR))
+ {
+ pfs= *entry;
+ pfs->m_file_stat.m_open_count++;
+ lf_hash_search_unpin(thread->m_filename_hash_pins);
+ return pfs;
+ }
+
+ /* filename is not constant, just using it for noise on create */
+ uint i= randomized_index(filename, file_max);
+
+ /*
+ Pass 1: [random, file_max - 1]
+ Pass 2: [0, file_max - 1]
+ */
+ for (pass= 1; pass <= 2; i=0, pass++)
+ {
+ pfs= file_array + i;
+ PFS_file *pfs_last= file_array + file_max;
+
+ for ( ; pfs < pfs_last; pfs++)
+ {
+ if (pfs->m_lock.is_free())
+ {
+ if (pfs->m_lock.free_to_dirty())
+ {
+ pfs->m_class= klass;
+ strncpy(pfs->m_filename, filename, len);
+ pfs->m_filename[len]= '\0';
+ pfs->m_filename_length= len;
+ pfs->m_file_stat.m_open_count= 1;
+ pfs->m_wait_stat.m_control_flag=
+ &flag_events_waits_summary_by_instance;
+ pfs->m_wait_stat.m_parent= &klass->m_wait_stat;
+ reset_single_stat_link(&pfs->m_wait_stat);
+
+ int res;
+ res= lf_hash_insert(&filename_hash, thread->m_filename_hash_pins,
+ &pfs);
+ if (likely(res == 0))
+ {
+ pfs->m_lock.dirty_to_allocated();
+ return pfs;
+ }
+
+ pfs->m_lock.dirty_to_free();
+
+ if (res > 0)
+ {
+ /* Duplicate insert by another thread */
+ if (++retry_count > retry_max)
+ {
+ /* Avoid infinite loops */
+ file_lost++;
+ return NULL;
+ }
+ goto search;
+ }
+
+ /* OOM in lf_hash_insert */
+ file_lost++;
+ return NULL;
+ }
+ }
+ }
+ }
+
+ file_lost++;
+ return NULL;
+}
+
+/**
+ Release instrumentation for a file instance.
+ @param pfs the file to release
+*/
+void release_file(PFS_file *pfs)
+{
+ DBUG_ASSERT(pfs != NULL);
+ pfs->m_file_stat.m_open_count--;
+}
+
+/**
+ Destroy instrumentation for a file instance.
+ @param thread the executing thread instrumentation
+ @param pfs the file to destroy
+*/
+void destroy_file(PFS_thread *thread, PFS_file *pfs)
+{
+ DBUG_ASSERT(thread != NULL);
+ DBUG_ASSERT(thread->m_filename_hash_pins != NULL);
+ DBUG_ASSERT(pfs != NULL);
+ lf_hash_delete(&filename_hash, thread->m_filename_hash_pins,
+ pfs->m_filename, pfs->m_filename_length);
+ pfs->m_lock.allocated_to_free();
+}
+
+/**
+ Create instrumentation for a table instance.
+ @param share the table share
+ @param identity the table address
+ @return a table instance, or NULL
+*/
+PFS_table* create_table(PFS_table_share *share, const void *identity)
+{
+ int pass;
+ uint i= randomized_index(identity, table_max);
+
+ /*
+ Pass 1: [random, table_max - 1]
+ Pass 2: [0, table_max - 1]
+ */
+ for (pass= 1; pass <= 2; i=0, pass++)
+ {
+ PFS_table *pfs= table_array + i;
+ PFS_table *pfs_last= table_array + table_max;
+ for ( ; pfs < pfs_last; pfs++)
+ {
+ if (pfs->m_lock.is_free())
+ {
+ if (pfs->m_lock.free_to_dirty())
+ {
+ pfs->m_identity= identity;
+ pfs->m_share= share;
+ pfs->m_wait_stat.m_control_flag=
+ &flag_events_waits_summary_by_instance;
+ pfs->m_wait_stat.m_parent= &share->m_wait_stat;
+ reset_single_stat_link(&pfs->m_wait_stat);
+ pfs->m_lock.dirty_to_allocated();
+ return pfs;
+ }
+ }
+ }
+ }
+
+ table_lost++;
+ return NULL;
+}
+
+/**
+ Destroy instrumentation for a table instance.
+ @param pfs the table to destroy
+*/
+void destroy_table(PFS_table *pfs)
+{
+ DBUG_ASSERT(pfs != NULL);
+ pfs->m_lock.allocated_to_free();
+}
+
+static void reset_mutex_waits_by_instance(void)
+{
+ PFS_mutex *pfs= mutex_array;
+ PFS_mutex *pfs_last= mutex_array + mutex_max;
+
+ for ( ; pfs < pfs_last; pfs++)
+ reset_single_stat_link(&pfs->m_wait_stat);
+}
+
+static void reset_rwlock_waits_by_instance(void)
+{
+ PFS_rwlock *pfs= rwlock_array;
+ PFS_rwlock *pfs_last= rwlock_array + rwlock_max;
+
+ for ( ; pfs < pfs_last; pfs++)
+ reset_single_stat_link(&pfs->m_wait_stat);
+}
+
+static void reset_cond_waits_by_instance(void)
+{
+ PFS_cond *pfs= cond_array;
+ PFS_cond *pfs_last= cond_array + cond_max;
+
+ for ( ; pfs < pfs_last; pfs++)
+ reset_single_stat_link(&pfs->m_wait_stat);
+}
+
+static void reset_file_waits_by_instance(void)
+{
+ PFS_file *pfs= file_array;
+ PFS_file *pfs_last= file_array + file_max;
+
+ for ( ; pfs < pfs_last; pfs++)
+ reset_single_stat_link(&pfs->m_wait_stat);
+}
+
+/** Reset the wait statistics per object instance. */
+void reset_events_waits_by_instance(void)
+{
+ reset_mutex_waits_by_instance();
+ reset_rwlock_waits_by_instance();
+ reset_cond_waits_by_instance();
+ reset_file_waits_by_instance();
+}
+
+/** Reset the io statistics per file instance. */
+void reset_file_instance_io(void)
+{
+ PFS_file *pfs= file_array;
+ PFS_file *pfs_last= file_array + file_max;
+
+ for ( ; pfs < pfs_last; pfs++)
+ reset_file_stat(&pfs->m_file_stat);
+}
+
+/** @} */