/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. 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, 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ /** @file storage/perfschema/table_events_waits.cc Table EVENTS_WAITS_xxx (implementation). */ #include "my_global.h" #include "my_pthread.h" #include "table_events_waits.h" #include "pfs_global.h" #include "pfs_instr_class.h" #include "pfs_instr.h" #include "pfs_events_waits.h" #include "pfs_timer.h" #include "m_string.h" THR_LOCK table_events_waits_current::m_table_lock; PFS_engine_table_share table_events_waits_current::m_share= { { C_STRING_WITH_LEN("events_waits_current") }, &pfs_truncatable_acl, &table_events_waits_current::create, NULL, /* write_row */ &table_events_waits_current::delete_all_rows, NULL, /* get_row_count */ 1000, /* records */ sizeof(pos_events_waits_current), /* ref length */ &m_table_lock, { C_STRING_WITH_LEN("CREATE TABLE events_waits_current(" "THREAD_ID BIGINT unsigned not null," "EVENT_ID BIGINT unsigned not null," "END_EVENT_ID BIGINT unsigned," "EVENT_NAME VARCHAR(128) not null," "SOURCE VARCHAR(64)," "TIMER_START BIGINT unsigned," "TIMER_END BIGINT unsigned," "TIMER_WAIT BIGINT unsigned," "SPINS INTEGER unsigned," "OBJECT_SCHEMA VARCHAR(64)," "OBJECT_NAME VARCHAR(512)," "INDEX_NAME VARCHAR(64)," "OBJECT_TYPE VARCHAR(64)," "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null," "NESTING_EVENT_ID BIGINT unsigned," "NESTING_EVENT_TYPE ENUM('STATEMENT', 'STAGE', 'WAIT')," "OPERATION VARCHAR(32) not null," "NUMBER_OF_BYTES BIGINT," "FLAGS INTEGER unsigned)") } }; THR_LOCK table_events_waits_history::m_table_lock; PFS_engine_table_share table_events_waits_history::m_share= { { C_STRING_WITH_LEN("events_waits_history") }, &pfs_truncatable_acl, &table_events_waits_history::create, NULL, /* write_row */ &table_events_waits_history::delete_all_rows, NULL, /* get_row_count */ 1000, /* records */ sizeof(pos_events_waits_history), /* ref length */ &m_table_lock, { C_STRING_WITH_LEN("CREATE TABLE events_waits_history(" "THREAD_ID BIGINT unsigned not null," "EVENT_ID BIGINT unsigned not null," "END_EVENT_ID BIGINT unsigned," "EVENT_NAME VARCHAR(128) not null," "SOURCE VARCHAR(64)," "TIMER_START BIGINT unsigned," "TIMER_END BIGINT unsigned," "TIMER_WAIT BIGINT unsigned," "SPINS INTEGER unsigned," "OBJECT_SCHEMA VARCHAR(64)," "OBJECT_NAME VARCHAR(512)," "INDEX_NAME VARCHAR(64)," "OBJECT_TYPE VARCHAR(64)," "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null," "NESTING_EVENT_ID BIGINT unsigned," "NESTING_EVENT_TYPE ENUM('STATEMENT', 'STAGE', 'WAIT')," "OPERATION VARCHAR(32) not null," "NUMBER_OF_BYTES BIGINT," "FLAGS INTEGER unsigned)") } }; THR_LOCK table_events_waits_history_long::m_table_lock; PFS_engine_table_share table_events_waits_history_long::m_share= { { C_STRING_WITH_LEN("events_waits_history_long") }, &pfs_truncatable_acl, &table_events_waits_history_long::create, NULL, /* write_row */ &table_events_waits_history_long::delete_all_rows, NULL, /* get_row_count */ 10000, /* records */ sizeof(PFS_simple_index), /* ref length */ &m_table_lock, { C_STRING_WITH_LEN("CREATE TABLE events_waits_history_long(" "THREAD_ID BIGINT unsigned not null," "EVENT_ID BIGINT unsigned not null," "END_EVENT_ID BIGINT unsigned," "EVENT_NAME VARCHAR(128) not null," "SOURCE VARCHAR(64)," "TIMER_START BIGINT unsigned," "TIMER_END BIGINT unsigned," "TIMER_WAIT BIGINT unsigned," "SPINS INTEGER unsigned," "OBJECT_SCHEMA VARCHAR(64)," "OBJECT_NAME VARCHAR(512)," "INDEX_NAME VARCHAR(64)," "OBJECT_TYPE VARCHAR(64)," "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null," "NESTING_EVENT_ID BIGINT unsigned," "NESTING_EVENT_TYPE ENUM('STATEMENT', 'STAGE', 'WAIT')," "OPERATION VARCHAR(32) not null," "NUMBER_OF_BYTES BIGINT," "FLAGS INTEGER unsigned)") } }; table_events_waits_common::table_events_waits_common (const PFS_engine_table_share *share, void *pos) : PFS_engine_table(share, pos), m_row_exists(false) {} void table_events_waits_common::clear_object_columns() { m_row.m_object_type= NULL; m_row.m_object_type_length= 0; m_row.m_object_schema_length= 0; m_row.m_object_name_length= 0; m_row.m_index_name_length= 0; m_row.m_object_instance_addr= 0; } int table_events_waits_common::make_table_object_columns(volatile PFS_events_waits *wait) { uint safe_index; PFS_table_share *safe_table_share; safe_table_share= sanitize_table_share(wait->m_weak_table_share); if (unlikely(safe_table_share == NULL)) return 1; if (wait->m_object_type == OBJECT_TYPE_TABLE) { m_row.m_object_type= "TABLE"; m_row.m_object_type_length= 5; } else { m_row.m_object_type= "TEMPORARY TABLE"; m_row.m_object_type_length= 15; } if (safe_table_share->get_version() == wait->m_weak_version) { /* OBJECT SCHEMA */ m_row.m_object_schema_length= safe_table_share->m_schema_name_length; if (unlikely((m_row.m_object_schema_length == 0) || (m_row.m_object_schema_length > sizeof(m_row.m_object_schema)))) return 1; memcpy(m_row.m_object_schema, safe_table_share->m_schema_name, m_row.m_object_schema_length); /* OBJECT NAME */ m_row.m_object_name_length= safe_table_share->m_table_name_length; if (unlikely((m_row.m_object_name_length == 0) || (m_row.m_object_name_length > sizeof(m_row.m_object_name)))) return 1; memcpy(m_row.m_object_name, safe_table_share->m_table_name, m_row.m_object_name_length); /* INDEX NAME */ safe_index= wait->m_index; uint safe_key_count= sanitize_index_count(safe_table_share->m_key_count); if (safe_index < safe_key_count) { PFS_table_key *key= & safe_table_share->m_keys[safe_index]; m_row.m_index_name_length= key->m_name_length; if (unlikely((m_row.m_index_name_length == 0) || (m_row.m_index_name_length > sizeof(m_row.m_index_name)))) return 1; memcpy(m_row.m_index_name, key->m_name, m_row.m_index_name_length); } else m_row.m_index_name_length= 0; } else { m_row.m_object_schema_length= 0; m_row.m_object_name_length= 0; m_row.m_index_name_length= 0; } m_row.m_object_instance_addr= (intptr) wait->m_object_instance_addr; return 0; } int table_events_waits_common::make_file_object_columns(volatile PFS_events_waits *wait) { PFS_file *safe_file; safe_file= sanitize_file(wait->m_weak_file); if (unlikely(safe_file == NULL)) return 1; m_row.m_object_type= "FILE"; m_row.m_object_type_length= 4; m_row.m_object_schema_length= 0; m_row.m_object_instance_addr= (intptr) wait->m_object_instance_addr; if (safe_file->get_version() == wait->m_weak_version) { /* OBJECT NAME */ m_row.m_object_name_length= safe_file->m_filename_length; if (unlikely((m_row.m_object_name_length == 0) || (m_row.m_object_name_length > sizeof(m_row.m_object_name)))) return 1; memcpy(m_row.m_object_name, safe_file->m_filename, m_row.m_object_name_length); } else { m_row.m_object_name_length= 0; } m_row.m_index_name_length= 0; return 0; } int table_events_waits_common::make_socket_object_columns(volatile PFS_events_waits *wait) { PFS_socket *safe_socket; safe_socket= sanitize_socket(wait->m_weak_socket); if (unlikely(safe_socket == NULL)) return 1; m_row.m_object_type= "SOCKET"; m_row.m_object_type_length= 6; m_row.m_object_schema_length= 0; m_row.m_object_instance_addr= (intptr) wait->m_object_instance_addr; if (safe_socket->get_version() == wait->m_weak_version) { /* Convert port number to string, include delimiter in port name length */ uint port; char port_str[128]; char ip_str[INET6_ADDRSTRLEN+1]; /* "ip_length" was "ip_len" originally. but it conflicted with some macro on AIX. Renamed. */ uint ip_length= 0; port_str[0]= ':'; /* Get the IP address and port number */ ip_length= pfs_get_socket_address(ip_str, sizeof(ip_str), &port, &safe_socket->m_sock_addr, safe_socket->m_addr_len); /* Convert port number to a string (length includes ':') */ int port_len= (int)(int10_to_str(port, (port_str+1), 10) - port_str + 1); /* OBJECT NAME */ m_row.m_object_name_length= ip_length + port_len; if (unlikely((m_row.m_object_name_length == 0) || (m_row.m_object_name_length > sizeof(m_row.m_object_name)))) return 1; char *name= m_row.m_object_name; memcpy(name, ip_str, ip_length); memcpy(name + ip_length, port_str, port_len); } else { m_row.m_object_name_length= 0; } m_row.m_index_name_length= 0; return 0; } /** Build a row. @param thread_own_wait True if the memory for the wait is owned by pfs_thread @param pfs_thread the thread the cursor is reading @param wait the wait the cursor is reading */ void table_events_waits_common::make_row(bool thread_own_wait, PFS_thread *pfs_thread, volatile PFS_events_waits *wait) { pfs_lock lock; PFS_thread *safe_thread; PFS_instr_class *safe_class; const char *base; const char *safe_source_file; enum_timer_name timer_name= wait_timer; ulonglong timer_end; m_row_exists= false; safe_thread= sanitize_thread(pfs_thread); if (unlikely(safe_thread == NULL)) return; /* Protect this reader against a thread termination */ if (thread_own_wait) safe_thread->m_lock.begin_optimistic_lock(&lock); /* Design choice: We could have used a pfs_lock in PFS_events_waits here, to protect the reader from concurrent event generation, but this leads to too many pfs_lock atomic operations each time an event is recorded: - 1 dirty() + 1 allocated() per event start, for EVENTS_WAITS_CURRENT - 1 dirty() + 1 allocated() per event end, for EVENTS_WAITS_CURRENT - 1 dirty() + 1 allocated() per copy to EVENTS_WAITS_HISTORY - 1 dirty() + 1 allocated() per copy to EVENTS_WAITS_HISTORY_LONG or 8 atomics per recorded event. The problem is that we record a *lot* of events ... This code is prepared to accept *dirty* records, and sanitizes all the data before returning a row. */ /* PFS_events_waits::m_class needs to be sanitized, for race conditions when this code: - reads a new value in m_wait_class, - reads an old value in m_class. */ switch (wait->m_wait_class) { case WAIT_CLASS_IDLE: clear_object_columns(); safe_class= sanitize_idle_class(wait->m_class); timer_name= idle_timer; break; case WAIT_CLASS_MUTEX: clear_object_columns(); safe_class= sanitize_mutex_class((PFS_mutex_class*) wait->m_class); break; case WAIT_CLASS_RWLOCK: clear_object_columns(); safe_class= sanitize_rwlock_class((PFS_rwlock_class*) wait->m_class); break; case WAIT_CLASS_COND: clear_object_columns(); safe_class= sanitize_cond_class((PFS_cond_class*) wait->m_class); break; case WAIT_CLASS_TABLE: if (make_table_object_columns(wait)) return; safe_class= sanitize_table_class(wait->m_class); break; case WAIT_CLASS_FILE: if (make_file_object_columns(wait)) return; safe_class= sanitize_file_class((PFS_file_class*) wait->m_class); break; case WAIT_CLASS_SOCKET: if (make_socket_object_columns(wait)) return; safe_class= sanitize_socket_class((PFS_socket_class*) wait->m_class); break; case NO_WAIT_CLASS: default: return; } if (unlikely(safe_class == NULL)) return; m_row.m_thread_internal_id= safe_thread->m_thread_internal_id; m_row.m_event_id= wait->m_event_id; m_row.m_end_event_id= wait->m_end_event_id; m_row.m_nesting_event_id= wait->m_nesting_event_id; m_row.m_nesting_event_type= wait->m_nesting_event_type; get_normalizer(safe_class); if (m_row.m_end_event_id == 0) { timer_end= get_timer_raw_value(timer_name); } else { timer_end= wait->m_timer_end; } m_normalizer->to_pico(wait->m_timer_start, timer_end, & m_row.m_timer_start, & m_row.m_timer_end, & m_row.m_timer_wait); m_row.m_name= safe_class->m_name; m_row.m_name_length= safe_class->m_name_length; /* We are assuming this pointer is sane, since it comes from __FILE__. */ safe_source_file= wait->m_source_file; if (unlikely(safe_source_file == NULL)) return; base= base_name(wait->m_source_file); m_row.m_source_length= my_snprintf(m_row.m_source, sizeof(m_row.m_source), "%s:%d", base, wait->m_source_line); if (m_row.m_source_length > sizeof(m_row.m_source)) m_row.m_source_length= sizeof(m_row.m_source); m_row.m_operation= wait->m_operation; m_row.m_number_of_bytes= wait->m_number_of_bytes; m_row.m_flags= wait->m_flags; if (thread_own_wait) { if (safe_thread->m_lock.end_optimistic_lock(&lock)) m_row_exists= true; } else { /* For EVENTS_WAITS_HISTORY_LONG (thread_own_wait is false), the wait record is always valid, because it is not stored in memory owned by pfs_thread. Even when the thread terminated, the record is mostly readable, so this record is displayed. */ m_row_exists= true; } } /** Operations names map, as displayed in the 'OPERATION' column. Indexed by enum_operation_type - 1. Note: enum_operation_type contains a more precise definition, since more details are needed internally by the instrumentation. Different similar operations (CLOSE vs STREAMCLOSE) are displayed with the same name 'close'. */ static const LEX_STRING operation_names_map[]= { /* Mutex operations */ { C_STRING_WITH_LEN("lock") }, { C_STRING_WITH_LEN("try_lock") }, /* RWLock operations */ { C_STRING_WITH_LEN("read_lock") }, { C_STRING_WITH_LEN("write_lock") }, { C_STRING_WITH_LEN("try_read_lock") }, { C_STRING_WITH_LEN("try_write_lock") }, /* Condition operations */ { C_STRING_WITH_LEN("wait") }, { C_STRING_WITH_LEN("timed_wait") }, /* File operations */ { C_STRING_WITH_LEN("create") }, { C_STRING_WITH_LEN("create") }, /* create tmp */ { C_STRING_WITH_LEN("open") }, { C_STRING_WITH_LEN("open") }, /* stream open */ { C_STRING_WITH_LEN("close") }, { C_STRING_WITH_LEN("close") }, /* stream close */ { C_STRING_WITH_LEN("read") }, { C_STRING_WITH_LEN("write") }, { C_STRING_WITH_LEN("seek") }, { C_STRING_WITH_LEN("tell") }, { C_STRING_WITH_LEN("flush") }, { C_STRING_WITH_LEN("stat") }, { C_STRING_WITH_LEN("stat") }, /* fstat */ { C_STRING_WITH_LEN("chsize") }, { C_STRING_WITH_LEN("delete") }, { C_STRING_WITH_LEN("rename") }, { C_STRING_WITH_LEN("sync") }, /* Table io operations */ { C_STRING_WITH_LEN("fetch") }, { C_STRING_WITH_LEN("insert") }, /* write row */ { C_STRING_WITH_LEN("update") }, /* update row */ { C_STRING_WITH_LEN("delete") }, /* delete row */ /* Table lock operations */ { C_STRING_WITH_LEN("read normal") }, { C_STRING_WITH_LEN("read with shared locks") }, { C_STRING_WITH_LEN("read high priority") }, { C_STRING_WITH_LEN("read no inserts") }, { C_STRING_WITH_LEN("write allow write") }, { C_STRING_WITH_LEN("write concurrent insert") }, { C_STRING_WITH_LEN("write delayed") }, { C_STRING_WITH_LEN("write low priority") }, { C_STRING_WITH_LEN("write normal") }, { C_STRING_WITH_LEN("read external") }, { C_STRING_WITH_LEN("write external") }, /* Socket operations */ { C_STRING_WITH_LEN("create") }, { C_STRING_WITH_LEN("connect") }, { C_STRING_WITH_LEN("bind") }, { C_STRING_WITH_LEN("close") }, { C_STRING_WITH_LEN("send") }, { C_STRING_WITH_LEN("recv") }, { C_STRING_WITH_LEN("sendto") }, { C_STRING_WITH_LEN("recvfrom") }, { C_STRING_WITH_LEN("sendmsg") }, { C_STRING_WITH_LEN("recvmsg") }, { C_STRING_WITH_LEN("seek") }, { C_STRING_WITH_LEN("opt") }, { C_STRING_WITH_LEN("stat") }, { C_STRING_WITH_LEN("shutdown") }, { C_STRING_WITH_LEN("select") }, /* Idle operations */ { C_STRING_WITH_LEN("idle") } }; int table_events_waits_common::read_row_values(TABLE *table, unsigned char *buf, Field **fields, bool read_all) { Field *f; const LEX_STRING *operation; compile_time_assert(COUNT_OPERATION_TYPE == array_elements(operation_names_map)); if (unlikely(! m_row_exists)) return HA_ERR_RECORD_DELETED; /* Set the null bits */ DBUG_ASSERT(table->s->null_bytes == 2); buf[0]= 0; buf[1]= 0; /* Some columns are unreliable, because they are joined with other buffers, which could have changed and been reused for something else. These columns are: - THREAD_ID (m_thread joins with PFS_thread), - SCHEMA_NAME (m_schema_name joins with PFS_table_share) - OBJECT_NAME (m_object_name joins with PFS_table_share) */ for (; (f= *fields) ; fields++) { if (read_all || bitmap_is_set(table->read_set, f->field_index)) { switch(f->field_index) { case 0: /* THREAD_ID */ set_field_ulonglong(f, m_row.m_thread_internal_id); break; case 1: /* EVENT_ID */ set_field_ulonglong(f, m_row.m_event_id); break; case 2: /* END_EVENT_ID */ if (m_row.m_end_event_id > 0) set_field_ulonglong(f, m_row.m_end_event_id - 1); else f->set_null(); break; case 3: /* EVENT_NAME */ set_field_varchar_utf8(f, m_row.m_name, m_row.m_name_length); break; case 4: /* SOURCE */ set_field_varchar_utf8(f, m_row.m_source, m_row.m_source_length); break; case 5: /* TIMER_START */ if (m_row.m_timer_start != 0) set_field_ulonglong(f, m_row.m_timer_start); else f->set_null(); break; case 6: /* TIMER_END */ if (m_row.m_timer_end != 0) set_field_ulonglong(f, m_row.m_timer_end); else f->set_null(); break; case 7: /* TIMER_WAIT */ if (m_row.m_timer_wait != 0) set_field_ulonglong(f, m_row.m_timer_wait); else f->set_null(); break; case 8: /* SPINS */ f->set_null(); break; case 9: /* OBJECT_SCHEMA */ if (m_row.m_object_schema_length > 0) { set_field_varchar_utf8(f, m_row.m_object_schema, m_row.m_object_schema_length); } else f->set_null(); break; case 10: /* OBJECT_NAME */ if (m_row.m_object_name_length > 0) { set_field_varchar_utf8(f, m_row.m_object_name, m_row.m_object_name_length); } else f->set_null(); break; case 11: /* INDEX_NAME */ if (m_row.m_index_name_length > 0) { set_field_varchar_utf8(f, m_row.m_index_name, m_row.m_index_name_length); } else f->set_null(); break; case 12: /* OBJECT_TYPE */ if (m_row.m_object_type) { set_field_varchar_utf8(f, m_row.m_object_type, m_row.m_object_type_length); } else f->set_null(); break; case 13: /* OBJECT_INSTANCE */ set_field_ulonglong(f, m_row.m_object_instance_addr); break; case 14: /* NESTING_EVENT_ID */ if (m_row.m_nesting_event_id != 0) set_field_ulonglong(f, m_row.m_nesting_event_id); else f->set_null(); break; case 15: /* NESTING_EVENT_TYPE */ if (m_row.m_nesting_event_id != 0) set_field_enum(f, m_row.m_nesting_event_type); else f->set_null(); break; case 16: /* OPERATION */ operation= &operation_names_map[(int) m_row.m_operation - 1]; set_field_varchar_utf8(f, operation->str, operation->length); break; case 17: /* NUMBER_OF_BYTES */ if ((m_row.m_operation == OPERATION_TYPE_FILEREAD) || (m_row.m_operation == OPERATION_TYPE_FILEWRITE) || (m_row.m_operation == OPERATION_TYPE_FILECHSIZE) || (m_row.m_operation == OPERATION_TYPE_SOCKETSEND) || (m_row.m_operation == OPERATION_TYPE_SOCKETRECV) || (m_row.m_operation == OPERATION_TYPE_SOCKETSENDTO) || (m_row.m_operation == OPERATION_TYPE_SOCKETRECVFROM)) set_field_ulonglong(f, m_row.m_number_of_bytes); else f->set_null(); break; case 18: /* FLAGS */ f->set_null(); break; default: DBUG_ASSERT(false); } } } return 0; } PFS_engine_table* table_events_waits_current::create(void) { return new table_events_waits_current(); } table_events_waits_current::table_events_waits_current() : table_events_waits_common(&m_share, &m_pos), m_pos(), m_next_pos() {} void table_events_waits_current::reset_position(void) { m_pos.reset(); m_next_pos.reset(); } int table_events_waits_current::rnd_next(void) { PFS_thread *pfs_thread; PFS_events_waits *wait; for (m_pos.set_at(&m_next_pos); m_pos.m_index_1 < thread_max; m_pos.next_thread()) { pfs_thread= &thread_array[m_pos.m_index_1]; if (! pfs_thread->m_lock.is_populated()) { /* This thread does not exist */ continue; } /* We do not show nested events for now, this will be revised with TABLE io */ // #define ONLY_SHOW_ONE_WAIT #ifdef ONLY_SHOW_ONE_WAIT if (m_pos.m_index_2 >= 1) continue; #else /* m_events_waits_stack[0] is a dummy record */ PFS_events_waits *top_wait = &pfs_thread->m_events_waits_stack[WAIT_STACK_BOTTOM]; wait= &pfs_thread->m_events_waits_stack[m_pos.m_index_2 + WAIT_STACK_BOTTOM]; PFS_events_waits *safe_current = pfs_thread->m_events_waits_current; if (safe_current == top_wait) { /* Display the last top level wait, when completed */ if (m_pos.m_index_2 >= 1) continue; } else { /* Display all pending waits, when in progress */ if (wait >= safe_current) continue; } #endif if (wait->m_wait_class == NO_WAIT_CLASS) { /* This locker does not exist. There can not be more lockers in the stack, skip to the next thread */ continue; } make_row(true, pfs_thread, wait); /* Next iteration, look for the next locker in this thread */ m_next_pos.set_after(&m_pos); return 0; } return HA_ERR_END_OF_FILE; } int table_events_waits_current::rnd_pos(const void *pos) { PFS_thread *pfs_thread; PFS_events_waits *wait; set_position(pos); DBUG_ASSERT(m_pos.m_index_1 < thread_max); pfs_thread= &thread_array[m_pos.m_index_1]; if (! pfs_thread->m_lock.is_populated()) return HA_ERR_RECORD_DELETED; #ifdef ONLY_SHOW_ONE_WAIT if (m_pos.m_index_2 >= 1) return HA_ERR_RECORD_DELETED; #else /* m_events_waits_stack[0] is a dummy record */ PFS_events_waits *top_wait = &pfs_thread->m_events_waits_stack[WAIT_STACK_BOTTOM]; wait= &pfs_thread->m_events_waits_stack[m_pos.m_index_2 + WAIT_STACK_BOTTOM]; PFS_events_waits *safe_current = pfs_thread->m_events_waits_current; if (safe_current == top_wait) { /* Display the last top level wait, when completed */ if (m_pos.m_index_2 >= 1) return HA_ERR_RECORD_DELETED; } else { /* Display all pending waits, when in progress */ if (wait >= safe_current) return HA_ERR_RECORD_DELETED; } #endif DBUG_ASSERT(m_pos.m_index_2 < WAIT_STACK_LOGICAL_SIZE); if (wait->m_wait_class == NO_WAIT_CLASS) return HA_ERR_RECORD_DELETED; make_row(true, pfs_thread, wait); return 0; } int table_events_waits_current::delete_all_rows(void) { reset_events_waits_current(); return 0; } PFS_engine_table* table_events_waits_history::create(void) { return new table_events_waits_history(); } table_events_waits_history::table_events_waits_history() : table_events_waits_common(&m_share, &m_pos), m_pos(), m_next_pos() {} void table_events_waits_history::reset_position(void) { m_pos.reset(); m_next_pos.reset(); } int table_events_waits_history::rnd_next(void) { PFS_thread *pfs_thread; PFS_events_waits *wait; if (events_waits_history_per_thread == 0) return HA_ERR_END_OF_FILE; for (m_pos.set_at(&m_next_pos); m_pos.m_index_1 < thread_max; m_pos.next_thread()) { pfs_thread= &thread_array[m_pos.m_index_1]; if (! pfs_thread->m_lock.is_populated()) { /* This thread does not exist */ continue; } if (m_pos.m_index_2 >= events_waits_history_per_thread) { /* This thread does not have more (full) history */ continue; } if ( ! pfs_thread->m_waits_history_full && (m_pos.m_index_2 >= pfs_thread->m_waits_history_index)) { /* This thread does not have more (not full) history */ continue; } if (pfs_thread->m_waits_history[m_pos.m_index_2].m_wait_class == NO_WAIT_CLASS) { /* This locker does not exist. There can not be more lockers in the stack, skip to the next thread */ continue; } wait= &pfs_thread->m_waits_history[m_pos.m_index_2]; make_row(true, pfs_thread, wait); /* Next iteration, look for the next history in this thread */ m_next_pos.set_after(&m_pos); return 0; } return HA_ERR_END_OF_FILE; } int table_events_waits_history::rnd_pos(const void *pos) { PFS_thread *pfs_thread; PFS_events_waits *wait; DBUG_ASSERT(events_waits_history_per_thread != 0); set_position(pos); DBUG_ASSERT(m_pos.m_index_1 < thread_max); pfs_thread= &thread_array[m_pos.m_index_1]; if (! pfs_thread->m_lock.is_populated()) return HA_ERR_RECORD_DELETED; DBUG_ASSERT(m_pos.m_index_2 < events_waits_history_per_thread); if ( ! pfs_thread->m_waits_history_full && (m_pos.m_index_2 >= pfs_thread->m_waits_history_index)) return HA_ERR_RECORD_DELETED; wait= &pfs_thread->m_waits_history[m_pos.m_index_2]; if (wait->m_wait_class == NO_WAIT_CLASS) return HA_ERR_RECORD_DELETED; make_row(true, pfs_thread, wait); return 0; } int table_events_waits_history::delete_all_rows(void) { reset_events_waits_history(); return 0; } PFS_engine_table* table_events_waits_history_long::create(void) { return new table_events_waits_history_long(); } table_events_waits_history_long::table_events_waits_history_long() : table_events_waits_common(&m_share, &m_pos), m_pos(0), m_next_pos(0) {} void table_events_waits_history_long::reset_position(void) { m_pos.m_index= 0; m_next_pos.m_index= 0; } int table_events_waits_history_long::rnd_next(void) { PFS_events_waits *wait; uint limit; if (events_waits_history_long_size == 0) return HA_ERR_END_OF_FILE; if (events_waits_history_long_full) limit= events_waits_history_long_size; else limit= events_waits_history_long_index % events_waits_history_long_size; for (m_pos.set_at(&m_next_pos); m_pos.m_index < limit; m_pos.next()) { wait= &events_waits_history_long_array[m_pos.m_index]; if (wait->m_wait_class != NO_WAIT_CLASS) { make_row(false, wait->m_thread, wait); /* Next iteration, look for the next entry */ m_next_pos.set_after(&m_pos); return 0; } } return HA_ERR_END_OF_FILE; } int table_events_waits_history_long::rnd_pos(const void *pos) { PFS_events_waits *wait; uint limit; if (events_waits_history_long_size == 0) return HA_ERR_RECORD_DELETED; set_position(pos); if (events_waits_history_long_full) limit= events_waits_history_long_size; else limit= events_waits_history_long_index % events_waits_history_long_size; if (m_pos.m_index >= limit) return HA_ERR_RECORD_DELETED; wait= &events_waits_history_long_array[m_pos.m_index]; if (wait->m_wait_class == NO_WAIT_CLASS) return HA_ERR_RECORD_DELETED; make_row(false, wait->m_thread, wait); return 0; } int table_events_waits_history_long::delete_all_rows(void) { reset_events_waits_history_long(); return 0; }