/* 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 */ #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; #ifdef __WIN__ #include #else #include #endif #include "my_global.h" #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_server.h" #include "lf.h" #include "pfs_con_slice.h" /** @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; }; /** 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; /** Instrument wait statistics. */ PFS_single_stat m_wait_stat; /** 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; }; /** 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(void) { if (m_has_io_stats && m_has_lock_stats) { safe_aggregate(& m_table_stat, m_share); m_has_io_stats= false; m_has_lock_stats= false; } else if (m_has_io_stats) { safe_aggregate_io(& m_table_stat, m_share); m_has_io_stats= false; } else 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; /** Owner. */ PFS_thread *m_thread_owner; /** Table share. */ PFS_table_share *m_share; /** Table identity, typically a handler. */ const void *m_identity; /** Table statistics. */ PFS_table_stat m_table_stat; private: static void safe_aggregate(PFS_table_stat *stat, PFS_table_share *safe_share); static void safe_aggregate_io(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; }; /** @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, 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; /** @def PFS_MAX_ALLOC_RETRY Maximum number of times the code attempts to allocate an item from internal buffers, before giving up. */ #define PFS_MAX_ALLOC_RETRY 1000 /** The maximun number of passes in @sa PFS_scan. */ #define PFS_MAX_SCAN_PASS 2 /** Helper to scan circular buffers. Given a buffer of size [0, max_size - 1], and a random starting point in the buffer, this helper returns up to two [first, last -1] intervals that: - fit into the [0, max_size - 1] range, - have a maximum combined length of at most PFS_MAX_ALLOC_RETRY. */ struct PFS_scan { public: /** Initialize a new scan. @param random a random index to start from @param max_size the max size of the interval to scan */ void init(uint random, uint max_size); /** Predicate, has a next pass. @return true if there is a next pass to perform. */ bool has_pass() const { return (m_pass < m_pass_max); } /** Iterator, proceed to the next pass. */ void next_pass() { m_pass++; } /** First index for this pass. */ uint first() const { return m_first[m_pass]; } /** Last index for this pass. */ uint last() const { return m_last[m_pass]; } private: /** Current pass. */ uint m_pass; /** Maximum number of passes. */ uint m_pass_max; /** First element for each pass. */ uint m_first[PFS_MAX_SCAN_PASS]; /** Last element for each pass. */ uint m_last[PFS_MAX_SCAN_PASS]; }; /** 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; /** 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; /** 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; /** 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. 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; /** 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; /** 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; /** 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_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; }; extern PFS_stage_stat *global_instr_class_stages_array; extern PFS_statement_stat *global_instr_class_statements_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); int init_instruments(const PFS_global_param *param); void cleanup_instruments(); int init_file_hash(); 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 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); /* For iterators and show status. */ extern ulong mutex_max; extern ulong mutex_lost; extern ulong rwlock_max; extern ulong rwlock_lost; extern ulong cond_max; extern ulong cond_lost; extern ulong thread_max; extern ulong thread_lost; extern ulong file_max; extern ulong file_lost; extern long file_handle_max; extern ulong file_handle_lost; extern ulong table_max; extern ulong table_lost; extern ulong socket_max; extern ulong socket_lost; extern ulong events_waits_history_per_thread; extern ulong events_stages_history_per_thread; extern ulong events_statements_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_mutex *mutex_array; extern PFS_rwlock *rwlock_array; extern PFS_cond *cond_array; extern PFS_thread *thread_array; extern PFS_file *file_array; extern PFS_file **file_handle_array; extern PFS_table *table_array; extern PFS_socket *socket_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_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 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 instruments. */ void update_instruments_derived_flags(); extern LF_HASH filename_hash; /** @} */ #endif