/* Copyright (c) 2008, 2017, 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 */ #ifndef PFS_INSTR_H #define PFS_INSTR_H /** @file storage/perfschema/pfs_instr.h Performance schema instruments (declarations). */ struct PFS_mutex_class; struct PFS_rwlock_class; struct PFS_cond_class; struct PFS_file_class; struct PFS_table_share; struct PFS_thread_class; struct PFS_socket_class; class PFS_opaque_container_page; class THD; #include "my_global.h" #include "my_thread_os_id.h" #ifdef _WIN32 #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #include "my_compiler.h" #include "pfs_lock.h" #include "pfs_stat.h" #include "pfs_instr_class.h" #include "pfs_events_waits.h" #include "pfs_events_stages.h" #include "pfs_events_statements.h" #include "pfs_events_transactions.h" #include "pfs_server.h" #include "lf.h" #include "pfs_con_slice.h" #include "pfs_column_types.h" #include "mdl.h" #include "violite.h" /* enum_vio_type */ extern PFS_single_stat *thread_instr_class_waits_array_start; extern PFS_single_stat *thread_instr_class_waits_array_end; extern my_bool show_compatibility_56; /** @addtogroup Performance_schema_buffers @{ */ struct PFS_thread; struct PFS_host; struct PFS_user; struct PFS_account; /** Base structure for wait instruments. */ struct PFS_instr { /** Internal lock. */ pfs_lock m_lock; /** Enabled flag. */ bool m_enabled; /** Timed flag. */ bool m_timed; /** Container page. */ PFS_opaque_container_page *m_page; }; /** Instrumented mutex implementation. @see PSI_mutex. */ struct PFS_ALIGNED PFS_mutex : public PFS_instr { /** Mutex identity, typically a pthread_mutex_t. */ const void *m_identity; /** Mutex class. */ PFS_mutex_class *m_class; /** Instrument statistics. */ PFS_mutex_stat m_mutex_stat; /** Current owner. */ PFS_thread *m_owner; /** Timestamp of the last lock. This statistic is not exposed in user visible tables yet. */ ulonglong m_last_locked; }; /** Instrumented rwlock implementation. @see PSI_rwlock. */ struct PFS_ALIGNED PFS_rwlock : public PFS_instr { /** RWLock identity, typically a pthread_rwlock_t. */ const void *m_identity; /** RWLock class. */ PFS_rwlock_class *m_class; /** Instrument statistics. */ PFS_rwlock_stat m_rwlock_stat; /** Current writer thread. */ PFS_thread *m_writer; /** Current count of readers. */ uint m_readers; /** Timestamp of the last write. This statistic is not exposed in user visible tables yet. */ ulonglong m_last_written; /** Timestamp of the last read. This statistic is not exposed in user visible tables yet. */ ulonglong m_last_read; }; /** Instrumented cond implementation. @see PSI_cond. */ struct PFS_ALIGNED PFS_cond : public PFS_instr { /** Condition identity, typically a pthread_cond_t. */ const void *m_identity; /** Condition class. */ PFS_cond_class *m_class; /** Condition instance usage statistics. */ PFS_cond_stat m_cond_stat; }; /** Instrumented File and FILE implementation. @see PSI_file. */ struct PFS_ALIGNED PFS_file : public PFS_instr { uint32 get_version() { return m_lock.get_version(); } /** File identity */ const void *m_identity; /** File name. */ char m_filename[FN_REFLEN]; /** File name length in bytes. */ uint m_filename_length; /** File class. */ PFS_file_class *m_class; /** File usage statistics. */ PFS_file_stat m_file_stat; /** True if a temporary file. */ bool m_temporary; }; /** Instrumented table implementation. @see PSI_table. */ struct PFS_ALIGNED PFS_table { /** True if table io instrumentation is enabled. This flag is computed. */ bool m_io_enabled; /** True if table lock instrumentation is enabled. This flag is computed. */ bool m_lock_enabled; /** True if table io instrumentation is timed. This flag is computed. */ bool m_io_timed; /** True if table lock instrumentation is timed. This flag is computed. */ bool m_lock_timed; /** True if table io statistics have been collected. */ bool m_has_io_stats; /** True if table lock statistics have been collected. */ bool m_has_lock_stats; public: /** Aggregate this table handle statistics to the parents. Only use this method for handles owned by the calling code. @sa sanitized_aggregate. */ void aggregate(const TABLE_SHARE *server_share) { if (m_has_io_stats) { safe_aggregate_io(server_share, & m_table_stat, m_share); m_has_io_stats= false; } if (m_has_lock_stats) { safe_aggregate_lock(& m_table_stat, m_share); m_has_lock_stats= false; } } /** Aggregate this table handle statistics to the parents. This method is safe to call on handles not owned by the calling code. @sa aggregate @sa sanitized_aggregate_io @sa sanitized_aggregate_lock */ void sanitized_aggregate(void); /** Aggregate this table handle io statistics to the parents. This method is safe to call on handles not owned by the calling code. */ void sanitized_aggregate_io(void); /** Aggregate this table handle lock statistics to the parents. This method is safe to call on handles not owned by the calling code. */ void sanitized_aggregate_lock(void); /** Internal lock. */ pfs_lock m_lock; /** Thread Owner. */ PFS_thread *m_thread_owner; /** Event Owner. */ ulonglong m_owner_event_id; /** Table share. */ PFS_table_share *m_share; /** Table identity, typically a handler. */ const void *m_identity; /** Table statistics. */ PFS_table_stat m_table_stat; /** Current internal lock. */ PFS_TL_LOCK_TYPE m_internal_lock; /** Current external lock. */ PFS_TL_LOCK_TYPE m_external_lock; /** Container page. */ PFS_opaque_container_page *m_page; private: static void safe_aggregate_io(const TABLE_SHARE *optional_server_share, PFS_table_stat *stat, PFS_table_share *safe_share); static void safe_aggregate_lock(PFS_table_stat *stat, PFS_table_share *safe_share); }; /** Instrumented socket implementation. @see PSI_socket. */ struct PFS_ALIGNED PFS_socket : public PFS_instr { uint32 get_version() { return m_lock.get_version(); } /** Socket identity, typically int */ const void *m_identity; /** Owning thread, if applicable */ PFS_thread *m_thread_owner; /** Socket file descriptor */ uint m_fd; /** Raw socket address */ struct sockaddr_storage m_sock_addr; /** Length of address */ socklen_t m_addr_len; /** Idle flag. */ bool m_idle; /** Socket class. */ PFS_socket_class *m_class; /** Socket usage statistics. */ PFS_socket_stat m_socket_stat; }; /** Instrumented metadata lock implementation. @see PSI_metadata_lock. */ struct PFS_ALIGNED PFS_metadata_lock : public PFS_instr { uint32 get_version() { return m_lock.get_version(); } /** Lock identity. */ const void *m_identity; MDL_key m_mdl_key; opaque_mdl_type m_mdl_type; opaque_mdl_duration m_mdl_duration; opaque_mdl_status m_mdl_status; const char *m_src_file; uint m_src_line; ulonglong m_owner_thread_id; ulonglong m_owner_event_id; }; /** @def WAIT_STACK_LOGICAL_SIZE Maximum number of nested waits. Some waits, such as: - "wait/io/table/sql/handler" - "wait/lock/table/sql/handler" are implemented by calling code in a storage engine, that can cause nested waits (file io, mutex, ...) Because of partitioned tables, a table io event (on the whole table) can contain a nested table io event (on a partition). Because of additional debug instrumentation, waiting on what looks like a "mutex" (safe_mutex, innodb sync0sync, ...) can cause nested waits to be recorded. For example, a wait on innodb mutexes can lead to: - wait/sync/mutex/innobase/some_mutex - wait/sync/mutex/innobase/sync0sync - wait/sync/mutex/innobase/os0sync The max depth of the event stack must be sufficient for these low level details to be visible. */ #define WAIT_STACK_LOGICAL_SIZE 5 /** @def WAIT_STACK_BOTTOM Maximum number dummy waits records. One dummy record is reserved for the parent stage / statement / transaction, at the bottom of the wait stack. */ #define WAIT_STACK_BOTTOM 1 /** @def WAIT_STACK_SIZE Physical size of the waits stack */ #define WAIT_STACK_SIZE (WAIT_STACK_BOTTOM + WAIT_STACK_LOGICAL_SIZE) /** Max size of the statements stack. */ extern uint statement_stack_max; /** Max size of the digests token array. */ extern size_t pfs_max_digest_length; /** Max size of SQL TEXT. */ extern size_t pfs_max_sqltext; /** Instrumented thread implementation. @see PSI_thread. */ struct PFS_ALIGNED PFS_thread : PFS_connection_slice { static PFS_thread* get_current_thread(void); /** Thread instrumentation flag. */ bool m_enabled; /** Thread history instrumentation flag. */ bool m_history; /** Derived flag flag_events_waits_history, per thread. Cached computation of TABLE SETUP_CONSUMERS[EVENTS_WAITS_HISTORY].ENABLED == 'YES' AND TABLE THREADS[THREAD_ID].HISTORY == 'YES' */ bool m_flag_events_waits_history; /** Derived flag flag_events_waits_history_long, per thread. Cached computation of TABLE SETUP_CONSUMERS[EVENTS_WAITS_HISTORY_LONG].ENABLED == 'YES' AND TABLE THREADS[THREAD_ID].HISTORY == 'YES' */ bool m_flag_events_waits_history_long; /** Derived flag flag_events_stages_history, per thread. Cached computation of TABLE SETUP_CONSUMERS[EVENTS_STAGES_HISTORY].ENABLED == 'YES' AND TABLE THREADS[THREAD_ID].HISTORY == 'YES' */ bool m_flag_events_stages_history; /** Derived flag flag_events_stages_history_long, per thread. Cached computation of TABLE SETUP_CONSUMERS[EVENTS_STAGES_HISTORY_LONG].ENABLED == 'YES' AND TABLE THREADS[THREAD_ID].HISTORY == 'YES' */ bool m_flag_events_stages_history_long; /** Derived flag flag_events_statements_history, per thread. Cached computation of TABLE SETUP_CONSUMERS[EVENTS_STATEMENTS_HISTORY].ENABLED == 'YES' AND TABLE THREADS[THREAD_ID].HISTORY == 'YES' */ bool m_flag_events_statements_history; /** Derived flag flag_events_statements_history_long, per thread. Cached computation of TABLE SETUP_CONSUMERS[EVENTS_STATEMENTS_HISTORY_LONG].ENABLED == 'YES' AND TABLE THREADS[THREAD_ID].HISTORY == 'YES' */ bool m_flag_events_statements_history_long; /** Derived flag flag_events_transactions_history, per thread. Cached computation of TABLE SETUP_CONSUMERS[EVENTS_TRANSACTIONS_HISTORY].ENABLED == 'YES' AND TABLE THREADS[THREAD_ID].HISTORY == 'YES' */ bool m_flag_events_transactions_history; /** Derived flag flag_events_transactions_history_long, per thread. Cached computation of TABLE SETUP_CONSUMERS[EVENTS_TRANSACTIONS_HISTORY_LONG].ENABLED == 'YES' AND TABLE THREADS[THREAD_ID].HISTORY == 'YES' */ bool m_flag_events_transactions_history_long; /** Current wait event in the event stack. */ PFS_events_waits *m_events_waits_current; /** Event ID counter */ ulonglong m_event_id; /** Internal lock. This lock is exclusively used to protect against races when creating and destroying PFS_thread. Do not use this lock to protect thread attributes, use one of @c m_stmt_lock or @c m_session_lock instead. */ pfs_lock m_lock; /** Pins for filename_hash. */ LF_PINS *m_filename_hash_pins; /** Pins for table_share_hash. */ LF_PINS *m_table_share_hash_pins; /** Pins for setup_actor_hash. */ LF_PINS *m_setup_actor_hash_pins; /** Pins for setup_object_hash. */ LF_PINS *m_setup_object_hash_pins; /** Pins for host_hash. */ LF_PINS *m_host_hash_pins; /** Pins for user_hash. */ LF_PINS *m_user_hash_pins; /** Pins for account_hash. */ LF_PINS *m_account_hash_pins; /** Pins for digest_hash. */ LF_PINS *m_digest_hash_pins; /** Pins for routine_hash. */ LF_PINS *m_program_hash_pins; /** Internal thread identifier, unique. */ ulonglong m_thread_internal_id; /** Parent internal thread identifier. */ ulonglong m_parent_thread_internal_id; /** External (SHOW PROCESSLIST) thread identifier, not unique. */ ulong m_processlist_id; /** External (Operating system) thread identifier, if any. */ my_thread_os_id_t m_thread_os_id; /** Thread class. */ PFS_thread_class *m_class; /** Stack of events waits. This member holds the data for the table PERFORMANCE_SCHEMA.EVENTS_WAITS_CURRENT. Note that stack[0] is a dummy record that represents the parent stage/statement/transaction. For example, assuming the following tree: - STAGE ID 100 - WAIT ID 101, parent STAGE 100 - WAIT ID 102, parent wait 101 the data in the stack will be: stack[0].m_event_id= 100, set by the stage instrumentation stack[0].m_event_type= STAGE, set by the stage instrumentation stack[0].m_nesting_event_id= unused stack[0].m_nesting_event_type= unused stack[1].m_event_id= 101 stack[1].m_event_type= WAIT stack[1].m_nesting_event_id= stack[0].m_event_id= 100 stack[1].m_nesting_event_type= stack[0].m_event_type= STAGE stack[2].m_event_id= 102 stack[2].m_event_type= WAIT stack[2].m_nesting_event_id= stack[1].m_event_id= 101 stack[2].m_nesting_event_type= stack[1].m_event_type= WAIT The whole point of the stack[0] record is to allow this optimization in the code, in the instrumentation for wait events: wait->m_nesting_event_id= (wait-1)->m_event_id; wait->m_nesting_event_type= (wait-1)->m_event_type; This code works for both the top level wait, and nested waits, and works without if conditions, which helps performances. */ PFS_events_waits m_events_waits_stack[WAIT_STACK_SIZE]; /** True if the circular buffer @c m_waits_history is full. */ bool m_waits_history_full; /** Current index in the circular buffer @c m_waits_history. */ uint m_waits_history_index; /** Waits history circular buffer. This member holds the data for the table PERFORMANCE_SCHEMA.EVENTS_WAITS_HISTORY. */ PFS_events_waits *m_waits_history; /** True if the circular buffer @c m_stages_history is full. */ bool m_stages_history_full; /** Current index in the circular buffer @c m_stages_history. */ uint m_stages_history_index; /** Stages history circular buffer. This member holds the data for the table PERFORMANCE_SCHEMA.EVENTS_STAGES_HISTORY. */ PFS_events_stages *m_stages_history; /** True if the circular buffer @c m_statements_history is full. */ bool m_statements_history_full; /** Current index in the circular buffer @c m_statements_history. */ uint m_statements_history_index; /** Statements history circular buffer. This member holds the data for the table PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_HISTORY. */ PFS_events_statements *m_statements_history; /** True if the circular buffer @c m_transactions_history is full. */ bool m_transactions_history_full; /** Current index in the circular buffer @c m_transactions_history. */ uint m_transactions_history_index; /** Statements history circular buffer. This member holds the data for the table PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_HISTORY. */ PFS_events_transactions *m_transactions_history; /** Internal lock, for session attributes. Statement attributes are expected to be updated in frequently, typically per session execution. */ pfs_lock m_session_lock; /** User name. Protected by @c m_session_lock. */ char m_username[USERNAME_LENGTH]; /** Length of @c m_username. Protected by @c m_session_lock. */ uint m_username_length; /** Host name. Protected by @c m_session_lock. */ char m_hostname[HOSTNAME_LENGTH]; /** Length of @c m_hostname. Protected by @c m_session_lock. */ uint m_hostname_length; /** Database name. Protected by @c m_stmt_lock. */ char m_dbname[NAME_LEN]; /** Length of @c m_dbname. Protected by @c m_stmt_lock. */ uint m_dbname_length; /** Current command. */ int m_command; /** Connection type. */ enum_vio_type m_connection_type; /** Start time. */ time_t m_start_time; /** Internal lock, for statement attributes. Statement attributes are expected to be updated frequently, typically per statement execution. */ pfs_lock m_stmt_lock; /** Processlist state (derived from stage). */ PFS_stage_key m_stage; /** Current stage progress. */ PSI_stage_progress* m_stage_progress; /** Processlist info. Protected by @c m_stmt_lock. */ char m_processlist_info[COL_INFO_SIZE]; /** Length of @c m_processlist_info_length. Protected by @c m_stmt_lock. */ uint m_processlist_info_length; PFS_events_stages m_stage_current; /** Size of @c m_events_statements_stack. */ uint m_events_statements_count; PFS_events_statements *m_statement_stack; PFS_events_transactions m_transaction_current; THD *m_thd; PFS_host *m_host; PFS_user *m_user; PFS_account *m_account; /** Reset session connect attributes */ void reset_session_connect_attrs(); /** Buffer for the connection attributes. Protected by @c m_session_lock. */ char *m_session_connect_attrs; /** Length used by @c m_connect_attrs. Protected by @c m_session_lock. */ uint m_session_connect_attrs_length; /** Character set in which @c m_connect_attrs are encoded. Protected by @c m_session_lock. */ uint m_session_connect_attrs_cs_number; void carry_memory_stat_delta(PFS_memory_stat_delta *delta, uint index); void set_enabled(bool enabled) { m_enabled= enabled; } void set_history(bool history) { m_history= history; set_history_derived_flags(); } void set_history_derived_flags(); }; void carry_global_memory_stat_delta(PFS_memory_stat_delta *delta, uint index); extern PFS_stage_stat *global_instr_class_stages_array; extern PFS_statement_stat *global_instr_class_statements_array; extern PFS_memory_stat *global_instr_class_memory_array; PFS_mutex *sanitize_mutex(PFS_mutex *unsafe); PFS_rwlock *sanitize_rwlock(PFS_rwlock *unsafe); PFS_cond *sanitize_cond(PFS_cond *unsafe); PFS_thread *sanitize_thread(PFS_thread *unsafe); PFS_file *sanitize_file(PFS_file *unsafe); PFS_socket *sanitize_socket(PFS_socket *unsafe); PFS_metadata_lock *sanitize_metadata_lock(PFS_metadata_lock *unsafe); int init_instruments(const PFS_global_param *param); void cleanup_instruments(); int init_file_hash(const PFS_global_param *param); void cleanup_file_hash(); PFS_mutex* create_mutex(PFS_mutex_class *mutex_class, const void *identity); void destroy_mutex(PFS_mutex *pfs); PFS_rwlock* create_rwlock(PFS_rwlock_class *klass, const void *identity); void destroy_rwlock(PFS_rwlock *pfs); PFS_cond* create_cond(PFS_cond_class *klass, const void *identity); void destroy_cond(PFS_cond *pfs); PFS_thread* create_thread(PFS_thread_class *klass, const void *identity, ulonglong processlist_id); void destroy_thread(PFS_thread *pfs); PFS_file* find_or_create_file(PFS_thread *thread, PFS_file_class *klass, const char *filename, uint len, bool create); void find_and_rename_file(PFS_thread *thread, const char *old_filename, uint old_len, const char *new_filename, uint new_len); void release_file(PFS_file *pfs); void destroy_file(PFS_thread *thread, PFS_file *pfs); PFS_table* create_table(PFS_table_share *share, PFS_thread *opening_thread, const void *identity); void destroy_table(PFS_table *pfs); PFS_socket* create_socket(PFS_socket_class *socket_class, const my_socket *fd, const struct sockaddr *addr, socklen_t addr_len); void destroy_socket(PFS_socket *pfs); PFS_metadata_lock* create_metadata_lock(void *identity, const MDL_key *mdl_key, opaque_mdl_type mdl_type, opaque_mdl_duration mdl_duration, opaque_mdl_status mdl_status, const char *src_file, uint src_line); void destroy_metadata_lock(PFS_metadata_lock *pfs); /* For iterators and show status. */ extern long file_handle_max; extern ulong file_handle_lost; extern ulong events_waits_history_per_thread; extern ulong events_stages_history_per_thread; extern ulong events_statements_history_per_thread; extern ulong events_transactions_history_per_thread; extern ulong locker_lost; extern ulong statement_lost; extern ulong session_connect_attrs_lost; extern ulong session_connect_attrs_size_per_thread; /* Exposing the data directly, for iterators. */ extern PFS_file **file_handle_array; void reset_events_waits_by_instance(); void reset_file_instance_io(); void reset_socket_instance_io(); void aggregate_all_event_names(PFS_single_stat *from_array, PFS_single_stat *to_array); void aggregate_all_event_names(PFS_single_stat *from_array, PFS_single_stat *to_array_1, PFS_single_stat *to_array_2); void aggregate_all_stages(PFS_stage_stat *from_array, PFS_stage_stat *to_array); void aggregate_all_stages(PFS_stage_stat *from_array, PFS_stage_stat *to_array_1, PFS_stage_stat *to_array_2); void aggregate_all_statements(PFS_statement_stat *from_array, PFS_statement_stat *to_array); void aggregate_all_statements(PFS_statement_stat *from_array, PFS_statement_stat *to_array_1, PFS_statement_stat *to_array_2); void aggregate_all_transactions(PFS_transaction_stat *from_array, PFS_transaction_stat *to_array); void aggregate_all_transactions(PFS_transaction_stat *from_array, PFS_transaction_stat *to_array_1, PFS_transaction_stat *to_array_2); void aggregate_all_memory(bool alive, PFS_memory_stat *from_array, PFS_memory_stat *to_array); void aggregate_all_memory(bool alive, PFS_memory_stat *from_array, PFS_memory_stat *to_array_1, PFS_memory_stat *to_array_2); void aggregate_thread(PFS_thread *thread, PFS_account *safe_account, PFS_user *safe_user, PFS_host *safe_host); void aggregate_thread_waits(PFS_thread *thread, PFS_account *safe_account, PFS_user *safe_user, PFS_host *safe_host); void aggregate_thread_stages(PFS_thread *thread, PFS_account *safe_account, PFS_user *safe_user, PFS_host *safe_host); void aggregate_thread_statements(PFS_thread *thread, PFS_account *safe_account, PFS_user *safe_user, PFS_host *safe_host); void aggregate_thread_transactions(PFS_thread *thread, PFS_account *safe_account, PFS_user *safe_user, PFS_host *safe_host); void aggregate_thread_memory(bool alive, PFS_thread *thread, PFS_account *safe_account, PFS_user *safe_user, PFS_host *safe_host); void aggregate_thread_status(PFS_thread *thread, PFS_account *safe_account, PFS_user *safe_user, PFS_host *safe_host); void clear_thread_account(PFS_thread *thread); void set_thread_account(PFS_thread *thread); /** Update derived flags for all mutex instances. */ void update_mutex_derived_flags(); /** Update derived flags for all rwlock instances. */ void update_rwlock_derived_flags(); /** Update derived flags for all condition instances. */ void update_cond_derived_flags(); /** Update derived flags for all file handles. */ void update_file_derived_flags(); /** Update derived flags for all table handles. */ void update_table_derived_flags(); /** Update derived flags for all socket instances. */ void update_socket_derived_flags(); /** Update derived flags for all metadata instances. */ void update_metadata_derived_flags(); /** Update derived flags for all thread instances. */ void update_thread_derived_flags(); /** Update derived flags for all instruments. */ void update_instruments_derived_flags(); extern LF_HASH filename_hash; /** @} */ #endif