diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:00:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:00:34 +0000 |
commit | 3f619478f796eddbba6e39502fe941b285dd97b1 (patch) | |
tree | e2c7b5777f728320e5b5542b6213fd3591ba51e2 /storage/perfschema | |
parent | Initial commit. (diff) | |
download | mariadb-3f619478f796eddbba6e39502fe941b285dd97b1.tar.xz mariadb-3f619478f796eddbba6e39502fe941b285dd97b1.zip |
Adding upstream version 1:10.11.6.upstream/1%10.11.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'storage/perfschema')
261 files changed, 76906 insertions, 0 deletions
diff --git a/storage/perfschema/CMakeLists.txt b/storage/perfschema/CMakeLists.txt new file mode 100644 index 00000000..b4f5e96b --- /dev/null +++ b/storage/perfschema/CMakeLists.txt @@ -0,0 +1,389 @@ +# Copyright (c) 2009, 2023, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, +# as published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an additional +# permission to link the program and your derivative works with the +# separately licensed software that they have included with MySQL. +# +# 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_BINARY_DIR}/sql + ${CMAKE_CURRENT_BINARY_DIR} + ${PCRE_INCLUDES} + ${SSL_INCLUDE_DIRS}) + +ADD_DEFINITIONS(-DMYSQL_SERVER) +IF (SSL_DEFINES) + ADD_DEFINITIONS(${SSL_DEFINES}) +ENDIF() + +IF(CMAKE_SYSTEM_NAME MATCHES AIX) + # Workaround linker bug on AIX + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-berok") +ENDIF() + +# +# Maintainer: keep this list sorted, to avoid merge collisions. +# Tip: ls -1 *.h, ls -1 *.cc +# +SET(PERFSCHEMA_SOURCES +ha_perfschema.h +cursor_by_account.h +cursor_by_host.h +cursor_by_thread.h +cursor_by_user.h +pfs.h +pfs_account.h +pfs_atomic.h +pfs_buffer_container.h +pfs_builtin_memory.h +pfs_column_types.h +pfs_column_values.h +pfs_con_slice.h +pfs_defaults.h +pfs_digest.h +pfs_program.h +pfs_prepared_stmt.h +pfs_engine_table.h +pfs_events.h +pfs_events_stages.h +pfs_events_statements.h +pfs_events_transactions.h +pfs_events_waits.h +pfs_global.h +pfs_host.h +pfs_instr.h +pfs_instr_class.h +pfs_lock.h +pfs_memory.h +pfs_server.h +pfs_setup_actor.h +pfs_setup_object.h +pfs_stat.h +pfs_status.h +pfs_timer.h +pfs_user.h +pfs_variable.h +pfs_visitor.h +table_accounts.h +table_all_instr.h +table_esgs_by_account_by_event_name.h +table_esgs_by_host_by_event_name.h +table_esgs_by_thread_by_event_name.h +table_esgs_by_user_by_event_name.h +table_esgs_global_by_event_name.h +table_esms_by_account_by_event_name.h +table_esms_by_host_by_event_name.h +table_esms_by_digest.h +table_esms_by_program.h +table_prepared_stmt_instances.h +#table_processlist.h +table_esms_by_thread_by_event_name.h +table_esms_by_user_by_event_name.h +table_esms_global_by_event_name.h +table_ets_by_account_by_event_name.h +table_ets_by_host_by_event_name.h +table_ets_by_thread_by_event_name.h +table_ets_by_user_by_event_name.h +table_ets_global_by_event_name.h +table_events_stages.h +table_events_statements.h +table_events_transactions.h +table_events_waits.h +table_events_waits_summary.h +table_ews_by_account_by_event_name.h +table_ews_by_host_by_event_name.h +table_ews_by_thread_by_event_name.h +table_ews_by_user_by_event_name.h +table_ews_global_by_event_name.h +table_file_instances.h +table_md_locks.h +table_mems_global_by_event_name.h +table_mems_by_account_by_event_name.h +table_mems_by_host_by_event_name.h +table_mems_by_thread_by_event_name.h +table_mems_by_user_by_event_name.h +table_file_summary_by_instance.h +table_file_summary_by_event_name.h +table_socket_instances.h +table_socket_summary_by_instance.h +table_socket_summary_by_event_name.h +table_helper.h +table_host_cache.h +table_hosts.h +table_os_global_by_type.h +table_performance_timers.h +table_setup_actors.h +table_setup_consumers.h +table_setup_instruments.h +table_setup_objects.h +table_setup_timers.h +table_sync_instances.h +table_status_by_account.h +table_status_by_host.h +table_status_by_thread.h +table_status_by_user.h +table_global_status.h +table_session_status.h +table_variables_by_thread.h +table_global_variables.h +table_session_variables.h +table_table_handles.h +table_threads.h +table_tiws_by_index_usage.h +table_tiws_by_table.h +table_tlws_by_table.h +table_users.h +table_uvar_by_thread.h +cursor_by_thread_connect_attr.h +table_session_connect.h +table_session_connect_attrs.h +table_session_account_connect_attrs.h +table_replication_connection_configuration.h +table_replication_group_members.h +table_replication_connection_status.h +table_replication_applier_configuration.h +table_replication_applier_status.h +table_replication_applier_status_by_coordinator.h +table_replication_applier_status_by_worker.h +table_replication_group_member_stats.h +cursor_by_account.cc +cursor_by_host.cc +cursor_by_thread.cc +cursor_by_user.cc +ha_perfschema.cc +mysqld_thd_manager.cc +pfs.cc +pfs_account.cc +pfs_autosize.cc +pfs_buffer_container.cc +pfs_builtin_memory.cc +pfs_column_values.cc +pfs_con_slice.cc +pfs_defaults.cc +pfs_digest.cc +pfs_program.cc +pfs_prepared_stmt.cc +pfs_engine_table.cc +pfs_events_stages.cc +pfs_events_statements.cc +pfs_events_transactions.cc +pfs_events_waits.cc +pfs_global.cc +pfs_host.cc +pfs_instr.cc +pfs_instr_class.cc +pfs_memory.cc +pfs_server.cc +pfs_setup_actor.cc +pfs_setup_object.cc +pfs_status.cc +pfs_timer.cc +pfs_user.cc +pfs_variable.cc +pfs_visitor.cc +table_accounts.cc +table_all_instr.cc +table_esgs_by_account_by_event_name.cc +table_esgs_by_host_by_event_name.cc +table_esgs_by_thread_by_event_name.cc +table_esgs_by_user_by_event_name.cc +table_esgs_global_by_event_name.cc +table_esms_by_account_by_event_name.cc +table_esms_by_host_by_event_name.cc +table_esms_by_digest.cc +table_esms_by_program.cc +table_prepared_stmt_instances.cc +#table_processlist.cc +table_esms_by_thread_by_event_name.cc +table_esms_by_user_by_event_name.cc +table_esms_global_by_event_name.cc +table_ets_by_account_by_event_name.cc +table_ets_by_host_by_event_name.cc +table_ets_by_thread_by_event_name.cc +table_ets_by_user_by_event_name.cc +table_ets_global_by_event_name.cc +table_events_stages.cc +table_events_statements.cc +table_events_transactions.cc +table_events_waits.cc +table_events_waits_summary.cc +table_ews_by_account_by_event_name.cc +table_ews_by_host_by_event_name.cc +table_ews_by_thread_by_event_name.cc +table_ews_by_user_by_event_name.cc +table_ews_global_by_event_name.cc +table_file_instances.cc +table_md_locks.cc +table_mems_global_by_event_name.cc +table_mems_by_account_by_event_name.cc +table_mems_by_host_by_event_name.cc +table_mems_by_thread_by_event_name.cc +table_mems_by_user_by_event_name.cc +table_file_summary_by_instance.cc +table_file_summary_by_event_name.cc +table_socket_instances.cc +table_socket_summary_by_instance.cc +table_socket_summary_by_event_name.cc +table_helper.cc +table_host_cache.cc +table_hosts.cc +table_os_global_by_type.cc +table_performance_timers.cc +table_setup_actors.cc +table_setup_consumers.cc +table_setup_instruments.cc +table_setup_objects.cc +table_setup_timers.cc +table_sync_instances.cc +table_status_by_account.cc +table_status_by_host.cc +table_status_by_thread.cc +table_status_by_user.cc +table_global_status.cc +table_session_status.cc +#table_variables_by_thread.cc +#table_global_variables.cc +#table_session_variables.cc +table_table_handles.cc +table_threads.cc +table_tiws_by_index_usage.cc +table_tiws_by_table.cc +table_tlws_by_table.cc +table_users.cc +table_uvar_by_thread.cc +cursor_by_thread_connect_attr.cc +table_session_connect.cc +table_session_connect_attrs.cc +table_session_account_connect_attrs.cc +table_replication_connection_configuration.cc +#table_replication_group_members.cc +#table_replication_connection_status.cc +table_replication_applier_configuration.cc +table_replication_applier_status.cc +table_replication_applier_status_by_coordinator.cc +table_replication_applier_status_by_worker.cc +#table_replication_group_member_stats.cc +) + +# Check for pthread_threadid_np() +CHECK_C_SOURCE_COMPILES(" +#include <pthread.h> +int main(int ac, char **av) +{ + unsigned long long tid64; + pthread_threadid_np(NULL, &tid64); + return (tid64 != 0 ? 0 : 1); +}" +HAVE_PTHREAD_THREADID_NP) + +# gettid() library function (glibc-2.30+) +CHECK_SYMBOL_EXISTS(gettid unistd.h HAVE_GETTID) + +# Check for gettid() system call +CHECK_C_SOURCE_COMPILES(" +#include <sys/types.h> +#include <sys/syscall.h> +#include <unistd.h> +int main(int ac, char **av) +{ + unsigned long long tid = syscall(SYS_gettid); + return (tid != 0 ? 0 : 1); +}" +HAVE_SYS_GETTID) + +# Check for getthrid() +CHECK_C_SOURCE_COMPILES(" +#include <unistd.h> +int main(int ac, char **av) +{ + unsigned long long tid = getthrid(); + return (tid != 0 ? 0 : 1); +}" +HAVE_GETTHRID) + +# Check for pthread_getthreadid_np() +CHECK_C_SOURCE_COMPILES(" +#include <pthread_np.h> +int main(int ac, char **av) +{ + unsigned long long tid = pthread_getthreadid_np(); + return (tid != 0 ? 0 : 1); +}" +HAVE_PTHREAD_GETTHREADID_NP) + +# Check for pthread_self() returning an integer type +CHECK_C_SOURCE_COMPILES(" +#include <sys/types.h> +#include <pthread.h> +int main(int ac, char **av) +{ + unsigned long long tid = pthread_self(); + return (tid != 0 ? 0 : 1); +}" +HAVE_INTEGER_PTHREAD_SELF +FAIL_REGEX "warning: incompatible pointer to integer conversion" +) + +CONFIGURE_FILE(pfs_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/pfs_config.h) + +MYSQL_ADD_PLUGIN(perfschema ${PERFSCHEMA_SOURCES} STORAGE_ENGINE DEFAULT + STATIC_ONLY RECOMPILE_FOR_EMBEDDED DEPENDS GenServerSource) +IF (TARGET perfschema) + IF(WITH_UNIT_TESTS) + ADD_SUBDIRECTORY(unittest) + ENDIF(WITH_UNIT_TESTS) +ENDIF(TARGET perfschema) + +# Only disable threads when building without *any* instrumentation, +# as other instrumentations have a dependency on threads. +OPTION(DISABLE_PSI_THREAD "Exclude the performance schema thread instrumentation" OFF) + +OPTION(DISABLE_PSI_MUTEX "Exclude the performance schema mutex instrumentation" OFF) +OPTION(DISABLE_PSI_RWLOCK "Exclude the performance schema rwlock instrumentation" OFF) +OPTION(DISABLE_PSI_COND "Exclude the performance schema condition instrumentation" OFF) +OPTION(DISABLE_PSI_FILE "Exclude the performance schema file instrumentation" OFF) +OPTION(DISABLE_PSI_TABLE "Exclude the performance schema table instrumentation" OFF) +OPTION(DISABLE_PSI_SOCKET "Exclude the performance schema socket instrumentation" OFF) +OPTION(DISABLE_PSI_STAGE "Exclude the performance schema stage instrumentation" OFF) +OPTION(DISABLE_PSI_STATEMENT "Exclude the performance schema statement instrumentation" OFF) +OPTION(DISABLE_PSI_SP "Exclude the performance schema stored procedure instrumentation" OFF) +OPTION(DISABLE_PSI_PS "Exclude the performance schema prepared statements instances instrumentation" OFF) +OPTION(DISABLE_PSI_IDLE "Exclude the performance schema idle instrumentation" OFF) +OPTION(DISABLE_PSI_STATEMENT_DIGEST "Exclude the performance schema statement digest instrumentation" OFF) +OPTION(DISABLE_PSI_METADATA "Exclude the performance schema metadata instrumentation" OFF) +OPTION(DISABLE_PSI_MEMORY "Exclude the performance schema memory instrumentation" OFF) +OPTION(DISABLE_PSI_TRANSACTION "Exclude the performance schema transaction instrumentation" OFF) + +MARK_AS_ADVANCED(DISABLE_PSI_THREAD) + +MARK_AS_ADVANCED(DISABLE_PSI_MUTEX) +MARK_AS_ADVANCED(DISABLE_PSI_RWLOCK) +MARK_AS_ADVANCED(DISABLE_PSI_COND) +MARK_AS_ADVANCED(DISABLE_PSI_FILE) +MARK_AS_ADVANCED(DISABLE_PSI_TABLE) +MARK_AS_ADVANCED(DISABLE_PSI_SOCKET) +MARK_AS_ADVANCED(DISABLE_PSI_STAGE) +MARK_AS_ADVANCED(DISABLE_PSI_STATEMENT) +MARK_AS_ADVANCED(DISABLE_PSI_SP) +MARK_AS_ADVANCED(DISABLE_PSI_PS) +MARK_AS_ADVANCED(DISABLE_PSI_IDLE) +MARK_AS_ADVANCED(DISABLE_PSI_STATEMENT_DIGEST) +MARK_AS_ADVANCED(DISABLE_PSI_METADATA) +MARK_AS_ADVANCED(DISABLE_PSI_MEMORY) +MARK_AS_ADVANCED(DISABLE_PSI_TRANSACTION) diff --git a/storage/perfschema/cursor_by_account.cc b/storage/perfschema/cursor_by_account.cc new file mode 100644 index 00000000..2fa224d3 --- /dev/null +++ b/storage/perfschema/cursor_by_account.cc @@ -0,0 +1,82 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/cursor_by_account.cc + Cursor CURSOR_BY_ACCOUNT (implementation). +*/ + +#include "my_global.h" +#include "cursor_by_account.h" +#include "pfs_buffer_container.h" + +ha_rows +cursor_by_account::get_row_count(void) +{ + return global_account_container.get_row_count(); +} + +cursor_by_account::cursor_by_account(const PFS_engine_table_share *share) + : PFS_engine_table(share, &m_pos), + m_pos(0), m_next_pos(0) +{} + +void cursor_by_account::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int cursor_by_account::rnd_next(void) +{ + PFS_account *pfs; + + m_pos.set_at(&m_next_pos); + PFS_account_iterator it= global_account_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int +cursor_by_account::rnd_pos(const void *pos) +{ + PFS_account *pfs; + + set_position(pos); + + pfs= global_account_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + diff --git a/storage/perfschema/cursor_by_account.h b/storage/perfschema/cursor_by_account.h new file mode 100644 index 00000000..44175ceb --- /dev/null +++ b/storage/perfschema/cursor_by_account.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef CURSOR_BY_ACCOUNT_H +#define CURSOR_BY_ACCOUNT_H + +/** + @file storage/perfschema/cursor_by_account.h + Cursor CURSOR_BY_ACCOUNT (declarations). +*/ + +#include "pfs_engine_table.h" +#include "pfs_account.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** Cursor CURSOR_BY_ACCOUNT. */ +class cursor_by_account : public PFS_engine_table +{ +public: + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + cursor_by_account(const PFS_engine_table_share *share); + +public: + ~cursor_by_account() = default; + +protected: + virtual void make_row(PFS_account *account)= 0; + +private: + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/cursor_by_host.cc b/storage/perfschema/cursor_by_host.cc new file mode 100644 index 00000000..aa22e8c0 --- /dev/null +++ b/storage/perfschema/cursor_by_host.cc @@ -0,0 +1,82 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/cursor_by_host.cc + Cursor CURSOR_BY_HOST (implementation). +*/ + +#include "my_global.h" +#include "cursor_by_host.h" +#include "pfs_buffer_container.h" + +ha_rows +cursor_by_host::get_row_count(void) +{ + return global_host_container.get_row_count(); +} + +cursor_by_host::cursor_by_host(const PFS_engine_table_share *share) + : PFS_engine_table(share, &m_pos), + m_pos(0), m_next_pos(0) +{} + +void cursor_by_host::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int cursor_by_host::rnd_next(void) +{ + PFS_host *pfs; + + m_pos.set_at(&m_next_pos); + PFS_host_iterator it= global_host_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int +cursor_by_host::rnd_pos(const void *pos) +{ + PFS_host *pfs; + + set_position(pos); + + pfs= global_host_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + diff --git a/storage/perfschema/cursor_by_host.h b/storage/perfschema/cursor_by_host.h new file mode 100644 index 00000000..c8a83a47 --- /dev/null +++ b/storage/perfschema/cursor_by_host.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef CURSOR_BY_HOST_H +#define CURSOR_BY_HOST_H + +/** + @file storage/perfschema/cursor_by_host.h + Cursor CURSOR_BY_HOST (declarations). +*/ + +#include "pfs_engine_table.h" +#include "pfs_host.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** Cursor CURSOR_BY_HOST. */ +class cursor_by_host : public PFS_engine_table +{ +public: + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + cursor_by_host(const PFS_engine_table_share *share); + +public: + ~cursor_by_host() = default; + +protected: + virtual void make_row(PFS_host *host)= 0; + +private: + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/cursor_by_thread.cc b/storage/perfschema/cursor_by_thread.cc new file mode 100644 index 00000000..307cb329 --- /dev/null +++ b/storage/perfschema/cursor_by_thread.cc @@ -0,0 +1,83 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/cursor_by_thread.cc + Cursor CURSOR_BY_THREAD (implementation). +*/ + +#include "my_global.h" +#include "cursor_by_thread.h" +#include "pfs_instr.h" +#include "pfs_buffer_container.h" + +ha_rows +cursor_by_thread::get_row_count(void) +{ + return global_thread_container.get_row_count(); +} + +cursor_by_thread::cursor_by_thread(const PFS_engine_table_share *share) + : PFS_engine_table(share, &m_pos), + m_pos(0), m_next_pos(0) +{} + +void cursor_by_thread::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int cursor_by_thread::rnd_next(void) +{ + PFS_thread *pfs; + + m_pos.set_at(&m_next_pos); + PFS_thread_iterator it= global_thread_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int +cursor_by_thread::rnd_pos(const void *pos) +{ + PFS_thread *pfs; + + set_position(pos); + + pfs= global_thread_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + diff --git a/storage/perfschema/cursor_by_thread.h b/storage/perfschema/cursor_by_thread.h new file mode 100644 index 00000000..50e16229 --- /dev/null +++ b/storage/perfschema/cursor_by_thread.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef CURSOR_BY_THREAD_H +#define CURSOR_BY_THREAD_H + +/** + @file storage/perfschema/cursor_by_thread.h + Cursor CURSOR_BY_THREAD (declarations). +*/ + +#include "pfs_engine_table.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** Cursor CURSOR_BY_THREAD. */ +class cursor_by_thread : public PFS_engine_table +{ +public: + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + cursor_by_thread(const PFS_engine_table_share *share); + +public: + ~cursor_by_thread() = default; + +protected: + virtual void make_row(PFS_thread *thread)= 0; + +private: + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/cursor_by_thread_connect_attr.cc b/storage/perfschema/cursor_by_thread_connect_attr.cc new file mode 100644 index 00000000..1db97f72 --- /dev/null +++ b/storage/perfschema/cursor_by_thread_connect_attr.cc @@ -0,0 +1,92 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "my_global.h" +#include "cursor_by_thread_connect_attr.h" +#include "pfs_buffer_container.h" + +ha_rows +cursor_by_thread_connect_attr::get_row_count(void) +{ + /* + The real number of attributes per thread does not matter, + we only need to hint the optimizer there are many per thread, + so abusing session_connect_attrs_size_per_thread + (which is a number of bytes, not attributes) + */ + return global_thread_container.get_row_count() * + session_connect_attrs_size_per_thread; +} + +cursor_by_thread_connect_attr::cursor_by_thread_connect_attr( + const PFS_engine_table_share *share) : + PFS_engine_table(share, &m_pos), m_row_exists(false) +{} + +int cursor_by_thread_connect_attr::rnd_next(void) +{ + PFS_thread *thread; + bool has_more_thread= true; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread); + if (thread != NULL) + { + make_row(thread, m_pos.m_index_2); + if (m_row_exists) + { + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + + return HA_ERR_END_OF_FILE; +} + + +int cursor_by_thread_connect_attr::rnd_pos(const void *pos) +{ + PFS_thread *thread; + + set_position(pos); + + thread= global_thread_container.get(m_pos.m_index_1); + if (thread != NULL) + { + make_row(thread, m_pos.m_index_2); + if (m_row_exists) + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + + +void cursor_by_thread_connect_attr::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} diff --git a/storage/perfschema/cursor_by_thread_connect_attr.h b/storage/perfschema/cursor_by_thread_connect_attr.h new file mode 100644 index 00000000..4f48b40c --- /dev/null +++ b/storage/perfschema/cursor_by_thread_connect_attr.h @@ -0,0 +1,88 @@ +/* Copyright (c) 2012, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef CURSOR_BY_THREAD_CONNECT_ATTR_H +#define CURSOR_BY_THREAD_CONNECT_ATTR_H + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr.h" + +/** + \addtogroup Performance_schema_tables + @{ +*/ + +/** + Position of a cursor on abstract table + PERFORMANCE_SCHEMA.SESSION_CONNECT_ATTRS. +*/ +struct pos_connect_attr_by_thread_by_attr +: public PFS_double_index +{ + pos_connect_attr_by_thread_by_attr() + : PFS_double_index(0, 0) + {} + + inline void next_thread(void) + { + m_index_1++; + m_index_2= 0; + } + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } +}; + +/** Cursor CURSOR_BY_THREAD_CONNECT_ATTR. */ +class cursor_by_thread_connect_attr : public PFS_engine_table +{ +public: + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + cursor_by_thread_connect_attr(const PFS_engine_table_share *share); + +public: + ~cursor_by_thread_connect_attr() = default; + +protected: + virtual void make_row(PFS_thread *thread, uint ordinal)= 0; + /** True if row exists */ + bool m_row_exists; + +private: + /** Current position. */ + pos_connect_attr_by_thread_by_attr m_pos; + /** Next position. */ + pos_connect_attr_by_thread_by_attr m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/cursor_by_user.cc b/storage/perfschema/cursor_by_user.cc new file mode 100644 index 00000000..f1c9969f --- /dev/null +++ b/storage/perfschema/cursor_by_user.cc @@ -0,0 +1,82 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/cursor_by_user.cc + Cursor CURSOR_BY_USER (implementation). +*/ + +#include "my_global.h" +#include "cursor_by_user.h" +#include "pfs_buffer_container.h" + +ha_rows +cursor_by_user::get_row_count(void) +{ + return global_user_container.get_row_count(); +} + +cursor_by_user::cursor_by_user(const PFS_engine_table_share *share) + : PFS_engine_table(share, &m_pos), + m_pos(0), m_next_pos(0) +{} + +void cursor_by_user::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int cursor_by_user::rnd_next(void) +{ + PFS_user *pfs; + + m_pos.set_at(&m_next_pos); + PFS_user_iterator it= global_user_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int +cursor_by_user::rnd_pos(const void *pos) +{ + PFS_user *pfs; + + set_position(pos); + + pfs= global_user_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + diff --git a/storage/perfschema/cursor_by_user.h b/storage/perfschema/cursor_by_user.h new file mode 100644 index 00000000..6438515c --- /dev/null +++ b/storage/perfschema/cursor_by_user.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef CURSOR_BY_USER_H +#define CURSOR_BY_USER_H + +/** + @file storage/perfschema/cursor_by_user.h + Cursor CURSOR_BY_USER (declarations). +*/ + +#include "pfs_engine_table.h" +#include "pfs_user.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** Cursor CURSOR_BY_USER. */ +class cursor_by_user : public PFS_engine_table +{ +public: + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + cursor_by_user(const PFS_engine_table_share *share); + +public: + ~cursor_by_user() = default; + +protected: + virtual void make_row(PFS_user *user)= 0; + +private: + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/ha_perfschema.cc b/storage/perfschema/ha_perfschema.cc new file mode 100644 index 00000000..c6d2f23d --- /dev/null +++ b/storage/perfschema/ha_perfschema.cc @@ -0,0 +1,543 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/ha_perfschema.cc + Performance schema storage engine (implementation). +*/ + +#include "sql_plugin.h" +#include "my_pthread.h" +#include "ha_perfschema.h" +#include "pfs_engine_table.h" +#include "pfs_column_values.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_account.h" +#include "pfs_host.h" +#include "pfs_user.h" +#include "pfs_program.h" +#include "pfs_prepared_stmt.h" +#include "pfs_buffer_container.h" + +handlerton *pfs_hton= NULL; + +#define PFS_ENABLED() (pfs_initialized && (pfs_enabled || m_table_share->m_perpetual)) + +static handler* pfs_create_handler(handlerton *hton, + TABLE_SHARE *table, + MEM_ROOT *mem_root) +{ + return new (mem_root) ha_perfschema(hton, table); +} + +static int compare_database_names(const char *name1, const char *name2) +{ + if (lower_case_table_names) + return strcasecmp(name1, name2); + return strcmp(name1, name2); +} + +static const PFS_engine_table_share* +find_table_share(const char *db, const char *name) +{ + DBUG_ENTER("find_table_share"); + + if (compare_database_names(db, PERFORMANCE_SCHEMA_str.str) != 0) + DBUG_RETURN(NULL); + + const PFS_engine_table_share* result; + result= PFS_engine_table::find_engine_table_share(name); + DBUG_RETURN(result); +} + +static int pfs_discover_table(handlerton *hton, THD *thd, TABLE_SHARE *share) +{ + const PFS_engine_table_share *pfs_share; + + if ((pfs_share= find_table_share(share->db.str, share->table_name.str))) + return share->init_from_sql_statement_string(thd, false, + pfs_share->sql.str, + pfs_share->sql.length); + return HA_ERR_NO_SUCH_TABLE; +} + +static int pfs_discover_table_existence(handlerton *hton, const char *db, + const char *table_name) +{ + return MY_TEST(find_table_share(db, table_name)); +} + +static int pfs_init_func(void *p) +{ + DBUG_ENTER("pfs_init_func"); + + pfs_hton= reinterpret_cast<handlerton *> (p); + + pfs_hton->create= pfs_create_handler; + pfs_hton->drop_table= [](handlerton *, const char*) { return -1; }; + pfs_hton->show_status= pfs_show_status; + pfs_hton->flags= HTON_ALTER_NOT_SUPPORTED | HTON_TEMPORARY_NOT_SUPPORTED | + HTON_NO_PARTITION | HTON_NO_BINLOG_ROW_OPT; + + /* + As long as the server implementation keeps using legacy_db_type, + as for example in mysql_truncate(), + we can not rely on the fact that different mysqld process will assign + consistently the same legacy_db_type for a given storage engine name. + In particular, using different --loose-skip-xxx options between + ./mysqld --bootstrap + ./mysqld + creates bogus .frm forms when bootstrapping the performance schema, + if we rely on ha_initialize_handlerton to assign a really dynamic value. + To fix this, a dedicated DB_TYPE is officially assigned to + the performance schema. See Bug#43039. + */ + pfs_hton->db_type= DB_TYPE_PERFORMANCE_SCHEMA; + pfs_hton->discover_table= pfs_discover_table; + pfs_hton->discover_table_existence= pfs_discover_table_existence; + pfs_hton->discover_table_names= pfs_discover_table_names; + + PFS_engine_table_share::init_all_locks(); + + DBUG_RETURN(0); +} + +static int pfs_done_func(void *p) +{ + DBUG_ENTER("pfs_done_func"); + + pfs_hton= NULL; + + PFS_engine_table_share::delete_all_locks(); + + DBUG_RETURN(0); +} + +static int show_func_mutex_instances_lost(THD *thd, SHOW_VAR *var, char *buff) +{ + var->type= SHOW_LONG; + var->value= buff; + long *value= reinterpret_cast<long*>(buff); + *value= global_mutex_container.get_lost_counter(); + return 0; +} + +static struct st_mysql_show_var pfs_status_vars[]= +{ + {"Performance_schema_mutex_classes_lost", + (char*) &mutex_class_lost, SHOW_LONG_NOFLUSH}, + {"Performance_schema_rwlock_classes_lost", + (char*) &rwlock_class_lost, SHOW_LONG_NOFLUSH}, + {"Performance_schema_cond_classes_lost", + (char*) &cond_class_lost, SHOW_LONG_NOFLUSH}, + {"Performance_schema_thread_classes_lost", + (char*) &thread_class_lost, SHOW_LONG_NOFLUSH}, + {"Performance_schema_file_classes_lost", + (char*) &file_class_lost, SHOW_LONG_NOFLUSH}, + {"Performance_schema_socket_classes_lost", + (char*) &socket_class_lost, SHOW_LONG_NOFLUSH}, + {"Performance_schema_memory_classes_lost", + (char*) &memory_class_lost, SHOW_LONG_NOFLUSH}, + {"Performance_schema_mutex_instances_lost", + (char*) &show_func_mutex_instances_lost, SHOW_FUNC}, + {"Performance_schema_rwlock_instances_lost", + (char*) &global_rwlock_container.m_lost, SHOW_LONG}, + {"Performance_schema_cond_instances_lost", + (char*) &global_cond_container.m_lost, SHOW_LONG}, + {"Performance_schema_thread_instances_lost", + (char*) &global_thread_container.m_lost, SHOW_LONG}, + {"Performance_schema_file_instances_lost", + (char*) &global_file_container.m_lost, SHOW_LONG}, + {"Performance_schema_file_handles_lost", + (char*) &file_handle_lost, SHOW_LONG}, + {"Performance_schema_socket_instances_lost", + (char*) &global_socket_container.m_lost, SHOW_LONG}, + {"Performance_schema_locker_lost", + (char*) &locker_lost, SHOW_LONG}, + /* table shares, can be flushed */ + {"Performance_schema_table_instances_lost", + (char*) &global_table_share_container.m_lost, SHOW_LONG}, + /* table handles, can be flushed */ + {"Performance_schema_table_handles_lost", + (char*) &global_table_container.m_lost, SHOW_LONG}, + /* table lock stats, can be flushed */ + {"Performance_schema_table_lock_stat_lost", + (char*) &global_table_share_lock_container.m_lost, SHOW_LONG}, + /* table index stats, can be flushed */ + {"Performance_schema_index_stat_lost", + (char*) &global_table_share_index_container.m_lost, SHOW_LONG}, + {"Performance_schema_hosts_lost", + (char*) &global_host_container.m_lost, SHOW_LONG}, + {"Performance_schema_users_lost", + (char*) &global_user_container.m_lost, SHOW_LONG}, + {"Performance_schema_accounts_lost", + (char*) &global_account_container.m_lost, SHOW_LONG}, + {"Performance_schema_stage_classes_lost", + (char*) &stage_class_lost, SHOW_LONG}, + {"Performance_schema_statement_classes_lost", + (char*) &statement_class_lost, SHOW_LONG}, + {"Performance_schema_digest_lost", + (char*) &digest_lost, SHOW_LONG}, + {"Performance_schema_session_connect_attrs_lost", + (char*) &session_connect_attrs_lost, SHOW_LONG}, + {"Performance_schema_program_lost", + (char*) &global_program_container.m_lost, SHOW_LONG}, + {"Performance_schema_nested_statement_lost", + (char*) &nested_statement_lost, SHOW_LONG}, + {"Performance_schema_prepared_statements_lost", + (char*) &global_prepared_stmt_container.m_lost, SHOW_LONG}, + {"Performance_schema_metadata_lock_lost", + (char*) &global_mdl_container.m_lost, SHOW_LONG}, + {NullS, NullS, SHOW_LONG} +}; + +struct st_mysql_storage_engine pfs_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION }; + +const char* pfs_engine_name= "PERFORMANCE_SCHEMA"; + +maria_declare_plugin(perfschema) +{ + MYSQL_STORAGE_ENGINE_PLUGIN, + &pfs_storage_engine, + pfs_engine_name, + "Marc Alff, Oracle", + "Performance Schema", + PLUGIN_LICENSE_GPL, + pfs_init_func, + pfs_done_func, + 0x0001, + pfs_status_vars, + NULL, + "5.7.31", + MariaDB_PLUGIN_MATURITY_STABLE +} +maria_declare_plugin_end; + +ha_perfschema::ha_perfschema(handlerton *hton, TABLE_SHARE *share) + : handler(hton, share), m_table_share(NULL), m_table(NULL) +{} + +ha_perfschema::~ha_perfschema() = default; + +int ha_perfschema::open(const char *name, int mode, uint test_if_locked) +{ + DBUG_ENTER("ha_perfschema::open"); + + m_table_share= find_table_share(table_share->db.str, + table_share->table_name.str); + if (! m_table_share) + DBUG_RETURN(HA_ERR_NO_SUCH_TABLE); + + thr_lock_data_init(m_table_share->m_thr_lock_ptr, &m_thr_lock, NULL); + ref_length= m_table_share->m_ref_length; + + DBUG_RETURN(0); +} + +int ha_perfschema::close(void) +{ + DBUG_ENTER("ha_perfschema::close"); + m_table_share= NULL; + delete m_table; + m_table= NULL; + + DBUG_RETURN(0); +} + +int ha_perfschema::write_row(const uchar *buf) +{ + int result; + + DBUG_ENTER("ha_perfschema::write_row"); + if (!PFS_ENABLED()) + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + + DBUG_ASSERT(m_table_share); + result= m_table_share->write_row(table, buf, table->field); + DBUG_RETURN(result); +} + +void ha_perfschema::use_hidden_primary_key(void) +{ + /* + This is also called in case of row based replication, + see TABLE::mark_columns_needed_for_update(). + Add all columns to the read set, but do not touch the write set, + as some columns in the SETUP_ tables are not writable. + */ + table->column_bitmaps_set_no_signal(&table->s->all_set, table->write_set); +} + +int ha_perfschema::update_row(const uchar *old_data, const uchar *new_data) +{ + DBUG_ENTER("ha_perfschema::update_row"); + if (!PFS_ENABLED()) + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + + if (is_executed_by_slave()) + DBUG_RETURN(0); + + DBUG_ASSERT(m_table); + int result= m_table->update_row(table, old_data, new_data, table->field); + DBUG_RETURN(result); +} + +int ha_perfschema::delete_row(const uchar *buf) +{ + DBUG_ENTER("ha_perfschema::delete_row"); + if (!PFS_ENABLED()) + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + + DBUG_ASSERT(m_table); + int result= m_table->delete_row(table, buf, table->field); + DBUG_RETURN(result); +} + +int ha_perfschema::rnd_init(bool scan) +{ + int result; + DBUG_ENTER("ha_perfschema::rnd_init"); + + assert(m_table_share); + assert(m_table_share->m_open_table != NULL); + + stats.records= 0; + if (m_table == NULL) + m_table= m_table_share->m_open_table(); + else + m_table->reset_position(); + + if (m_table != NULL) + m_table->rnd_init(scan); + + result= m_table ? 0 : HA_ERR_OUT_OF_MEM; + DBUG_RETURN(result); +} + +int ha_perfschema::rnd_end(void) +{ + DBUG_ENTER("ha_perfschema::rnd_end"); + assert(m_table); + delete m_table; + m_table= NULL; + DBUG_RETURN(0); +} + +int ha_perfschema::rnd_next(uchar *buf) +{ + DBUG_ENTER("ha_perfschema::rnd_next"); + if (!PFS_ENABLED()) + { + table->status= STATUS_NOT_FOUND; + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + DBUG_ASSERT(m_table); + + int result= m_table->rnd_next(); + if (result == 0) + { + result= m_table->read_row(table, buf, table->field); + if (result == 0) + stats.records++; + } + table->status= (result ? STATUS_NOT_FOUND : 0); + DBUG_RETURN(result); +} + +void ha_perfschema::position(const uchar *record) +{ + DBUG_ENTER("ha_perfschema::position"); + + assert(m_table); + m_table->get_position(ref); + DBUG_VOID_RETURN; +} + +int ha_perfschema::rnd_pos(uchar *buf, uchar *pos) +{ + DBUG_ENTER("ha_perfschema::rnd_pos"); + if (!PFS_ENABLED()) + { + table->status= STATUS_NOT_FOUND; + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + DBUG_ASSERT(m_table); + int result= m_table->rnd_pos(pos); + if (result == 0) + result= m_table->read_row(table, buf, table->field); + table->status= (result ? STATUS_NOT_FOUND : 0); + DBUG_RETURN(result); +} + +int ha_perfschema::info(uint flag) +{ + DBUG_ENTER("ha_perfschema::info"); + assert(m_table_share); + if (flag & HA_STATUS_VARIABLE) + stats.records= m_table_share->get_row_count(); + if (flag & HA_STATUS_CONST) + ref_length= m_table_share->m_ref_length; + DBUG_RETURN(0); +} + +int ha_perfschema::delete_all_rows(void) +{ + int result; + + DBUG_ENTER("ha_perfschema::delete_all_rows"); + if (!PFS_ENABLED()) + DBUG_RETURN(0); + + if (is_executed_by_slave()) + DBUG_RETURN(0); + + assert(m_table_share); + if (m_table_share->m_delete_all_rows) + result= m_table_share->m_delete_all_rows(); + else + { + result= HA_ERR_WRONG_COMMAND; + } + DBUG_RETURN(result); +} + +int ha_perfschema::truncate() +{ + return delete_all_rows(); +} + +THR_LOCK_DATA **ha_perfschema::store_lock(THD *thd, + THR_LOCK_DATA **to, + enum thr_lock_type lock_type) +{ + if (lock_type != TL_IGNORE && m_thr_lock.type == TL_UNLOCK) + m_thr_lock.type= lock_type; + *to++= &m_thr_lock; + m_thr_lock.m_psi= m_psi; + return to; +} + +int ha_perfschema::delete_table(const char *name) +{ + DBUG_ENTER("ha_perfschema::delete_table"); + + /* + The name string looks like: + "./performance_schema/processlist" + + Make a copy of it, parse the '/' to + isolate the schema and table name. + */ + + char table_path[FN_REFLEN+1]; + strncpy(table_path, name, sizeof(table_path)); + table_path[FN_REFLEN]='\0'; + + char *ptr; + char *table_name; + char *db_name; + const PFS_engine_table_share *share; + + /* Start scan from the end. */ + ptr = strend(table_path) - 1; + + /* Find path separator */ + while ((ptr >= table_path) && (*ptr != '\\') && (*ptr != '/')) { + ptr--; + } + + table_name = ptr + 1; + *ptr = '\0'; + + /* Find path separator */ + while ((ptr >= table_path) && (*ptr != '\\') && (*ptr != '/')) { + ptr--; + } + + db_name = ptr + 1; + + share = find_table_share(db_name, table_name); + if (share != NULL) { + if (share->m_optional) { + /* + An optional table is deleted, + disarm the checked flag so we don't trust it any more. + */ + share->m_state->m_checked = false; + } + } + + DBUG_RETURN(0); +} + +int ha_perfschema::rename_table(const char * from, const char * to) +{ + DBUG_ENTER("ha_perfschema::rename_table "); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); +} + +int ha_perfschema::create(const char *name, TABLE *table_arg, + HA_CREATE_INFO *create_info) +{ + DBUG_ENTER("ha_perfschema::create"); + /* + This is not a general purpose engine. + Failure to CREATE TABLE is the expected result. + */ + DBUG_RETURN(HA_ERR_WRONG_COMMAND); +} + +void ha_perfschema::print_error(int error, myf errflag) +{ + switch (error) + { + case HA_ERR_TABLE_NEEDS_UPGRADE: + /* + The error message for ER_TABLE_NEEDS_UPGRADE refers to REPAIR table, + which does not apply to performance schema tables. + */ + my_error(ER_WRONG_NATIVE_TABLE_STRUCTURE, MYF(0), + table_share->db.str, table_share->table_name.str); + break; + case HA_ERR_WRONG_COMMAND: + /* + The performance schema is not a general purpose storage engine, + some operations are not supported, by design. + We do not want to print "Command not supported", + which gives the impression that a command implementation is missing, + and that the failure should be considered a bug. + We print "Invalid performance_schema usage." instead, + to emphasise that the operation attempted is not meant to be legal, + and that the failure returned is indeed the expected result. + */ + my_error(ER_WRONG_PERFSCHEMA_USAGE, MYF(0)); + break; + default: + handler::print_error(error, errflag); + break; + } +} + diff --git a/storage/perfschema/ha_perfschema.h b/storage/perfschema/ha_perfschema.h new file mode 100644 index 00000000..d8ef9e00 --- /dev/null +++ b/storage/perfschema/ha_perfschema.h @@ -0,0 +1,251 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef HA_PERFSCHEMA_H +#define HA_PERFSCHEMA_H + +#include "handler.h" /* class handler */ +#include "table.h" +#include "sql_class.h" + +/** + @file storage/perfschema/ha_perfschema.h + Performance schema storage engine (declarations). + + @defgroup Performance_schema_engine Performance Schema Engine + @ingroup Performance_schema_implementation + @{ +*/ +struct PFS_engine_table_share; +class PFS_engine_table; +/** Name of the performance schema engine. */ +extern const char *pfs_engine_name; + +/** A handler for a PERFORMANCE_SCHEMA table. */ +class ha_perfschema final : public handler +{ +public: + /** + Create a new performance schema table handle on a table. + @param hton storage engine handler singleton + @param share table share + */ + ha_perfschema(handlerton *hton, TABLE_SHARE *share); + + ~ha_perfschema(); + + const char *index_type(uint) { return ""; } + + /** Capabilities of the performance schema tables. */ + ulonglong table_flags(void) const + { + /* + About HA_FAST_KEY_READ: + + The storage engine ::rnd_pos() method is fast to locate records by key, + so HA_FAST_KEY_READ is technically true, but the record content can be + overwritten between ::rnd_next() and ::rnd_pos(), because all the P_S + data is volatile. + The HA_FAST_KEY_READ flag is not advertised, to force the optimizer + to cache records instead, to provide more consistent records. + For example, consider the following statement: + - select * from P_S.EVENTS_WAITS_HISTORY_LONG where THREAD_ID=<n> + order by ... + With HA_FAST_KEY_READ, it can return records where "THREAD_ID=<n>" + is false, because the where clause was evaluated to true after + ::rnd_pos(), then the content changed, then the record was fetched by + key using ::rnd_pos(). + Without HA_FAST_KEY_READ, the optimizer reads all columns and never + calls ::rnd_pos(), so it is guaranteed to return only thread <n> + records. + */ + return HA_NO_TRANSACTIONS | HA_REC_NOT_IN_SEQ | HA_NO_AUTO_INCREMENT | + HA_PRIMARY_KEY_REQUIRED_FOR_DELETE; + } + + /** + Operations supported by indexes. + None, there are no indexes. + */ + ulong index_flags(uint , uint , bool ) const + { return 0; } + + uint max_supported_record_length(void) const + { return HA_MAX_REC_LENGTH; } + + uint max_supported_keys(void) const + { return 0; } + + uint max_supported_key_parts(void) const + { return 0; } + + uint max_supported_key_length(void) const + { return 0; } + + ha_rows estimate_rows_upper_bound(void) + { return HA_POS_ERROR; } + + double scan_time(void) + { return 1.0; } + + /** + Open a performance schema table. + @param name the table to open + @param mode unused + @param test_if_locked unused + @return 0 on success + */ + int open(const char *name, int mode, uint test_if_locked); + + /** + Close a table handle. + @sa open. + */ + int close(void); + + /** + Write a row. + @param buf the row to write + @return 0 on success + */ + int write_row(const uchar *buf); + + void use_hidden_primary_key(); + + /** + Update a row. + @param old_data the row old values + @param new_data the row new values + @return 0 on success + */ + int update_row(const uchar *old_data, const uchar *new_data); + + /** + Delete a row. + @param buf the row to delete + @return 0 on success + */ + int delete_row(const uchar *buf); + + int rnd_init(bool scan); + + /** + Scan end. + @sa rnd_init. + */ + int rnd_end(void); + + /** + Iterator, fetch the next row. + @param[out] buf the row fetched. + @return 0 on success + */ + int rnd_next(uchar *buf); + + /** + Iterator, fetch the row at a given position. + @param[out] buf the row fetched. + @param pos the row position + @return 0 on success + */ + int rnd_pos(uchar *buf, uchar *pos); + + /** + Read the row current position. + @param record the current row + */ + void position(const uchar *record); + + int info(uint); + + int delete_all_rows(void); + + int truncate(); + + int delete_table(const char *from); + + int rename_table(const char * from, const char * to); + + int create(const char *name, TABLE *form, + HA_CREATE_INFO *create_info); + + THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, + enum thr_lock_type lock_type); + + virtual uint8 table_cache_type(void) + { return HA_CACHE_TBL_NOCACHE; } + + virtual my_bool register_query_cache_table + (THD *, const char *, uint , qc_engine_callback *engine_callback, + ulonglong *) + { + *engine_callback= 0; + return FALSE; + } + + virtual void print_error(int error, myf errflags); + +private: + /** + Check if the caller is a replication thread or the caller is called + by a client thread executing base64 encoded BINLOG'... statement. + + In theory, performance schema tables are not supposed to be replicated. + This is true and enforced starting with MySQL 5.6.10. + In practice, in previous versions such as MySQL 5.5 (GA) or earlier 5.6 + (non GA) DML on performance schema tables could end up written in the binlog, + both in STATEMENT and ROW format. + While these records are not supposed to be there, they are found when: + - performing replication from a 5.5 master to a 5.6 slave during + upgrades + - performing replication from 5.5 (performance_schema enabled) + to a 5.6 slave + - performing point in time recovery in 5.6 with old archived logs. + + This API detects when the code calling the performance schema storage + engine is a slave thread or whether the code calling isthe client thread + executing a BINLOG'.. statement. + + This API acts as a late filter for the above mentioned cases. + + For ROW format, @see Rows_log_event::do_apply_event() + + */ + bool is_executed_by_slave() const + { + assert(table != NULL); + assert(table->in_use != NULL); + return table->in_use->slave_thread; + + } + + /** MySQL lock */ + THR_LOCK_DATA m_thr_lock; + /** Performance schema table share for this table handler. */ + const PFS_engine_table_share *m_table_share; + /** Performance schema table cursor. */ + PFS_engine_table *m_table; +}; + +/** @} */ +#endif + diff --git a/storage/perfschema/my_thread.h b/storage/perfschema/my_thread.h new file mode 100644 index 00000000..c1a079ce --- /dev/null +++ b/storage/perfschema/my_thread.h @@ -0,0 +1,137 @@ +#ifndef STORAGE_PERFSCHEMA_MY_THREAD_INCLUDED +#define STORAGE_PERFSCHEMA_MY_THREAD_INCLUDED + +#include <my_pthread.h> +#include <m_string.h> +#include "pfs_config.h" + +#ifdef HAVE_SYS_GETTID +#include <sys/types.h> +#include <sys/syscall.h> +#endif + +#ifdef HAVE_PTHREAD_GETTHREADID_NP +#include <pthread_np.h> +#endif + +#if defined(HAVE_INTEGER_PTHREAD_SELF) +#include <cstdint> +#endif + +typedef pthread_key_t thread_local_key_t; +typedef pthread_t my_thread_handle; +typedef pthread_attr_t my_thread_attr_t; +#if defined(HAVE_PTHREAD_THREADID_NP) || defined(HAVE_GETTID) || defined(HAVE_SYS_GETTID) || defined(HAVE_GETTHRID) +typedef pid_t my_thread_os_id_t; +#elif defined(_WIN32) +typedef uint32 my_thread_os_id_t; +#elif defined(HAVE_PTHREAD_GETTHREADID_NP) +typedef int my_thread_os_id_t; +#elif defined(HAVE_INTEGER_PTHREAD_SELF) +typedef uintptr_t my_thread_os_id_t; +#else +typedef unsigned long long my_thread_os_id_t; +#endif + +#define LOCK_plugin_delete LOCK_plugin + +static inline int my_create_thread_local_key(thread_local_key_t *key, void (*destructor)(void*)) +{ return pthread_key_create(key, destructor); } + +static inline int my_delete_thread_local_key(thread_local_key_t key) +{ return pthread_key_delete(key); } + +static inline void *my_get_thread_local(thread_local_key_t key) +{ return pthread_getspecific(key); } + +static inline int my_set_thread_local(thread_local_key_t key, const void *ptr) +{ return pthread_setspecific(key, ptr); } + +static inline int my_thread_create(my_thread_handle *thread, + const my_thread_attr_t *attr, void *(*start_routine)(void *), void *arg) +{ return pthread_create(thread, attr, start_routine, arg); } + +static inline my_thread_os_id_t my_thread_os_id() +{ +#ifdef HAVE_PTHREAD_THREADID_NP + /* + macOS. + + Be careful to use this version first, and to not use SYS_gettid on macOS, + as SYS_gettid has a different meaning compared to linux gettid(). + */ + uint64_t tid64; + pthread_threadid_np(nullptr, &tid64); + return (pid_t)tid64; +#else +#ifdef HAVE_GETTID + /* Linux glibc-2.30+ */ + return gettid(); +#else +#ifdef HAVE_SYS_GETTID + /* + Linux before glibc-2.30 + See man gettid + */ + return syscall(SYS_gettid); +#else +#ifdef _WIN32 + /* Windows */ + return GetCurrentThreadId(); +#else +#ifdef HAVE_PTHREAD_GETTHREADID_NP + /* FreeBSD 10.2 */ + return pthread_getthreadid_np(); +#else +#ifdef HAVE_GETTHRID + /* OpenBSD */ + return getthrid(); +#else +#ifdef HAVE_INTEGER_PTHREAD_SELF + /* NetBSD, and perhaps something else, fallback. */ + return (my_thread_os_id_t) pthread_self(); +#else + /* Feature not available. */ + return 0; +#endif /* HAVE_INTEGER_PTHREAD_SELF */ +#endif /* HAVE_GETTHRID */ +#endif /* HAVE_PTHREAD_GETTHREADID_NP */ +#endif /* _WIN32 */ +#endif /* HAVE_SYS_GETTID */ +#endif /* HAVE_GETTID */ +#endif /* HAVE_PTHREAD_THREADID_NP */ +} + +#define CHANNEL_NAME_LENGTH MAX_CONNECTION_NAME + +enum enum_mysql_show_scope +{ + SHOW_SCOPE_UNDEF, + SHOW_SCOPE_GLOBAL, + SHOW_SCOPE_SESSION, + SHOW_SCOPE_ALL +}; +typedef enum enum_mysql_show_scope SHOW_SCOPE; + +#define SHOW_VAR_MAX_NAME_LEN NAME_LEN + +static inline char *my_stpnmov(char *dst, const char *src, size_t n) +{ return strnmov(dst, src, n); } + +static inline size_t bin_to_hex_str(char *to, size_t to_len, + const char *from, size_t from_len) +{ + if (to_len < from_len * 2 + 1) + return 0 ; + for (size_t i=0; i < from_len; i++, from++) + { + *to++=_dig_vec_upper[((unsigned char) *from) >> 4]; + *to++=_dig_vec_upper[((unsigned char) *from) & 0xF]; + } + *to= '\0'; + return from_len * 2 + 1; +} + +#define thd_get_psi(X) ((X)->get_psi()) + +#endif diff --git a/storage/perfschema/mysqld_thd_manager.cc b/storage/perfschema/mysqld_thd_manager.cc new file mode 100644 index 00000000..61282b7e --- /dev/null +++ b/storage/perfschema/mysqld_thd_manager.cc @@ -0,0 +1,39 @@ +#include "mysqld_thd_manager.h" +#include "sql_class.h" + +static Global_THD_manager manager; +Global_THD_manager* Global_THD_manager::get_instance() +{ + return &manager; +} + +struct find_thd_arg +{ + Find_THD_Impl *func; + THD *cur; +}; + +static my_bool find_thd_cb(THD *tmp, find_thd_arg *arg) +{ + arg->cur= tmp; + return (*arg->func)(tmp); +} + +THD* Global_THD_manager::find_thd(Find_THD_Impl *func) +{ + find_thd_arg arg= {func, NULL}; + if (THD_list_iterator::iterator()->iterate(find_thd_cb, &arg)) + return arg.cur; + return NULL; +} + +static my_bool do_for_all_cb(THD *tmp, Do_THD_Impl *arg) +{ + (*arg)(tmp); + return 0; +} + +void Global_THD_manager::do_for_all_thd(Do_THD_Impl *arg) +{ + THD_list_iterator::iterator()->iterate(do_for_all_cb, arg); +} diff --git a/storage/perfschema/mysqld_thd_manager.h b/storage/perfschema/mysqld_thd_manager.h new file mode 100644 index 00000000..fbb6f86a --- /dev/null +++ b/storage/perfschema/mysqld_thd_manager.h @@ -0,0 +1,29 @@ +#ifndef STORAGE_PERFSCHEMA_MYSQL_THD_MANAGER_INCLUDED +#define STORAGE_PERFSCHEMA_MYSQL_THD_MANAGER_INCLUDED +#include "my_global.h" +#include "my_thread.h" + +class Find_THD_Impl +{ + public: + virtual ~Find_THD_Impl() {} + virtual bool operator()(THD *thd) = 0; +}; + +class Do_THD_Impl +{ + public: + virtual ~Do_THD_Impl() {} + virtual void operator()(THD*) = 0; +}; + +class Global_THD_manager +{ + public: + static Global_THD_manager* get_instance(); + THD* find_thd(Find_THD_Impl *func); + void do_for_all_thd(Do_THD_Impl *arg); +}; + +ulong get_system_variable_hash_records(void); +#endif diff --git a/storage/perfschema/pfs.cc b/storage/perfschema/pfs.cc new file mode 100644 index 00000000..1373f9dd --- /dev/null +++ b/storage/perfschema/pfs.cc @@ -0,0 +1,7169 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs.cc + The performance schema implementation of all instruments. +*/ +#include "my_global.h" +#include "thr_lock.h" + +/* Make sure exported prototypes match the implementation. */ +#include "pfs_file_provider.h" +#include "pfs_idle_provider.h" +#include "pfs_memory_provider.h" +#include "pfs_metadata_provider.h" +#include "pfs_socket_provider.h" +#include "pfs_stage_provider.h" +#include "pfs_statement_provider.h" +#include "pfs_table_provider.h" +#include "pfs_thread_provider.h" +#include "pfs_transaction_provider.h" + +#include "mysql/psi/psi.h" +#include "mysql/psi/mysql_thread.h" +#include "my_pthread.h" +#include "sql_const.h" +#include "pfs.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_host.h" +#include "pfs_user.h" +#include "pfs_account.h" +#include "pfs_global.h" +#include "pfs_column_values.h" +#include "pfs_timer.h" +#include "pfs_events_waits.h" +#include "pfs_events_stages.h" +#include "pfs_events_statements.h" +#include "pfs_events_transactions.h" +#include "pfs_setup_actor.h" +#include "pfs_setup_object.h" +#include "sql_error.h" +#include "sp_head.h" +#include "mdl.h" /* mdl_key_init */ +#include "pfs_digest.h" +#include "pfs_program.h" +#include "pfs_prepared_stmt.h" + +using std::min; + +/* + This is a development tool to investigate memory statistics, + do not use in production. +*/ +#undef PFS_PARANOID + +#ifdef PFS_PARANOID +static void report_memory_accounting_error( + const char *api_name, + PFS_thread *new_thread, + size_t size, + PFS_memory_class *klass, + PFS_thread *old_thread) +{ + pfs_print_error("%s " + "thread <%d> of class <%s> " + "not owner of <%d> bytes in class <%s> " + "allocated by thread <%d> of class <%s>\n", + api_name, + new_thread->m_thread_internal_id, + new_thread->m_class->m_name, + size, klass->m_name, + old_thread->m_thread_internal_id, + old_thread->m_class->m_name); + + assert(strcmp(new_thread->m_class->m_name, "thread/sql/event_worker") != 0); + assert(strcmp(new_thread->m_class->m_name, "thread/sql/event_scheduler") != 0); + assert(strcmp(new_thread->m_class->m_name, "thread/sql/one_connection") != 0); +} +#endif /* PFS_PARANOID */ + +/** + @page PAGE_PERFORMANCE_SCHEMA The Performance Schema main page + MySQL PERFORMANCE_SCHEMA implementation. + + @section INTRO Introduction + The PERFORMANCE_SCHEMA is a way to introspect the internal execution of + the server at runtime. + The performance schema focuses primarily on performance data, + as opposed to the INFORMATION_SCHEMA whose purpose is to inspect metadata. + + From a user point of view, the performance schema consists of: + - a dedicated database schema, named PERFORMANCE_SCHEMA, + - SQL tables, used to query the server internal state or change + configuration settings. + + From an implementation point of view, the performance schema is a dedicated + Storage Engine which exposes data collected by 'Instrumentation Points' + placed in the server code. + + @section INTERFACES Multiple interfaces + + The performance schema exposes many different interfaces, + for different components, and for different purposes. + + @subsection INT_INSTRUMENTING Instrumenting interface + + All the data representing the server internal state exposed + in the performance schema must be first collected: + this is the role of the instrumenting interface. + The instrumenting interface is a coding interface provided + by implementors (of the performance schema) to implementors + (of the server or server components). + + This interface is available to: + - C implementations + - C++ implementations + - the core SQL layer (/sql) + - the mysys library (/mysys) + - MySQL plugins, including storage engines, + - third party plugins, including third party storage engines. + + For details, see the @ref PAGE_INSTRUMENTATION_INTERFACE + "instrumentation interface page". + + @subsection INT_COMPILING Compiling interface + + The implementation of the performance schema can be enabled or disabled at + build time, when building MySQL from the source code. + + When building with the performance schema code, some compilation flags + are available to change the default values used in the code, if required. + + For more details, see: + @verbatim ./configure --help @endverbatim + + To compile with the performance schema: + @verbatim ./configure --with-perfschema @endverbatim + + The implementation of all the compiling options is located in + @verbatim ./storage/perfschema/plug.in @endverbatim + + @subsection INT_STARTUP Server startup interface + + The server startup interface consists of the "./mysqld ..." + command line used to start the server. + When the performance schema is compiled in the server binary, + extra command line options are available. + + These extra start options allow the DBA to: + - enable or disable the performance schema + - specify some sizing parameters. + + To see help for the performance schema startup options, see: + @verbatim ./sql/mysqld --verbose --help @endverbatim + + The implementation of all the startup options is located in + @verbatim ./sql/mysqld.cc, my_long_options[] @endverbatim + + @subsection INT_BOOTSTRAP Server bootstrap interface + + The bootstrap interface is a private interface exposed by + the performance schema, and used by the SQL layer. + Its role is to advertise all the SQL tables natively + supported by the performance schema to the SQL server. + The code consists of creating MySQL tables for the + performance schema itself, and is used in './mysql --bootstrap' + mode when a server is installed. + + The implementation of the database creation script is located in + @verbatim ./scripts/mysql_performance_tables.sql @endverbatim + + @subsection INT_CONFIG Runtime configuration interface + + When the performance schema is used at runtime, various configuration + parameters can be used to specify what kind of data is collected, + what kind of aggregations are computed, what kind of timers are used, + what events are timed, etc. + + For all these capabilities, not a single statement or special syntax + was introduced in the parser. + Instead of new SQL statements, the interface consists of DML + (SELECT, INSERT, UPDATE, DELETE) against special "SETUP" tables. + + For example: + @verbatim mysql> update performance_schema.SETUP_INSTRUMENTS + set ENABLED='YES', TIMED='YES'; + Query OK, 234 rows affected (0.00 sec) + Rows matched: 234 Changed: 234 Warnings: 0 @endverbatim + + @subsection INT_STATUS Internal audit interface + + The internal audit interface is provided to the DBA to inspect if the + performance schema code itself is functioning properly. + This interface is necessary because a failure caused while + instrumenting code in the server should not cause failures in the + MySQL server itself, so that the performance schema implementation + never raises errors during runtime execution. + + This auditing interface consists of: + @verbatim SHOW ENGINE PERFORMANCE_SCHEMA STATUS; @endverbatim + It displays data related to the memory usage of the performance schema, + as well as statistics about lost events, if any. + + The SHOW STATUS command is implemented in + @verbatim ./storage/perfschema/pfs_engine_table.cc @endverbatim + + @subsection INT_QUERY Query interface + + The query interface is used to query the internal state of a running server. + It is provided as SQL tables. + + For example: + @verbatim mysql> select * from performance_schema.EVENTS_WAITS_CURRENT; + @endverbatim + + @section DESIGN_PRINCIPLES Design principles + + @subsection PRINCIPLE_BEHAVIOR No behavior changes + + The primary goal of the performance schema is to measure (instrument) the + execution of the server. A good measure should not cause any change + in behavior. + + To achieve this, the overall design of the performance schema complies + with the following very severe design constraints: + + The parser is unchanged. There are no new keywords, no new statements. + This guarantees that existing applications will run the same way with or + without the performance schema. + + All the instrumentation points return "void", there are no error codes. + Even if the performance schema internally fails, execution of the server + code will proceed. + + None of the instrumentation points allocate memory. + All the memory used by the performance schema is pre-allocated at startup, + and is considered "static" during the server life time. + + None of the instrumentation points use any pthread_mutex, pthread_rwlock, + or pthread_cond (or platform equivalents). + Executing the instrumentation point should not cause thread scheduling to + change in the server. + + In other words, the implementation of the instrumentation points, + including all the code called by the instrumentation points, is: + - malloc free + - mutex free + - rwlock free + + TODO: All the code located in storage/perfschema is malloc free, + but unfortunately the usage of LF_HASH introduces some memory allocation. + This should be revised if possible, to use a lock-free, + malloc-free hash code table. + + @subsection PRINCIPLE_PERFORMANCE No performance hit + + The instrumentation of the server should be as fast as possible. + In cases when there are choices between: + - doing some processing when recording the performance data + in the instrumentation, + - doing some processing when retrieving the performance data, + + priority is given in the design to make the instrumentation faster, + pushing some complexity to data retrieval. + + As a result, some parts of the design, related to: + - the setup code path, + - the query code path, + + might appear to be sub-optimal. + + The criterion used here is to optimize primarily the critical path (data + collection), possibly at the expense of non-critical code paths. + + @subsection PRINCIPLE_NOT_INTRUSIVE Unintrusive instrumentation + + For the performance schema in general to be successful, the barrier + of entry for a developer should be low, so it's easy to instrument code. + + In particular, the instrumentation interface: + - is available for C and C++ code (so it's a C interface), + - does not require parameters that the calling code can't easily provide, + - supports partial instrumentation (for example, instrumenting mutexes does + not require that every mutex is instrumented) + + @subsection PRINCIPLE_EXTENDABLE Extendable instrumentation + + As the content of the performance schema improves, + with more tables exposed and more data collected, + the instrumentation interface will also be augmented + to support instrumenting new concepts. + Existing instrumentations should not be affected when additional + instrumentation is made available, and making a new instrumentation + available should not require existing instrumented code to support it. + + @subsection PRINCIPLE_VERSIONED Versioned instrumentation + + Given that the instrumentation offered by the performance schema will + be augmented with time, when more features are implemented, + the interface itself should be versioned, to keep compatibility + with previous instrumented code. + + For example, after both plugin-A and plugin-B have been instrumented for + mutexes, read write locks and conditions, using the instrumentation + interface, we can anticipate that the instrumentation interface + is expanded to support file based operations. + + Plugin-A, a file based storage engine, will most likely use the expanded + interface and instrument its file usage, using the version 2 + interface, while Plugin-B, a network based storage engine, will not change + its code and not release a new binary. + + When later the instrumentation interface is expanded to support network + based operations (which will define interface version 3), the Plugin-B code + can then be changed to make use of it. + + Note, this is just an example to illustrate the design concept here. + Both mutexes and file instrumentation are already available + since version 1 of the instrumentation interface. + + @subsection PRINCIPLE_DEPLOYMENT Easy deployment + + Internally, we might want every plugin implementation to upgrade the + instrumented code to the latest available, but this will cause additional + work and this is not practical if the code change is monolithic. + + Externally, for third party plugin implementors, asking implementors to + always stay aligned to the latest instrumentation and make new releases, + even when the change does not provide new functionality for them, + is a bad idea. + + For example, requiring a network based engine to re-release because the + instrumentation interface changed for file based operations, will create + too many deployment issues. + + So, the performance schema implementation must support concurrently, + in the same deployment, multiple versions of the instrumentation + interface, and ensure binary compatibility with each version. + + In addition to this, the performance schema can be included or excluded + from the server binary, using build time configuration options. + + Regardless, the following types of deployment are valid: + - a server supporting the performance schema + a storage engine + that is not instrumented + - a server not supporting the performance schema + a storage engine + that is instrumented +*/ + +/** + @page PAGE_INSTRUMENTATION_INTERFACE Performance schema: instrumentation interface page. + MySQL performance schema instrumentation interface. + + @section INTRO Introduction + + The instrumentation interface consist of two layers: + - a raw ABI (Application Binary Interface) layer, that exposes the primitive + instrumentation functions exported by the performance schema instrumentation + - an API (Application Programing Interface) layer, + that provides many helpers for a developer instrumenting some code, + to make the instrumentation as easy as possible. + + The ABI layer consists of: +@code +#include "mysql/psi/psi.h" +@endcode + + The API layer consists of: +@code +#include "mysql/psi/mutex_mutex.h" +#include "mysql/psi/mutex_file.h" +@endcode + + The first helper is for mutexes, rwlocks and conditions, + the second for file io. + + The API layer exposes C macros and typedefs which will expand: + - either to non-instrumented code, when compiled without the performance + schema instrumentation + - or to instrumented code, that will issue the raw calls to the ABI layer + so that the implementation can collect data. + + Note that all the names introduced (for example, @c mysql_mutex_lock) do not + collide with any other namespace. + In particular, the macro @c mysql_mutex_lock is on purpose not named + @c pthread_mutex_lock. + This is to: + - avoid overloading @c pthread_mutex_lock with yet another macro, + which is dangerous as it can affect user code and pollute + the end-user namespace. + - allow the developer instrumenting code to selectively instrument + some code but not all. + + @section PRINCIPLES Design principles + + The ABI part is designed as a facade, that exposes basic primitives. + The expectation is that each primitive will be very stable over time, + but the list will constantly grow when more instruments are supported. + To support binary compatibility with plugins compiled with a different + version of the instrumentation, the ABI itself is versioned + (see @c PSI_v1, @c PSI_v2). + + For a given instrumentation point in the API, the basic coding pattern + used is: + - (a) notify the performance schema of the operation + about to be performed. + - (b) execute the instrumented code. + - (c) notify the performance schema that the operation + is completed. + + An opaque "locker" pointer is returned by (a), that is given to (c). + This pointer helps the implementation to keep context, for performances. + + The following code fragment is annotated to show how in detail this pattern + in implemented, when the instrumentation is compiled in: + +@verbatim +static inline int mysql_mutex_lock( + mysql_mutex_t *that, myf flags, const char *src_file, uint src_line) +{ + int result; + struct PSI_mutex_locker_state state; + struct PSI_mutex_locker *locker= NULL; + + ............... (a) + locker= PSI_MUTEX_CALL(start_mutex_wait)(&state, that->p_psi, PSI_MUTEX_LOCK, + locker, src_file, src_line); + + ............... (b) + result= pthread_mutex_lock(&that->m_mutex); + + ............... (c) + PSI_MUTEX_CALL(end_mutex_wait)(locker, result); + + return result; +} +@endverbatim + + When the performance schema instrumentation is not compiled in, + the code becomes simply a wrapper, expanded in line by the compiler: + +@verbatim +static inline int mysql_mutex_lock(...) +{ + int result; + + ............... (b) + result= pthread_mutex_lock(&that->m_mutex); + + return result; +} +@endverbatim + + When the performance schema instrumentation is compiled in, + and when the code compiled is internal to the server implementation, + PSI_MUTEX_CALL expands directly to functions calls in the performance schema, + to make (a) and (c) calls as efficient as possible. + +@verbatim +static inline int mysql_mutex_lock(...) +{ + int result; + struct PSI_mutex_locker_state state; + struct PSI_mutex_locker *locker= NULL; + + ............... (a) + locker= pfs_start_mutex_wait_v1(&state, that->p_psi, PSI_MUTEX_LOCK, + locker, src_file, src_line); + + ............... (b) + result= pthread_mutex_lock(&that->m_mutex); + + ............... (c) + pfs_end_mutex_wait_v1(locker, result); + + return result; +} +@endverbatim + + When the performance schema instrumentation is compiled in, + and when the code compiled is external to the server implementation + (typically, a dynamic plugin), + PSI_MUTEX_CALL expands to dynamic calls to the underlying implementation, + using the PSI_server entry point. + This makes (a) and (c) slower, as a function pointer is used instead of a static call, + but also independent of the implementation, for binary compatibility. + +@verbatim +static inline int mysql_mutex_lock(...) +{ + int result; + struct PSI_mutex_locker_state state; + struct PSI_mutex_locker *locker= NULL; + + ............... (a) + locker= PSI_server->start_mutex_wait(&state, that->p_psi, PSI_MUTEX_LOCK, + locker, src_file, src_line); + + ............... (b) + result= pthread_mutex_lock(&that->m_mutex); + + ............... (c) + PSI_server->end_mutex_wait(locker, result); + + return result; +} +@endverbatim + +*/ + +/** + @page PAGE_AGGREGATES Performance schema: the aggregates page. + Performance schema aggregates. + + @section INTRO Introduction + + Aggregates tables are tables that can be formally defined as + SELECT ... from EVENTS_WAITS_HISTORY_INFINITE ... group by 'group clause'. + + Each group clause defines a different kind of aggregate, and corresponds to + a different table exposed by the performance schema. + + Aggregates can be either: + - computed on the fly, + - computed on demand, based on other available data. + + 'EVENTS_WAITS_HISTORY_INFINITE' is a table that does not exist, + the best approximation is EVENTS_WAITS_HISTORY_LONG. + Aggregates computed on the fly in fact are based on EVENTS_WAITS_CURRENT, + while aggregates computed on demand are based on other + EVENTS_WAITS_SUMMARY_BY_xxx tables. + + To better understand the implementation itself, a bit of math is + required first, to understand the model behind the code: + the code is deceptively simple, the real complexity resides + in the flyweight of pointers between various performance schema buffers. + + @section DIMENSION Concept of dimension + + An event measured by the instrumentation has many attributes. + An event is represented as a data point P(x1, x2, ..., xN), + where each x_i coordinate represents a given attribute value. + + Examples of attributes are: + - the time waited + - the object waited on + - the instrument waited on + - the thread that waited + - the operation performed + - per object or per operation additional attributes, such as spins, + number of bytes, etc. + + Computing an aggregate per thread is fundamentally different from + computing an aggregate by instrument, so the "_BY_THREAD" and + "_BY_EVENT_NAME" aggregates are different dimensions, + operating on different x_i and x_j coordinates. + These aggregates are "orthogonal". + + @section PROJECTION Concept of projection + + A given x_i attribute value can convey either just one basic information, + such as a number of bytes, or can convey implied information, + such as an object fully qualified name. + + For example, from the value "test.t1", the name of the object schema + "test" can be separated from the object name "t1", so that now aggregates + by object schema can be implemented. + + In math terms, that corresponds to defining a function: + F_i (x): x --> y + Applying this function to our point P gives another point P': + + F_i (P): + P(x1, x2, ..., x{i-1}, x_i, x{i+1}, ..., x_N) + --> P' (x1, x2, ..., x{i-1}, f_i(x_i), x{i+1}, ..., x_N) + + That function defines in fact an aggregate ! + In SQL terms, this aggregate would look like the following table: + +@verbatim + CREATE VIEW EVENTS_WAITS_SUMMARY_BY_Func_i AS + SELECT col_1, col_2, ..., col_{i-1}, + Func_i(col_i), + COUNT(col_i), + MIN(col_i), AVG(col_i), MAX(col_i), -- if col_i is a numeric value + col_{i+1}, ..., col_N + FROM EVENTS_WAITS_HISTORY_INFINITE + group by col_1, col_2, ..., col_{i-1}, col{i+1}, ..., col_N. +@endverbatim + + Note that not all columns have to be included, + in particular some columns that are dependent on the x_i column should + be removed, so that in practice, MySQL's aggregation method tends to + remove many attributes at each aggregation steps. + + For example, when aggregating wait events by object instances, + - the wait_time and number_of_bytes can be summed, + and sum(wait_time) now becomes an object instance attribute. + - the source, timer_start, timer_end columns are not in the + _BY_INSTANCE table, because these attributes are only + meaningful for a wait. + + @section COMPOSITION Concept of composition + + Now, the "test.t1" --> "test" example was purely theory, + just to explain the concept, and does not lead very far. + Let's look at a more interesting example of data that can be derived + from the row event. + + An event creates a transient object, PFS_wait_locker, per operation. + This object's life cycle is extremely short: it's created just + before the start_wait() instrumentation call, and is destroyed in + the end_wait() call. + + The wait locker itself contains a pointer to the object instance + waited on. + That allows to implement a wait_locker --> object instance projection, + with m_target. + The object instance life cycle depends on _init and _destroy calls + from the code, such as mysql_mutex_init() + and mysql_mutex_destroy() for a mutex. + + The object instance waited on contains a pointer to the object class, + which is represented by the instrument name. + That allows to implement an object instance --> object class projection. + The object class life cycle is permanent, as instruments are loaded in + the server and never removed. + + The object class is named in such a way + (for example, "wait/sync/mutex/sql/LOCK_open", + "wait/io/file/maria/data_file) that the component ("sql", "maria") + that it belongs to can be inferred. + That allows to implement an object class --> server component projection. + + Back to math again, we have, for example for mutexes: + + F1 (l) : PFS_wait_locker l --> PFS_mutex m = l->m_target.m_mutex + + F1_to_2 (m) : PFS_mutex m --> PFS_mutex_class i = m->m_class + + F2_to_3 (i) : PFS_mutex_class i --> const char *component = + substring(i->m_name, ...) + + Per components aggregates are not implemented, this is just an illustration. + + F1 alone defines this aggregate: + + EVENTS_WAITS_HISTORY_INFINITE --> EVENTS_WAITS_SUMMARY_BY_INSTANCE + (or MUTEX_INSTANCE) + + F1_to_2 alone could define this aggregate: + + EVENTS_WAITS_SUMMARY_BY_INSTANCE --> EVENTS_WAITS_SUMMARY_BY_EVENT_NAME + + Alternatively, using function composition, with + F2 = F1_to_2 o F1, F2 defines: + + EVENTS_WAITS_HISTORY_INFINITE --> EVENTS_WAITS_SUMMARY_BY_EVENT_NAME + + Likewise, F_2_to_3 defines: + + EVENTS_WAITS_SUMMARY_BY_EVENT_NAME --> EVENTS_WAITS_SUMMARY_BY_COMPONENT + + and F3 = F_2_to_3 o F_1_to_2 o F1 defines: + + EVENTS_WAITS_HISTORY_INFINITE --> EVENTS_WAITS_SUMMARY_BY_COMPONENT + + What has all this to do with the code ? + + Functions (or aggregates) such as F_3 are not implemented as is. + Instead, they are decomposed into F_2_to_3 o F_1_to_2 o F1, + and each intermediate aggregate is stored into an internal buffer. + This allows to support every F1, F2, F3 aggregates from shared + internal buffers, where computation already performed to compute F2 + is reused when computing F3. + + @section OBJECT_GRAPH Object graph + + In terms of object instances, or records, pointers between + different buffers define an object instance graph. + + For example, assuming the following scenario: + - A mutex class "M" is instrumented, the instrument name + is "wait/sync/mutex/sql/M" + - This mutex instrument has been instantiated twice, + mutex instances are noted M-1 and M-2 + - Threads T-A and T-B are locking mutex instance M-1 + - Threads T-C and T-D are locking mutex instance M-2 + + The performance schema will record the following data: + - EVENTS_WAITS_CURRENT has 4 rows, one for each mutex locker + - EVENTS_WAITS_SUMMARY_BY_INSTANCE shows 2 rows, for M-1 and M-2 + - EVENTS_WAITS_SUMMARY_BY_EVENT_NAME shows 1 row, for M + + The graph of structures will look like: + +@verbatim + PFS_wait_locker (T-A, M-1) ---------- + | + v + PFS_mutex (M-1) + - m_wait_stat ------------ + ^ | + | | + PFS_wait_locker (T-B, M-1) ---------- | + v + PFS_mutex_class (M) + - m_wait_stat + PFS_wait_locker (T-C, M-2) ---------- ^ + | | + v | + PFS_mutex (M-2) | + - m_wait_stat ------------ + ^ + | + PFS_wait_locker (T-D, M-2) ---------- + + || || || + || || || + vv vv vv + + EVENTS_WAITS_CURRENT ..._SUMMARY_BY_INSTANCE ..._SUMMARY_BY_EVENT_NAME +@endverbatim + + @section ON_THE_FLY On the fly aggregates + + 'On the fly' aggregates are computed during the code execution. + This is necessary because the data the aggregate is based on is volatile, + and can not be kept indefinitely. + + With on the fly aggregates: + - the writer thread does all the computation + - the reader thread accesses the result directly + + This model is to be avoided if possible, due to the overhead + caused when instrumenting code. + + @section HIGHER_LEVEL Higher level aggregates + + 'Higher level' aggregates are implemented on demand only. + The code executing a SELECT from the aggregate table is + collecting data from multiple internal buffers to produce the result. + + With higher level aggregates: + - the reader thread does all the computation + - the writer thread has no overhead. + + @section MIXED Mixed level aggregates + + The 'Mixed' model is a compromise between 'On the fly' and 'Higher level' + aggregates, for internal buffers that are not permanent. + + While an object is present in a buffer, the higher level model is used. + When an object is about to be destroyed, statistics are saved into + a 'parent' buffer with a longer life cycle, to follow the on the fly model. + + With mixed aggregates: + - the reader thread does a lot of complex computation, + - the writer thread has minimal overhead, on destroy events. + + @section IMPL_WAIT Implementation for waits aggregates + + For waits, the tables that contains aggregated wait data are: + - EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME + - EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME + - EVENTS_WAITS_SUMMARY_BY_INSTANCE + - EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME + - EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME + - FILE_SUMMARY_BY_EVENT_NAME + - FILE_SUMMARY_BY_INSTANCE + - SOCKET_SUMMARY_BY_INSTANCE + - SOCKET_SUMMARY_BY_EVENT_NAME + - OBJECTS_SUMMARY_GLOBAL_BY_TYPE + + The instrumented code that generates waits events consist of: + - mutexes (mysql_mutex_t) + - rwlocks (mysql_rwlock_t) + - conditions (mysql_cond_t) + - file io (MYSQL_FILE) + - socket io (MYSQL_SOCKET) + - table io + - table lock + - idle + + The flow of data between aggregates tables varies for each instrumentation. + + @subsection IMPL_WAIT_MUTEX Mutex waits + +@verbatim + mutex_locker(T, M) + | + | [1] + | + |-> pfs_mutex(M) =====>> [B], [C] + | | + | | [2] + | | + | |-> pfs_mutex_class(M.class) =====>> [C] + | + |-> pfs_thread(T).event_name(M) =====>> [A], [D], [E], [F] + | + | [3] + | + 3a |-> pfs_account(U, H).event_name(M) =====>> [D], [E], [F] + . | + . | [4-RESET] + . | + 3b .....+-> pfs_user(U).event_name(M) =====>> [E] + . | + 3c .....+-> pfs_host(H).event_name(M) =====>> [F] +@endverbatim + + How to read this diagram: + - events that occur during the instrumented code execution are noted with numbers, + as in [1]. Code executed by these events has an impact on overhead. + - events that occur during TRUNCATE TABLE operations are noted with numbers, + followed by "-RESET", as in [4-RESET]. + Code executed by these events has no impact on overhead, + since they are executed by independent monitoring sessions. + - events that occur when a reader extracts data from a performance schema table + are noted with letters, as in [A]. The name of the table involved, + and the method that builds a row are documented. Code executed by these events + has no impact on the instrumentation overhead. Note that the table + implementation may pull data from different buffers. + - nominal code paths are in plain lines. A "nominal" code path corresponds to + cases where the performance schema buffers are sized so that no records are lost. + - degenerated code paths are in dotted lines. A "degenerated" code path corresponds + to edge cases where parent buffers are full, which forces the code to aggregate to + grand parents directly. + + Implemented as: + - [1] @c start_mutex_wait_v1(), @c end_mutex_wait_v1() + - [2] @c destroy_mutex_v1() + - [3] @c aggregate_thread_waits() + - [4] @c PFS_account::aggregate_waits() + - [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME, + @c table_ews_by_thread_by_event_name::make_row() + - [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE, + @c table_events_waits_summary_by_instance::make_mutex_row() + - [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME, + @c table_ews_global_by_event_name::make_mutex_row() + - [D] EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME, + @c table_ews_by_account_by_event_name::make_row() + - [E] EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME, + @c table_ews_by_user_by_event_name::make_row() + - [F] EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME, + @c table_ews_by_host_by_event_name::make_row() + + Table EVENTS_WAITS_SUMMARY_BY_INSTANCE is a 'on the fly' aggregate, + because the data is collected on the fly by (1) and stored into a buffer, + pfs_mutex. The table implementation [B] simply reads the results directly + from this buffer. + + Table EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME is a 'mixed' aggregate, + because some data is collected on the fly (1), + some data is preserved with (2) at a later time in the life cycle, + and two different buffers pfs_mutex and pfs_mutex_class are used to store the + statistics collected. The table implementation [C] is more complex, since + it reads from two buffers pfs_mutex and pfs_mutex_class. + + @subsection IMPL_WAIT_RWLOCK Rwlock waits + +@verbatim + rwlock_locker(T, R) + | + | [1] + | + |-> pfs_rwlock(R) =====>> [B], [C] + | | + | | [2] + | | + | |-> pfs_rwlock_class(R.class) =====>> [C] + | + |-> pfs_thread(T).event_name(R) =====>> [A] + | + ... +@endverbatim + + Implemented as: + - [1] @c start_rwlock_rdwait_v1(), @c end_rwlock_rdwait_v1(), ... + - [2] @c destroy_rwlock_v1() + - [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME, + @c table_ews_by_thread_by_event_name::make_row() + - [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE, + @c table_events_waits_summary_by_instance::make_rwlock_row() + - [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME, + @c table_ews_global_by_event_name::make_rwlock_row() + + @subsection IMPL_WAIT_COND Cond waits + +@verbatim + cond_locker(T, C) + | + | [1] + | + |-> pfs_cond(C) =====>> [B], [C] + | | + | | [2] + | | + | |-> pfs_cond_class(C.class) =====>> [C] + | + |-> pfs_thread(T).event_name(C) =====>> [A] + | + ... +@endverbatim + + Implemented as: + - [1] @c start_cond_wait_v1(), @c end_cond_wait_v1() + - [2] @c destroy_cond_v1() + - [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME, + @c table_ews_by_thread_by_event_name::make_row() + - [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE, + @c table_events_waits_summary_by_instance::make_cond_row() + - [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME, + @c table_ews_global_by_event_name::make_cond_row() + + @subsection IMPL_WAIT_FILE File waits + +@verbatim + file_locker(T, F) + | + | [1] + | + |-> pfs_file(F) =====>> [B], [C], [D], [E] + | | + | | [2] + | | + | |-> pfs_file_class(F.class) =====>> [C], [D] + | + |-> pfs_thread(T).event_name(F) =====>> [A] + | + ... +@endverbatim + + Implemented as: + - [1] @c get_thread_file_name_locker_v1(), @c start_file_wait_v1(), + @c end_file_wait_v1(), ... + - [2] @c close_file_v1() + - [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME, + @c table_ews_by_thread_by_event_name::make_row() + - [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE, + @c table_events_waits_summary_by_instance::make_file_row() + - [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME, + @c table_ews_global_by_event_name::make_file_row() + - [D] FILE_SUMMARY_BY_EVENT_NAME, + @c table_file_summary_by_event_name::make_row() + - [E] FILE_SUMMARY_BY_INSTANCE, + @c table_file_summary_by_instance::make_row() + + @subsection IMPL_WAIT_SOCKET Socket waits + +@verbatim + socket_locker(T, S) + | + | [1] + | + |-> pfs_socket(S) =====>> [A], [B], [C], [D], [E] + | + | [2] + | + |-> pfs_socket_class(S.class) =====>> [C], [D] + | + |-> pfs_thread(T).event_name(S) =====>> [A] + | + | [3] + | + 3a |-> pfs_account(U, H).event_name(S) =====>> [F], [G], [H] + . | + . | [4-RESET] + . | + 3b .....+-> pfs_user(U).event_name(S) =====>> [G] + . | + 3c .....+-> pfs_host(H).event_name(S) =====>> [H] +@endverbatim + + Implemented as: + - [1] @c start_socket_wait_v1(), @c end_socket_wait_v1(). + - [2] @c close_socket_v1() + - [3] @c aggregate_thread_waits() + - [4] @c PFS_account::aggregate_waits() + - [5] @c PFS_host::aggregate_waits() + - [A] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME, + @c table_ews_by_thread_by_event_name::make_row() + - [B] EVENTS_WAITS_SUMMARY_BY_INSTANCE, + @c table_events_waits_summary_by_instance::make_socket_row() + - [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME, + @c table_ews_global_by_event_name::make_socket_row() + - [D] SOCKET_SUMMARY_BY_EVENT_NAME, + @c table_socket_summary_by_event_name::make_row() + - [E] SOCKET_SUMMARY_BY_INSTANCE, + @c table_socket_summary_by_instance::make_row() + - [F] EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME, + @c table_ews_by_account_by_event_name::make_row() + - [G] EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME, + @c table_ews_by_user_by_event_name::make_row() + - [H] EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME, + @c table_ews_by_host_by_event_name::make_row() + + @subsection IMPL_WAIT_TABLE Table waits + +@verbatim + table_locker(Thread Th, Table Tb, Event = io or lock) + | + | [1] + | +1a |-> pfs_table(Tb) =====>> [A], [B], [C] + | | + | | [2] + | | + | |-> pfs_table_share(Tb.share) =====>> [B], [C] + | | + | | [3] + | | + | |-> global_table_io_stat =====>> [C] + | | + | |-> global_table_lock_stat =====>> [C] + | +1b |-> pfs_thread(Th).event_name(E) =====>> [D], [E], [F], [G] + | | + | | [ 4-RESET] + | | + | |-> pfs_account(U, H).event_name(E) =====>> [E], [F], [G] + | . | + | . | [5-RESET] + | . | + | .....+-> pfs_user(U).event_name(E) =====>> [F] + | . | + | .....+-> pfs_host(H).event_name(E) =====>> [G] + | +1c |-> pfs_thread(Th).waits_current(W) =====>> [H] + | +1d |-> pfs_thread(Th).waits_history(W) =====>> [I] + | +1e |-> waits_history_long(W) =====>> [J] +@endverbatim + + Implemented as: + - [1] @c start_table_io_wait_v1(), @c end_table_io_wait_v1() + - [2] @c close_table_v1() + - [3] @c drop_table_share_v1() + - [4] @c TRUNCATE TABLE EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME + - [5] @c TRUNCATE TABLE EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME + - [A] EVENTS_WAITS_SUMMARY_BY_INSTANCE, + @c table_events_waits_summary_by_instance::make_table_row() + - [B] OBJECTS_SUMMARY_GLOBAL_BY_TYPE, + @c table_os_global_by_type::make_row() + - [C] EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME, + @c table_ews_global_by_event_name::make_table_io_row(), + @c table_ews_global_by_event_name::make_table_lock_row() + - [D] EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME, + @c table_ews_by_thread_by_event_name::make_row() + - [E] EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME, + @c table_ews_by_user_by_account_name::make_row() + - [F] EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME, + @c table_ews_by_user_by_event_name::make_row() + - [G] EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME, + @c table_ews_by_host_by_event_name::make_row() + - [H] EVENTS_WAITS_CURRENT, + @c table_events_waits_common::make_row() + - [I] EVENTS_WAITS_HISTORY, + @c table_events_waits_common::make_row() + - [J] EVENTS_WAITS_HISTORY_LONG, + @c table_events_waits_common::make_row() + + @section IMPL_STAGE Implementation for stages aggregates + + For stages, the tables that contains aggregated data are: + - EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME + - EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME + - EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME + - EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME + +@verbatim + start_stage(T, S) + | + | [1] + | +1a |-> pfs_thread(T).event_name(S) =====>> [A], [B], [C], [D], [E] + | | + | | [2] + | | + | 2a |-> pfs_account(U, H).event_name(S) =====>> [B], [C], [D], [E] + | . | + | . | [3-RESET] + | . | + | 2b .....+-> pfs_user(U).event_name(S) =====>> [C] + | . | + | 2c .....+-> pfs_host(H).event_name(S) =====>> [D], [E] + | . . | + | . . | [4-RESET] + | 2d . . | +1b |----+----+----+-> pfs_stage_class(S) =====>> [E] + +@endverbatim + + Implemented as: + - [1] @c start_stage_v1() + - [2] @c delete_thread_v1(), @c aggregate_thread_stages() + - [3] @c PFS_account::aggregate_stages() + - [4] @c PFS_host::aggregate_stages() + - [A] EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME, + @c table_esgs_by_thread_by_event_name::make_row() + - [B] EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME, + @c table_esgs_by_account_by_event_name::make_row() + - [C] EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME, + @c table_esgs_by_user_by_event_name::make_row() + - [D] EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME, + @c table_esgs_by_host_by_event_name::make_row() + - [E] EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME, + @c table_esgs_global_by_event_name::make_row() + +@section IMPL_STATEMENT Implementation for statements consumers + + For statements, the tables that contains individual event data are: + - EVENTS_STATEMENTS_CURRENT + - EVENTS_STATEMENTS_HISTORY + - EVENTS_STATEMENTS_HISTORY_LONG + + For statements, the tables that contains aggregated data are: + - EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME + - EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME + - EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME + - EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME + - EVENTS_STATEMENTS_SUMMARY_BY_DIGEST + +@verbatim + statement_locker(T, S) + | + | [1] + | +1a |-> pfs_thread(T).event_name(S) =====>> [A], [B], [C], [D], [E] + | | + | | [2] + | | + | 2a |-> pfs_account(U, H).event_name(S) =====>> [B], [C], [D], [E] + | . | + | . | [3-RESET] + | . | + | 2b .....+-> pfs_user(U).event_name(S) =====>> [C] + | . | + | 2c .....+-> pfs_host(H).event_name(S) =====>> [D], [E] + | . . | + | . . | [4-RESET] + | 2d . . | +1b |----+----+----+-> pfs_statement_class(S) =====>> [E] + | +1c |-> pfs_thread(T).statement_current(S) =====>> [F] + | +1d |-> pfs_thread(T).statement_history(S) =====>> [G] + | +1e |-> statement_history_long(S) =====>> [H] + | +1f |-> statement_digest(S) =====>> [I] + +@endverbatim + + Implemented as: + - [1] @c start_statement_v1(), end_statement_v1() + (1a, 1b) is an aggregation by EVENT_NAME, + (1c, 1d, 1e) is an aggregation by TIME, + (1f) is an aggregation by DIGEST + all of these are orthogonal, + and implemented in end_statement_v1(). + - [2] @c delete_thread_v1(), @c aggregate_thread_statements() + - [3] @c PFS_account::aggregate_statements() + - [4] @c PFS_host::aggregate_statements() + - [A] EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME, + @c table_esms_by_thread_by_event_name::make_row() + - [B] EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME, + @c table_esms_by_account_by_event_name::make_row() + - [C] EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME, + @c table_esms_by_user_by_event_name::make_row() + - [D] EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME, + @c table_esms_by_host_by_event_name::make_row() + - [E] EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME, + @c table_esms_global_by_event_name::make_row() + - [F] EVENTS_STATEMENTS_CURRENT, + @c table_events_statements_current::rnd_next(), + @c table_events_statements_common::make_row() + - [G] EVENTS_STATEMENTS_HISTORY, + @c table_events_statements_history::rnd_next(), + @c table_events_statements_common::make_row() + - [H] EVENTS_STATEMENTS_HISTORY_LONG, + @c table_events_statements_history_long::rnd_next(), + @c table_events_statements_common::make_row() + - [I] EVENTS_STATEMENTS_SUMMARY_BY_DIGEST + @c table_esms_by_digest::make_row() + +@section IMPL_TRANSACTION Implementation for transactions consumers + + For transactions, the tables that contains individual event data are: + - EVENTS_TRANSACTIONS_CURRENT + - EVENTS_TRANSACTIONS_HISTORY + - EVENTS_TRANSACTIONS_HISTORY_LONG + + For transactions, the tables that contains aggregated data are: + - EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME + - EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME + - EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME + - EVENTS_TRANSACTIONS_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME + +@verbatim + transaction_locker(T, TX) + | + | [1] + | +1a |-> pfs_thread(T).event_name(TX) =====>> [A], [B], [C], [D], [E] + | | + | | [2] + | | + | 2a |-> pfs_account(U, H).event_name(TX) =====>> [B], [C], [D], [E] + | . | + | . | [3-RESET] + | . | + | 2b .....+-> pfs_user(U).event_name(TX) =====>> [C] + | . | + | 2c .....+-> pfs_host(H).event_name(TX) =====>> [D], [E] + | . . | + | . . | [4-RESET] + | 2d . . | +1b |----+----+----+-> pfs_transaction_class(TX) =====>> [E] + | +1c |-> pfs_thread(T).transaction_current(TX) =====>> [F] + | +1d |-> pfs_thread(T).transaction_history(TX) =====>> [G] + | +1e |-> transaction_history_long(TX) =====>> [H] + +@endverbatim + + Implemented as: + - [1] @c start_transaction_v1(), end_transaction_v1() + (1a, 1b) is an aggregation by EVENT_NAME, + (1c, 1d, 1e) is an aggregation by TIME, + all of these are orthogonal, + and implemented in end_transaction_v1(). + - [2] @c delete_thread_v1(), @c aggregate_thread_transactions() + - [3] @c PFS_account::aggregate_transactions() + - [4] @c PFS_host::aggregate_transactions() + + - [A] EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME, + @c table_ets_by_thread_by_event_name::make_row() + - [B] EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME, + @c table_ets_by_account_by_event_name::make_row() + - [C] EVENTS_TRANSACTIONS_SUMMARY_BY_USER_BY_EVENT_NAME, + @c table_ets_by_user_by_event_name::make_row() + - [D] EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME, + @c table_ets_by_host_by_event_name::make_row() + - [E] EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME, + @c table_ets_global_by_event_name::make_row() + - [F] EVENTS_TRANSACTIONS_CURRENT, + @c table_events_transactions_current::rnd_next(), + @c table_events_transactions_common::make_row() + - [G] EVENTS_TRANSACTIONS_HISTORY, + @c table_events_transactions_history::rnd_next(), + @c table_events_transactions_common::make_row() + - [H] EVENTS_TRANSACTIONS_HISTORY_LONG, + @c table_events_transactions_history_long::rnd_next(), + @c table_events_transactions_common::make_row() + +@section IMPL_MEMORY Implementation for memory instruments + + For memory, there are no tables that contains individual event data. + + For memory, the tables that contains aggregated data are: + - MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME + - MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME + - MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME + - MEMORY_SUMMARY_BY_USER_BY_EVENT_NAME + - MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME + +@verbatim + memory_event(T, S) + | + | [1] + | +1a |-> pfs_thread(T).event_name(S) =====>> [A], [B], [C], [D], [E] + | | + | | [2] + | | +1+ | 2a |-> pfs_account(U, H).event_name(S) =====>> [B], [C], [D], [E] + | . | + | . | [3-RESET] + | . | +1+ | 2b .....+-> pfs_user(U).event_name(S) =====>> [C] + | . | +1+ | 2c .....+-> pfs_host(H).event_name(S) =====>> [D], [E] + | . . | + | . . | [4-RESET] + | 2d . . | +1b |----+----+----+-> global.event_name(S) =====>> [E] + +@endverbatim + + Implemented as: + - [1] @c pfs_memory_alloc_v1(), + @c pfs_memory_realloc_v1(), + @c pfs_memory_free_v1(). + - [1+] are overflows that can happen during [1a], + implemented with @c carry_memory_stat_delta() + - [2] @c delete_thread_v1(), @c aggregate_thread_memory() + - [3] @c PFS_account::aggregate_memory() + - [4] @c PFS_host::aggregate_memory() + - [A] EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME, + @c table_mems_by_thread_by_event_name::make_row() + - [B] EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME, + @c table_mems_by_account_by_event_name::make_row() + - [C] EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME, + @c table_mems_by_user_by_event_name::make_row() + - [D] EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME, + @c table_mems_by_host_by_event_name::make_row() + - [E] EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME, + @c table_mems_global_by_event_name::make_row() + +*/ + +/** + @defgroup Performance_schema Performance Schema + The performance schema component. + For details, see the + @ref PAGE_PERFORMANCE_SCHEMA "performance schema main page". + + @defgroup Performance_schema_implementation Performance Schema Implementation + @ingroup Performance_schema + + @defgroup Performance_schema_tables Performance Schema Tables + @ingroup Performance_schema_implementation +*/ + +thread_local_key_t THR_PFS; +thread_local_key_t THR_PFS_VG; // global_variables +thread_local_key_t THR_PFS_SV; // session_variables +thread_local_key_t THR_PFS_VBT; // variables_by_thread +thread_local_key_t THR_PFS_SG; // global_status +thread_local_key_t THR_PFS_SS; // session_status +thread_local_key_t THR_PFS_SBT; // status_by_thread +thread_local_key_t THR_PFS_SBU; // status_by_user +thread_local_key_t THR_PFS_SBH; // status_by_host +thread_local_key_t THR_PFS_SBA; // status_by_account + +bool THR_PFS_initialized= false; + +static inline PFS_thread* +my_thread_get_THR_PFS() +{ + assert(THR_PFS_initialized); + PFS_thread *thread= static_cast<PFS_thread*>(my_get_thread_local(THR_PFS)); + assert(thread == NULL || sanitize_thread(thread) != NULL); + return thread; +} + +static inline void +my_thread_set_THR_PFS(PFS_thread *pfs) +{ + assert(THR_PFS_initialized); + my_set_thread_local(THR_PFS, pfs); +} + +/** + Conversion map from PSI_mutex_operation to enum_operation_type. + Indexed by enum PSI_mutex_operation. +*/ +static enum_operation_type mutex_operation_map[]= +{ + OPERATION_TYPE_LOCK, + OPERATION_TYPE_TRYLOCK +}; + +/** + Conversion map from PSI_rwlock_operation to enum_operation_type. + Indexed by enum PSI_rwlock_operation. +*/ +static enum_operation_type rwlock_operation_map[]= +{ + OPERATION_TYPE_READLOCK, + OPERATION_TYPE_WRITELOCK, + OPERATION_TYPE_TRYREADLOCK, + OPERATION_TYPE_TRYWRITELOCK, + + OPERATION_TYPE_SHAREDLOCK, + OPERATION_TYPE_SHAREDEXCLUSIVELOCK, + OPERATION_TYPE_EXCLUSIVELOCK, + OPERATION_TYPE_TRYSHAREDLOCK, + OPERATION_TYPE_TRYSHAREDEXCLUSIVELOCK, + OPERATION_TYPE_TRYEXCLUSIVELOCK, +}; + +/** + Conversion map from PSI_cond_operation to enum_operation_type. + Indexed by enum PSI_cond_operation. +*/ +static enum_operation_type cond_operation_map[]= +{ + OPERATION_TYPE_WAIT, + OPERATION_TYPE_TIMEDWAIT +}; + +/** + Conversion map from PSI_file_operation to enum_operation_type. + Indexed by enum PSI_file_operation. +*/ +static enum_operation_type file_operation_map[]= +{ + OPERATION_TYPE_FILECREATE, + OPERATION_TYPE_FILECREATETMP, + OPERATION_TYPE_FILEOPEN, + OPERATION_TYPE_FILESTREAMOPEN, + OPERATION_TYPE_FILECLOSE, + OPERATION_TYPE_FILESTREAMCLOSE, + OPERATION_TYPE_FILEREAD, + OPERATION_TYPE_FILEWRITE, + OPERATION_TYPE_FILESEEK, + OPERATION_TYPE_FILETELL, + OPERATION_TYPE_FILEFLUSH, + OPERATION_TYPE_FILESTAT, + OPERATION_TYPE_FILEFSTAT, + OPERATION_TYPE_FILECHSIZE, + OPERATION_TYPE_FILEDELETE, + OPERATION_TYPE_FILERENAME, + OPERATION_TYPE_FILESYNC +}; + +/** + Conversion map from PSI_table_operation to enum_operation_type. + Indexed by enum PSI_table_io_operation. +*/ +static enum_operation_type table_io_operation_map[]= +{ + OPERATION_TYPE_TABLE_FETCH, + OPERATION_TYPE_TABLE_WRITE_ROW, + OPERATION_TYPE_TABLE_UPDATE_ROW, + OPERATION_TYPE_TABLE_DELETE_ROW +}; + +/** + Conversion map from enum PFS_TL_LOCK_TYPE to enum_operation_type. + Indexed by enum PFS_TL_LOCK_TYPE. +*/ +static enum_operation_type table_lock_operation_map[]= +{ + OPERATION_TYPE_TL_READ_NORMAL, /* PFS_TL_READ */ + OPERATION_TYPE_TL_READ_WITH_SHARED_LOCKS, /* PFS_TL_READ_WITH_SHARED_LOCKS */ + OPERATION_TYPE_TL_READ_HIGH_PRIORITY, /* PFS_TL_READ_HIGH_PRIORITY */ + OPERATION_TYPE_TL_READ_NO_INSERTS, /* PFS_TL_READ_NO_INSERT */ + OPERATION_TYPE_TL_WRITE_ALLOW_WRITE, /* PFS_TL_WRITE_ALLOW_WRITE */ + OPERATION_TYPE_TL_WRITE_CONCURRENT_INSERT, /* PFS_TL_WRITE_CONCURRENT_INSERT */ + OPERATION_TYPE_TL_WRITE_DELAYED, /* PFS_TL_WRITE_DELAYED */ + OPERATION_TYPE_TL_WRITE_LOW_PRIORITY, /* PFS_TL_WRITE_LOW_PRIORITY */ + OPERATION_TYPE_TL_WRITE_NORMAL, /* PFS_TL_WRITE */ + OPERATION_TYPE_TL_READ_EXTERNAL, /* PFS_TL_READ_EXTERNAL */ + OPERATION_TYPE_TL_WRITE_EXTERNAL /* PFS_TL_WRITE_EXTERNAL */ +}; + +/** + Conversion map from PSI_socket_operation to enum_operation_type. + Indexed by enum PSI_socket_operation. +*/ +static enum_operation_type socket_operation_map[]= +{ + OPERATION_TYPE_SOCKETCREATE, + OPERATION_TYPE_SOCKETCONNECT, + OPERATION_TYPE_SOCKETBIND, + OPERATION_TYPE_SOCKETCLOSE, + OPERATION_TYPE_SOCKETSEND, + OPERATION_TYPE_SOCKETRECV, + OPERATION_TYPE_SOCKETSENDTO, + OPERATION_TYPE_SOCKETRECVFROM, + OPERATION_TYPE_SOCKETSENDMSG, + OPERATION_TYPE_SOCKETRECVMSG, + OPERATION_TYPE_SOCKETSEEK, + OPERATION_TYPE_SOCKETOPT, + OPERATION_TYPE_SOCKETSTAT, + OPERATION_TYPE_SOCKETSHUTDOWN, + OPERATION_TYPE_SOCKETSELECT +}; + +/** + Build the prefix name of a class of instruments in a category. + For example, this function builds the string 'wait/sync/mutex/sql/' from + a prefix 'wait/sync/mutex' and a category 'sql'. + This prefix is used later to build each instrument name, such as + 'wait/sync/mutex/sql/LOCK_open'. + @param prefix Prefix for this class of instruments + @param category Category name + @param [out] output Buffer of length PFS_MAX_INFO_NAME_LENGTH. + @param [out] output_length Length of the resulting output string. + @return 0 for success, non zero for errors +*/ +static int build_prefix(const LEX_CSTRING *prefix, const char *category, + char *output, size_t *output_length) +{ + size_t len= strlen(category); + char *out_ptr= output; + size_t prefix_length= prefix->length; + + if (unlikely((prefix_length + len + 1) >= + PFS_MAX_FULL_PREFIX_NAME_LENGTH)) + { + pfs_print_error("build_prefix: prefix+category is too long <%s> <%s>\n", + prefix->str, category); + return 1; + } + + if (unlikely(strchr(category, '/') != NULL)) + { + pfs_print_error("build_prefix: invalid category <%s>\n", + category); + return 1; + } + + /* output = prefix + category + '/' */ + memcpy(out_ptr, prefix->str, prefix_length); + out_ptr+= prefix_length; + if (len > 0) + { + memcpy(out_ptr, category, len); + out_ptr+= len; + *out_ptr= '/'; + out_ptr++; + } + *output_length= int(out_ptr - output); + + return 0; +} + +#define REGISTER_BODY_V1(KEY_T, PREFIX, REGISTER_FUNC) \ + KEY_T key; \ + char formatted_name[PFS_MAX_INFO_NAME_LENGTH]; \ + size_t prefix_length; \ + size_t len; \ + size_t full_length; \ + \ + assert(category != NULL); \ + assert(info != NULL); \ + if (unlikely(build_prefix(&PREFIX, category, \ + formatted_name, &prefix_length)) || \ + ! pfs_initialized) \ + { \ + for (; count>0; count--, info++) \ + *(info->m_key)= 0; \ + return ; \ + } \ + \ + for (; count>0; count--, info++) \ + { \ + assert(info->m_key != NULL); \ + assert(info->m_name != NULL); \ + len= strlen(info->m_name); \ + full_length= prefix_length + len; \ + if (likely(full_length <= PFS_MAX_INFO_NAME_LENGTH)) \ + { \ + memcpy(formatted_name + prefix_length, info->m_name, len); \ + key= REGISTER_FUNC(formatted_name, (uint)full_length, info->m_flags); \ + } \ + else \ + { \ + pfs_print_error("REGISTER_BODY_V1: name too long <%s> <%s>\n", \ + category, info->m_name); \ + key= 0; \ + } \ + \ + *(info->m_key)= key; \ + } \ + return; + +/* Use C linkage for the interface functions. */ + +C_MODE_START + +/** + Implementation of the mutex instrumentation interface. + @sa PSI_v1::register_mutex. +*/ +void pfs_register_mutex_v1(const char *category, + PSI_mutex_info_v1 *info, + int count) +{ + REGISTER_BODY_V1(PSI_mutex_key, + mutex_instrument_prefix, + register_mutex_class) +} + +/** + Implementation of the rwlock instrumentation interface. + @sa PSI_v1::register_rwlock. +*/ +void pfs_register_rwlock_v1(const char *category, + PSI_rwlock_info_v1 *info, + int count) +{ + PSI_rwlock_key key; + char rw_formatted_name[PFS_MAX_INFO_NAME_LENGTH]; + char sx_formatted_name[PFS_MAX_INFO_NAME_LENGTH]; + size_t rw_prefix_length; + size_t sx_prefix_length; + size_t len; + size_t full_length; + + assert(category != NULL); + assert(info != NULL); + if (build_prefix(&rwlock_instrument_prefix, category, + rw_formatted_name, &rw_prefix_length) || + build_prefix(&sxlock_instrument_prefix, category, + sx_formatted_name, &sx_prefix_length) || + ! pfs_initialized) + { + for (; count>0; count--, info++) + *(info->m_key)= 0; + return ; + } + + for (; count>0; count--, info++) + { + assert(info->m_key != NULL); + assert(info->m_name != NULL); + len= strlen(info->m_name); + + if (info->m_flags & PSI_RWLOCK_FLAG_SX) + { + full_length= sx_prefix_length + len; + if (likely(full_length <= PFS_MAX_INFO_NAME_LENGTH)) + { + memcpy(sx_formatted_name + sx_prefix_length, info->m_name, len); + key= register_rwlock_class(sx_formatted_name, (uint)full_length, info->m_flags); + } + else + { + pfs_print_error("REGISTER_BODY_V1: (sx) name too long <%s> <%s>\n", + category, info->m_name); + key= 0; + } + } + else + { + full_length= rw_prefix_length + len; + if (likely(full_length <= PFS_MAX_INFO_NAME_LENGTH)) + { + memcpy(rw_formatted_name + rw_prefix_length, info->m_name, len); + key= register_rwlock_class(rw_formatted_name, (uint)full_length, info->m_flags); + } + else + { + pfs_print_error("REGISTER_BODY_V1: (rw) name too long <%s> <%s>\n", + category, info->m_name); + key= 0; + } + } + + *(info->m_key)= key; + } + return; +} + +/** + Implementation of the cond instrumentation interface. + @sa PSI_v1::register_cond. +*/ +void pfs_register_cond_v1(const char *category, + PSI_cond_info_v1 *info, + int count) +{ + REGISTER_BODY_V1(PSI_cond_key, + cond_instrument_prefix, + register_cond_class) +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::register_thread. +*/ +void pfs_register_thread_v1(const char *category, + PSI_thread_info_v1 *info, + int count) +{ + REGISTER_BODY_V1(PSI_thread_key, + thread_instrument_prefix, + register_thread_class) +} + +/** + Implementation of the file instrumentation interface. + @sa PSI_v1::register_file. +*/ +void pfs_register_file_v1(const char *category, + PSI_file_info_v1 *info, + int count) +{ + REGISTER_BODY_V1(PSI_file_key, + file_instrument_prefix, + register_file_class) +} + +void pfs_register_stage_v1(const char *category, + PSI_stage_info_v1 **info_array, + int count) +{ + char formatted_name[PFS_MAX_INFO_NAME_LENGTH]; + size_t prefix_length; + size_t len; + size_t full_length; + PSI_stage_info_v1 *info; + + assert(category != NULL); + assert(info_array != NULL); + if (unlikely(build_prefix(&stage_instrument_prefix, category, + formatted_name, &prefix_length)) || + ! pfs_initialized) + { + for (; count>0; count--, info_array++) + (*info_array)->m_key= 0; + return ; + } + + for (; count>0; count--, info_array++) + { + info= *info_array; + DBUG_ASSERT(info != NULL); + DBUG_ASSERT(info->m_name != NULL); + len= (int)strlen(info->m_name); + DBUG_ASSERT(len <= 64); // see table_threads.cc near PROCESSLIST_STATE + full_length= prefix_length + len; + if (likely(full_length <= PFS_MAX_INFO_NAME_LENGTH)) + { + memcpy(formatted_name + prefix_length, info->m_name, len); + info->m_key= register_stage_class(formatted_name, + (uint)prefix_length, + (uint)full_length, + info->m_flags); + } + else + { + pfs_print_error("register_stage_v1: name too long <%s> <%s>\n", + category, info->m_name); + info->m_key= 0; + } + } + return; +} + +void pfs_register_statement_v1(const char *category, + PSI_statement_info_v1 *info, + int count) +{ + char formatted_name[PFS_MAX_INFO_NAME_LENGTH]; + size_t prefix_length; + size_t len; + size_t full_length; + + assert(category != NULL); + assert(info != NULL); + if (unlikely(build_prefix(&statement_instrument_prefix, + category, formatted_name, &prefix_length)) || + ! pfs_initialized) + { + for (; count>0; count--, info++) + info->m_key= 0; + return ; + } + + for (; count>0; count--, info++) + { + if (info->m_name == NULL) + continue; + + len= (int)strlen(info->m_name); + full_length= prefix_length + len; + if (likely(full_length <= PFS_MAX_INFO_NAME_LENGTH)) + { + memcpy(formatted_name + prefix_length, info->m_name, len); + info->m_key= register_statement_class(formatted_name, (uint)full_length, info->m_flags); + } + else + { + pfs_print_error("register_statement_v1: name too long <%s>\n", + info->m_name); + info->m_key= 0; + } + } + return; +} + +void pfs_register_socket_v1(const char *category, + PSI_socket_info_v1 *info, + int count) +{ + REGISTER_BODY_V1(PSI_socket_key, + socket_instrument_prefix, + register_socket_class) +} + +#define INIT_BODY_V1(T, KEY, ID) \ + PFS_##T##_class *klass; \ + PFS_##T *pfs; \ + klass= find_##T##_class(KEY); \ + if (unlikely(klass == NULL)) \ + return NULL; \ + pfs= create_##T(klass, ID); \ + return reinterpret_cast<PSI_##T *> (pfs) + +/** + Implementation of the mutex instrumentation interface. + @sa PSI_v1::init_mutex. +*/ +PSI_mutex* +pfs_init_mutex_v1(PSI_mutex_key key, void *identity) +{ + INIT_BODY_V1(mutex, key, identity); +} + +/** + Implementation of the mutex instrumentation interface. + @sa PSI_v1::destroy_mutex. +*/ +void pfs_destroy_mutex_v1(PSI_mutex* mutex) +{ + PFS_mutex *pfs= reinterpret_cast<PFS_mutex*> (mutex); + + assert(pfs != NULL); + + destroy_mutex(pfs); +} + +/** + Implementation of the rwlock instrumentation interface. + @sa PSI_v1::init_rwlock. +*/ +PSI_rwlock* +pfs_init_rwlock_v1(PSI_rwlock_key key, void *identity) +{ + INIT_BODY_V1(rwlock, key, identity); +} + +/** + Implementation of the rwlock instrumentation interface. + @sa PSI_v1::destroy_rwlock. +*/ +void pfs_destroy_rwlock_v1(PSI_rwlock* rwlock) +{ + PFS_rwlock *pfs= reinterpret_cast<PFS_rwlock*> (rwlock); + + assert(pfs != NULL); + + destroy_rwlock(pfs); +} + +/** + Implementation of the cond instrumentation interface. + @sa PSI_v1::init_cond. +*/ +PSI_cond* +pfs_init_cond_v1(PSI_cond_key key, void *identity) +{ + INIT_BODY_V1(cond, key, identity); +} + +/** + Implementation of the cond instrumentation interface. + @sa PSI_v1::destroy_cond. +*/ +void pfs_destroy_cond_v1(PSI_cond* cond) +{ + PFS_cond *pfs= reinterpret_cast<PFS_cond*> (cond); + + assert(pfs != NULL); + + destroy_cond(pfs); +} + +/** + Implementation of the table instrumentation interface. + @sa PSI_v1::get_table_share. +*/ +PSI_table_share* +pfs_get_table_share_v1(my_bool temporary, TABLE_SHARE *share) +{ + /* Ignore temporary tables and views. */ + if (temporary || share->is_view) + return NULL; + /* An instrumented thread is required, for LF_PINS. */ + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + PFS_table_share* pfs_share; + pfs_share= find_or_create_table_share(pfs_thread, temporary, share); + return reinterpret_cast<PSI_table_share*> (pfs_share); +} + +/** + Implementation of the table instrumentation interface. + @sa PSI_v1::release_table_share. +*/ +void pfs_release_table_share_v1(PSI_table_share* share) +{ + PFS_table_share* pfs= reinterpret_cast<PFS_table_share*> (share); + + if (unlikely(pfs == NULL)) + return; + + release_table_share(pfs); +} + +/** + Implementation of the table instrumentation interface. + @sa PSI_v1::drop_table_share. +*/ +void +pfs_drop_table_share_v1(my_bool temporary, + const char *schema_name, int schema_name_length, + const char *table_name, int table_name_length) +{ + /* Ignore temporary tables. */ + if (temporary) + return; + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return; + /* TODO: temporary tables */ + drop_table_share(pfs_thread, temporary, schema_name, schema_name_length, + table_name, table_name_length); +} + +/** + Implementation of the table instrumentation interface. + @sa PSI_v1::open_table. +*/ +PSI_table* +pfs_open_table_v1(PSI_table_share *share, const void *identity) +{ + PFS_table_share *pfs_table_share= reinterpret_cast<PFS_table_share*> (share); + + /* + When the performance schema is off, do not instrument anything. + Table handles have short life cycle, instrumentation will happen + again if needed during the next open(). + */ + if (psi_unlikely(! flag_global_instrumentation)) + return NULL; + + if (unlikely(pfs_table_share == NULL)) + return NULL; + + /* This object is not to be instrumented. */ + if (! pfs_table_share->m_enabled) + return NULL; + + /* This object is instrumented, but all table instruments are disabled. */ + if (! global_table_io_class.m_enabled && ! global_table_lock_class.m_enabled) + return NULL; + + PFS_thread *thread= my_thread_get_THR_PFS(); + + if (unlikely(thread == NULL)) + return NULL; + + PFS_table *pfs_table= create_table(pfs_table_share, thread, identity); + return reinterpret_cast<PSI_table *> (pfs_table); +} + +/** + Implementation of the table instrumentation interface. + @sa PSI_v1::unbind_table. +*/ +void pfs_unbind_table_v1(PSI_table *table) +{ + PFS_table *pfs= reinterpret_cast<PFS_table*> (table); + if (likely(pfs != NULL)) + { + pfs->m_thread_owner= NULL; + pfs->m_owner_event_id= 0; + } +} + +/** + Implementation of the table instrumentation interface. + @sa PSI_v1::rebind_table. +*/ +PSI_table * +pfs_rebind_table_v1(PSI_table_share *share, const void *identity, PSI_table *table) +{ + PFS_table *pfs= reinterpret_cast<PFS_table*> (table); + if (likely(pfs != NULL)) + { + assert(pfs->m_thread_owner == NULL); + + if (unlikely(! pfs->m_share->m_enabled)) + { + destroy_table(pfs); + return NULL; + } + + if (unlikely(! global_table_io_class.m_enabled && ! global_table_lock_class.m_enabled)) + { + destroy_table(pfs); + return NULL; + } + + if (psi_unlikely(! flag_global_instrumentation)) + { + destroy_table(pfs); + return NULL; + } + + /* The table handle was already instrumented, reuse it for this thread. */ + PFS_thread *thread= my_thread_get_THR_PFS(); + pfs->m_thread_owner= thread; + if (thread != NULL) + pfs->m_owner_event_id= thread->m_event_id; + else + pfs->m_owner_event_id= 0; + return table; + } + + /* See open_table_v1() */ + + PFS_table_share *pfs_table_share= reinterpret_cast<PFS_table_share*> (share); + + if (unlikely(pfs_table_share == NULL)) + return NULL; + + if (! pfs_table_share->m_enabled) + return NULL; + + if (! global_table_io_class.m_enabled && ! global_table_lock_class.m_enabled) + return NULL; + + if (! flag_global_instrumentation) + return NULL; + + PFS_thread *thread= my_thread_get_THR_PFS(); + if (unlikely(thread == NULL)) + return NULL; + + PFS_table *pfs_table= create_table(pfs_table_share, thread, identity); + return reinterpret_cast<PSI_table *> (pfs_table); +} + +/** + Implementation of the table instrumentation interface. + @sa PSI_v1::close_table. +*/ +void pfs_close_table_v1(TABLE_SHARE *server_share, PSI_table *table) +{ + PFS_table *pfs= reinterpret_cast<PFS_table*> (table); + if (unlikely(pfs == NULL)) + return; + pfs->aggregate(server_share); + destroy_table(pfs); +} + +PSI_socket* +pfs_init_socket_v1(PSI_socket_key key, const my_socket *fd, + const struct sockaddr *addr, socklen_t addr_len) +{ + PFS_socket_class *klass; + PFS_socket *pfs; + klass= find_socket_class(key); + if (unlikely(klass == NULL)) + return NULL; + pfs= create_socket(klass, fd, addr, addr_len); + return reinterpret_cast<PSI_socket *> (pfs); +} + +void pfs_destroy_socket_v1(PSI_socket *socket) +{ + PFS_socket *pfs= reinterpret_cast<PFS_socket*> (socket); + + assert(pfs != NULL); + + destroy_socket(pfs); +} + +/** + Implementation of the file instrumentation interface. + @sa PSI_v1::create_file. +*/ +void pfs_create_file_v1(PSI_file_key key, const char *name, File file) +{ + if (psi_unlikely(! flag_global_instrumentation)) + return; + int index= (int) file; + if (unlikely(index < 0)) + return; + PFS_file_class *klass= find_file_class(key); + if (unlikely(klass == NULL)) + return; + if (! klass->m_enabled) + return; + + /* A thread is needed for LF_PINS */ + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return; + + if (flag_thread_instrumentation && ! pfs_thread->m_enabled) + return; + + /* + We want this check after pfs_thread->m_enabled, + to avoid reporting false loss. + */ + if (unlikely(index >= file_handle_max)) + { + file_handle_lost++; + return; + } + + uint len= (uint)strlen(name); + PFS_file *pfs_file= find_or_create_file(pfs_thread, klass, name, len, true); + + file_handle_array[index]= pfs_file; +} + +/** + Arguments given from a parent to a child thread, packaged in one structure. + This data is used when spawning a new instrumented thread. + @sa pfs_spawn_thread. +*/ +struct PFS_spawn_thread_arg +{ + ulonglong m_thread_internal_id; + char m_username[USERNAME_LENGTH]; + uint m_username_length; + char m_hostname[HOSTNAME_LENGTH]; + uint m_hostname_length; + + PSI_thread_key m_child_key; + const void *m_child_identity; + void *(*m_user_start_routine)(void*); + void *m_user_arg; +}; + +extern "C" void* pfs_spawn_thread(void *arg) +{ + PFS_spawn_thread_arg *typed_arg= (PFS_spawn_thread_arg*) arg; + void *user_arg; + void *(*user_start_routine)(void*); + + PFS_thread *pfs; + + /* First, attach instrumentation to this newly created pthread. */ + PFS_thread_class *klass= find_thread_class(typed_arg->m_child_key); + if (likely(klass != NULL)) + { + pfs= create_thread(klass, typed_arg->m_child_identity, 0); + if (likely(pfs != NULL)) + { + clear_thread_account(pfs); + + pfs->m_parent_thread_internal_id= typed_arg->m_thread_internal_id; + + memcpy(pfs->m_username, typed_arg->m_username, sizeof(pfs->m_username)); + pfs->m_username_length= typed_arg->m_username_length; + + memcpy(pfs->m_hostname, typed_arg->m_hostname, sizeof(pfs->m_hostname)); + pfs->m_hostname_length= typed_arg->m_hostname_length; + + set_thread_account(pfs); + } + } + else + { + pfs= NULL; + } + my_thread_set_THR_PFS(pfs); + + /* + Secondly, free the memory allocated in spawn_thread_v1(). + It is preferable to do this before invoking the user + routine, to avoid memory leaks at shutdown, in case + the server exits without waiting for this thread. + */ + user_start_routine= typed_arg->m_user_start_routine; + user_arg= typed_arg->m_user_arg; + my_free(typed_arg); + + /* Then, execute the user code for this thread. */ + (*user_start_routine)(user_arg); + + return NULL; +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::spawn_thread. +*/ +int pfs_spawn_thread_v1(PSI_thread_key key, + my_thread_handle *thread, const my_thread_attr_t *attr, + void *(*start_routine)(void*), void *arg) +{ + PFS_spawn_thread_arg *psi_arg; + PFS_thread *parent; + + /* psi_arg can not be global, and can not be a local variable. */ + psi_arg= (PFS_spawn_thread_arg*) my_malloc(PSI_NOT_INSTRUMENTED, + sizeof(PFS_spawn_thread_arg), + MYF(MY_WME)); + if (unlikely(psi_arg == NULL)) + return EAGAIN; + + psi_arg->m_child_key= key; + psi_arg->m_child_identity= (arg ? arg : thread); + psi_arg->m_user_start_routine= start_routine; + psi_arg->m_user_arg= arg; + + parent= my_thread_get_THR_PFS(); + if (parent != NULL) + { + /* + Make a copy of the parent attributes. + This is required, because instrumentation for this thread (the parent) + may be destroyed before the child thread instrumentation is created. + */ + psi_arg->m_thread_internal_id= parent->m_thread_internal_id; + + memcpy(psi_arg->m_username, parent->m_username, sizeof(psi_arg->m_username)); + psi_arg->m_username_length= parent->m_username_length; + + memcpy(psi_arg->m_hostname, parent->m_hostname, sizeof(psi_arg->m_hostname)); + psi_arg->m_hostname_length= parent->m_hostname_length; + } + else + { + psi_arg->m_thread_internal_id= 0; + psi_arg->m_username_length= 0; + psi_arg->m_hostname_length= 0; + } + + int result= my_thread_create(thread, attr, pfs_spawn_thread, psi_arg); + if (unlikely(result != 0)) + my_free(psi_arg); + return result; +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::new_thread. +*/ +PSI_thread* +pfs_new_thread_v1(PSI_thread_key key, const void *identity, ulonglong processlist_id) +{ + PFS_thread *pfs; + + PFS_thread_class *klass= find_thread_class(key); + if (likely(klass != NULL)) + { + pfs= create_thread(klass, identity, processlist_id); + if (pfs != NULL) + { + PFS_thread *parent= my_thread_get_THR_PFS(); + if (parent != NULL) + pfs->m_parent_thread_internal_id= parent->m_parent_thread_internal_id; + } + } + else + pfs= NULL; + + return reinterpret_cast<PSI_thread*> (pfs); +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::set_thread_id. +*/ +void pfs_set_thread_id_v1(PSI_thread *thread, ulonglong processlist_id) +{ + PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread); + if (unlikely(pfs == NULL)) + return; + pfs->m_processlist_id= (ulong)processlist_id; +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::set_thread_THD. +*/ +void pfs_set_thread_THD_v1(PSI_thread *thread, THD *thd) +{ + PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread); + if (unlikely(pfs == NULL)) + return; + pfs->m_thd= thd; +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::set_thread_os_thread_id. +*/ +void pfs_set_thread_os_id_v1(PSI_thread *thread) +{ + PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread); + if (unlikely(pfs == NULL)) + return; + pfs->m_thread_os_id= my_thread_os_id(); +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::get_thread_id. +*/ +PSI_thread* +pfs_get_thread_v1(void) +{ + PFS_thread *pfs= my_thread_get_THR_PFS(); + return reinterpret_cast<PSI_thread*> (pfs); +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::set_thread_user. +*/ +void pfs_set_thread_user_v1(const char *user, int user_len) +{ + pfs_dirty_state dirty_state; + PFS_thread *pfs= my_thread_get_THR_PFS(); + + assert((user != NULL) || (user_len == 0)); + assert(user_len >= 0); + assert((uint) user_len <= sizeof(pfs->m_username)); + + if (unlikely(pfs == NULL)) + return; + + aggregate_thread(pfs, pfs->m_account, pfs->m_user, pfs->m_host); + + pfs->m_session_lock.allocated_to_dirty(& dirty_state); + + clear_thread_account(pfs); + + if (user_len > 0) + memcpy(pfs->m_username, user, user_len); + pfs->m_username_length= user_len; + + set_thread_account(pfs); + + bool enabled; + bool history; + if (pfs->m_account != NULL) + { + enabled= pfs->m_account->m_enabled; + history= pfs->m_account->m_history; + } + else + { + if ((pfs->m_username_length > 0) && (pfs->m_hostname_length > 0)) + { + lookup_setup_actor(pfs, + pfs->m_username, pfs->m_username_length, + pfs->m_hostname, pfs->m_hostname_length, + &enabled, &history); + } + else + { + /* There is no setting for background threads */ + enabled= true; + history= true; + } + } + pfs->set_enabled(enabled); + pfs->set_history(history); + + pfs->m_session_lock.dirty_to_allocated(& dirty_state); +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::set_thread_account. +*/ +void pfs_set_thread_account_v1(const char *user, int user_len, + const char *host, int host_len) +{ + pfs_dirty_state dirty_state; + PFS_thread *pfs= my_thread_get_THR_PFS(); + + assert((user != NULL) || (user_len == 0)); + assert(user_len >= 0); + assert((uint) user_len <= sizeof(pfs->m_username)); + assert((host != NULL) || (host_len == 0)); + assert(host_len >= 0); + + host_len= MY_MIN(host_len, static_cast<int>(sizeof(pfs->m_hostname))); + + if (unlikely(pfs == NULL)) + return; + + pfs->m_session_lock.allocated_to_dirty(& dirty_state); + + clear_thread_account(pfs); + + if (host_len > 0) + memcpy(pfs->m_hostname, host, host_len); + pfs->m_hostname_length= host_len; + + if (user_len > 0) + memcpy(pfs->m_username, user, user_len); + pfs->m_username_length= user_len; + + set_thread_account(pfs); + + bool enabled; + bool history; + if (pfs->m_account != NULL) + { + enabled= pfs->m_account->m_enabled; + history= pfs->m_account->m_history; + } + else + { + if ((pfs->m_username_length > 0) && (pfs->m_hostname_length > 0)) + { + lookup_setup_actor(pfs, + pfs->m_username, pfs->m_username_length, + pfs->m_hostname, pfs->m_hostname_length, + &enabled, &history); + } + else + { + /* There is no setting for background threads */ + enabled= true; + history= true; + } + } + pfs->set_enabled(enabled); + pfs->set_history(history); + + pfs->m_session_lock.dirty_to_allocated(& dirty_state); +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::set_thread_db. +*/ +void pfs_set_thread_db_v1(const char* db, int db_len) +{ + PFS_thread *pfs= my_thread_get_THR_PFS(); + + assert((db != NULL) || (db_len == 0)); + assert(db_len >= 0); + assert((uint) db_len <= sizeof(pfs->m_dbname)); + + if (likely(pfs != NULL)) + { + pfs_dirty_state dirty_state; + pfs->m_stmt_lock.allocated_to_dirty(& dirty_state); + if (db_len > 0) + memcpy(pfs->m_dbname, db, db_len); + pfs->m_dbname_length= db_len; + pfs->m_stmt_lock.dirty_to_allocated(& dirty_state); + } +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::set_thread_command. +*/ +void pfs_set_thread_command_v1(int command) +{ + PFS_thread *pfs= my_thread_get_THR_PFS(); + + assert(command >= 0); + assert(command <= (int) COM_END); + + if (likely(pfs != NULL)) + { + pfs->m_command= command; + } +} + +/** +Implementation of the thread instrumentation interface. +@sa PSI_v1::set_thread_connection_type. +*/ +void pfs_set_connection_type_v1(opaque_vio_type conn_type) +{ + PFS_thread *pfs= my_thread_get_THR_PFS(); + + if (likely(pfs != NULL)) + { + pfs->m_connection_type= static_cast<enum_vio_type> (conn_type); + } +} + + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::set_thread_start_time. +*/ +void pfs_set_thread_start_time_v1(time_t start_time) +{ + PFS_thread *pfs= my_thread_get_THR_PFS(); + + if (likely(pfs != NULL)) + { + pfs->m_start_time= start_time; + } +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::set_thread_state. +*/ +void pfs_set_thread_state_v1(const char* state) +{ + /* DEPRECATED. */ +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::set_thread_info. +*/ +void pfs_set_thread_info_v1(const char* info, uint info_len) +{ + pfs_dirty_state dirty_state; + PFS_thread *pfs= my_thread_get_THR_PFS(); + + assert((info != NULL) || (info_len == 0)); + + if (likely(pfs != NULL)) + { + if ((info != NULL) && (info_len > 0)) + { + if (info_len > sizeof(pfs->m_processlist_info)) + info_len= sizeof(pfs->m_processlist_info); + + pfs->m_stmt_lock.allocated_to_dirty(& dirty_state); + memcpy(pfs->m_processlist_info, info, info_len); + pfs->m_processlist_info_length= info_len; + pfs->m_stmt_lock.dirty_to_allocated(& dirty_state); + } + else + { + pfs->m_stmt_lock.allocated_to_dirty(& dirty_state); + pfs->m_processlist_info_length= 0; + pfs->m_stmt_lock.dirty_to_allocated(& dirty_state); + } + } +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::set_thread. +*/ +void pfs_set_thread_v1(PSI_thread* thread) +{ + PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread); + my_thread_set_THR_PFS(pfs); +} + +/** + Implementation of the thread instrumentation interface. +*/ +void pfs_set_thread_peer_port_v1(PSI_thread *thread, uint port) { + PFS_thread *pfs = reinterpret_cast<PFS_thread *>(thread); + if (likely(pfs != NULL)) { + pfs->m_peer_port = port; + } +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::delete_current_thread. +*/ +void pfs_delete_current_thread_v1(void) +{ + PFS_thread *thread= my_thread_get_THR_PFS(); + if (thread != NULL) + { + aggregate_thread(thread, thread->m_account, thread->m_user, thread->m_host); + my_thread_set_THR_PFS(NULL); + destroy_thread(thread); + } +} + +/** + Implementation of the thread instrumentation interface. + @sa PSI_v1::delete_thread. +*/ +void pfs_delete_thread_v1(PSI_thread *thread) +{ + PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread); + + if (pfs != NULL) + { + aggregate_thread(pfs, pfs->m_account, pfs->m_user, pfs->m_host); + destroy_thread(pfs); + } +} + +/** + Implementation of the mutex instrumentation interface. + @sa PSI_v1::start_mutex_wait. +*/ +PSI_mutex_locker* +pfs_start_mutex_wait_v1(PSI_mutex_locker_state *state, + PSI_mutex *mutex, PSI_mutex_operation op, + const char *src_file, uint src_line) +{ + PFS_mutex *pfs_mutex= reinterpret_cast<PFS_mutex*> (mutex); + assert((int) op >= 0); + assert((uint) op < array_elements(mutex_operation_map)); + assert(state != NULL); + + assert(pfs_mutex != NULL); + assert(pfs_mutex->m_class != NULL); + + if (! pfs_mutex->m_enabled) + return NULL; + + uint flags; + ulonglong timer_start= 0; + + if (flag_thread_instrumentation) + { + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); + flags= STATE_FLAG_THREAD; + + if (pfs_mutex->m_timed) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + flags|= STATE_FLAG_TIMED; + } + + if (flag_events_waits_current) + { + if (unlikely(pfs_thread->m_events_waits_current >= + & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) + { + locker_lost++; + return NULL; + } + PFS_events_waits *wait= pfs_thread->m_events_waits_current; + state->m_wait= wait; + flags|= STATE_FLAG_EVENT; + + PFS_events_waits *parent_event= wait - 1; + wait->m_event_type= EVENT_TYPE_WAIT; + wait->m_nesting_event_id= parent_event->m_event_id; + wait->m_nesting_event_type= parent_event->m_event_type; + + wait->m_thread_internal_id= pfs_thread->m_thread_internal_id; + wait->m_class= pfs_mutex->m_class; + wait->m_timer_start= timer_start; + wait->m_timer_end= 0; + wait->m_object_instance_addr= pfs_mutex->m_identity; + wait->m_event_id= pfs_thread->m_event_id++; + wait->m_end_event_id= 0; + wait->m_operation= mutex_operation_map[(int) op]; + wait->m_source_file= src_file; + wait->m_source_line= src_line; + wait->m_wait_class= WAIT_CLASS_MUTEX; + + pfs_thread->m_events_waits_current++; + } + } + else + { + if (pfs_mutex->m_timed) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + flags= STATE_FLAG_TIMED; + state->m_thread= NULL; + } + else + { + /* + Complete shortcut. + */ + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ + pfs_mutex->m_mutex_stat.m_wait_stat.aggregate_counted(); + return NULL; + } + } + + state->m_flags= flags; + state->m_mutex= mutex; + return reinterpret_cast<PSI_mutex_locker*> (state); +} + +/** + Implementation of the rwlock instrumentation interface. + @sa PSI_v1::start_rwlock_rdwait + @sa PSI_v1::start_rwlock_wrwait +*/ +PSI_rwlock_locker* +pfs_start_rwlock_wait_v1(PSI_rwlock_locker_state *state, + PSI_rwlock *rwlock, + PSI_rwlock_operation op, + const char *src_file, uint src_line) +{ + PFS_rwlock *pfs_rwlock= reinterpret_cast<PFS_rwlock*> (rwlock); + assert(static_cast<int> (op) >= 0); + assert(static_cast<uint> (op) < array_elements(rwlock_operation_map)); + assert(state != NULL); + assert(pfs_rwlock != NULL); + assert(pfs_rwlock->m_class != NULL); + + /* Operations supported for READ WRITE LOCK */ + + assert( pfs_rwlock->m_class->is_shared_exclusive() + || (op == PSI_RWLOCK_READLOCK) + || (op == PSI_RWLOCK_WRITELOCK) + || (op == PSI_RWLOCK_TRYREADLOCK) + || (op == PSI_RWLOCK_TRYWRITELOCK) + ); + + /* Operations supported for SHARED EXCLUSIVE LOCK */ + + assert( ! pfs_rwlock->m_class->is_shared_exclusive() + || (op == PSI_RWLOCK_SHAREDLOCK) + || (op == PSI_RWLOCK_SHAREDEXCLUSIVELOCK) + || (op == PSI_RWLOCK_EXCLUSIVELOCK) + || (op == PSI_RWLOCK_TRYSHAREDLOCK) + || (op == PSI_RWLOCK_TRYSHAREDEXCLUSIVELOCK) + || (op == PSI_RWLOCK_TRYEXCLUSIVELOCK) + ); + + if (! pfs_rwlock->m_enabled) + return NULL; + + uint flags; + ulonglong timer_start= 0; + + if (flag_thread_instrumentation) + { + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); + flags= STATE_FLAG_THREAD; + + if (pfs_rwlock->m_timed) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + flags|= STATE_FLAG_TIMED; + } + + if (flag_events_waits_current) + { + if (unlikely(pfs_thread->m_events_waits_current >= + & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) + { + locker_lost++; + return NULL; + } + PFS_events_waits *wait= pfs_thread->m_events_waits_current; + state->m_wait= wait; + flags|= STATE_FLAG_EVENT; + + PFS_events_waits *parent_event= wait - 1; + wait->m_event_type= EVENT_TYPE_WAIT; + wait->m_nesting_event_id= parent_event->m_event_id; + wait->m_nesting_event_type= parent_event->m_event_type; + + wait->m_thread_internal_id= pfs_thread->m_thread_internal_id; + wait->m_class= pfs_rwlock->m_class; + wait->m_timer_start= timer_start; + wait->m_timer_end= 0; + wait->m_object_instance_addr= pfs_rwlock->m_identity; + wait->m_event_id= pfs_thread->m_event_id++; + wait->m_end_event_id= 0; + wait->m_operation= rwlock_operation_map[static_cast<int> (op)]; + wait->m_source_file= src_file; + wait->m_source_line= src_line; + wait->m_wait_class= WAIT_CLASS_RWLOCK; + + pfs_thread->m_events_waits_current++; + } + } + else + { + if (pfs_rwlock->m_timed) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + flags= STATE_FLAG_TIMED; + state->m_thread= NULL; + } + else + { + /* + Complete shortcut. + */ + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ + pfs_rwlock->m_rwlock_stat.m_wait_stat.aggregate_counted(); + return NULL; + } + } + + state->m_flags= flags; + state->m_rwlock= rwlock; + state->m_operation= op; + return reinterpret_cast<PSI_rwlock_locker*> (state); +} + +PSI_rwlock_locker* +pfs_start_rwlock_rdwait_v1(PSI_rwlock_locker_state *state, + PSI_rwlock *rwlock, + PSI_rwlock_operation op, + const char *src_file, uint src_line) +{ + assert((op == PSI_RWLOCK_READLOCK) || + (op == PSI_RWLOCK_TRYREADLOCK) || + (op == PSI_RWLOCK_SHAREDLOCK) || + (op == PSI_RWLOCK_TRYSHAREDLOCK)); + + return pfs_start_rwlock_wait_v1(state, rwlock, op, src_file, src_line); +} + +PSI_rwlock_locker* +pfs_start_rwlock_wrwait_v1(PSI_rwlock_locker_state *state, + PSI_rwlock *rwlock, + PSI_rwlock_operation op, + const char *src_file, uint src_line) +{ + assert((op == PSI_RWLOCK_WRITELOCK) || + (op == PSI_RWLOCK_TRYWRITELOCK) || + (op == PSI_RWLOCK_SHAREDEXCLUSIVELOCK) || + (op == PSI_RWLOCK_TRYSHAREDEXCLUSIVELOCK) || + (op == PSI_RWLOCK_EXCLUSIVELOCK) || + (op == PSI_RWLOCK_TRYEXCLUSIVELOCK)); + + return pfs_start_rwlock_wait_v1(state, rwlock, op, src_file, src_line); +} + +/** + Implementation of the cond instrumentation interface. + @sa PSI_v1::start_cond_wait. +*/ +PSI_cond_locker* +pfs_start_cond_wait_v1(PSI_cond_locker_state *state, + PSI_cond *cond, PSI_mutex *mutex, + PSI_cond_operation op, + const char *src_file, uint src_line) +{ + /* + Note about the unused PSI_mutex *mutex parameter: + In the pthread library, a call to pthread_cond_wait() + causes an unlock() + lock() on the mutex associated with the condition. + This mutex operation is not instrumented, so the mutex will still + appear as locked when a thread is waiting on a condition. + This has no impact now, as unlock_mutex() is not recording events. + When unlock_mutex() is implemented by later work logs, + this parameter here will be used to adjust the mutex state, + in start_cond_wait_v1() and end_cond_wait_v1(). + */ + PFS_cond *pfs_cond= reinterpret_cast<PFS_cond*> (cond); + assert(static_cast<int> (op) >= 0); + assert(static_cast<uint> (op) < array_elements(cond_operation_map)); + assert(state != NULL); + assert(pfs_cond != NULL); + assert(pfs_cond->m_class != NULL); + + if (! pfs_cond->m_enabled) + return NULL; + + uint flags; + ulonglong timer_start= 0; + + if (flag_thread_instrumentation) + { + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); + flags= STATE_FLAG_THREAD; + + if (pfs_cond->m_timed) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + flags|= STATE_FLAG_TIMED; + } + + if (flag_events_waits_current) + { + if (unlikely(pfs_thread->m_events_waits_current >= + & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) + { + locker_lost++; + return NULL; + } + PFS_events_waits *wait= pfs_thread->m_events_waits_current; + state->m_wait= wait; + flags|= STATE_FLAG_EVENT; + + PFS_events_waits *parent_event= wait - 1; + wait->m_event_type= EVENT_TYPE_WAIT; + wait->m_nesting_event_id= parent_event->m_event_id; + wait->m_nesting_event_type= parent_event->m_event_type; + + wait->m_thread_internal_id= pfs_thread->m_thread_internal_id; + wait->m_class= pfs_cond->m_class; + wait->m_timer_start= timer_start; + wait->m_timer_end= 0; + wait->m_object_instance_addr= pfs_cond->m_identity; + wait->m_event_id= pfs_thread->m_event_id++; + wait->m_end_event_id= 0; + wait->m_operation= cond_operation_map[static_cast<int> (op)]; + wait->m_source_file= src_file; + wait->m_source_line= src_line; + wait->m_wait_class= WAIT_CLASS_COND; + + pfs_thread->m_events_waits_current++; + } + } + else + { + if (pfs_cond->m_timed) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + flags= STATE_FLAG_TIMED; + } + else + { + /* + Complete shortcut. + */ + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ + pfs_cond->m_cond_stat.m_wait_stat.aggregate_counted(); + return NULL; + } + } + + state->m_flags= flags; + state->m_cond= cond; + state->m_mutex= mutex; + return reinterpret_cast<PSI_cond_locker*> (state); +} + +static inline PFS_TL_LOCK_TYPE lock_flags_to_lock_type(uint flags) +{ + enum thr_lock_type value= static_cast<enum thr_lock_type> (flags); + + switch (value) + { + case TL_READ: + return PFS_TL_READ; + case TL_READ_WITH_SHARED_LOCKS: + return PFS_TL_READ_WITH_SHARED_LOCKS; + case TL_READ_HIGH_PRIORITY: + return PFS_TL_READ_HIGH_PRIORITY; + case TL_READ_NO_INSERT: + return PFS_TL_READ_NO_INSERT; + case TL_WRITE_ALLOW_WRITE: + return PFS_TL_WRITE_ALLOW_WRITE; + case TL_WRITE_CONCURRENT_INSERT: + return PFS_TL_WRITE_CONCURRENT_INSERT; + case TL_WRITE_DELAYED: + return PFS_TL_WRITE_DELAYED; + case TL_WRITE_LOW_PRIORITY: + return PFS_TL_WRITE_LOW_PRIORITY; + case TL_WRITE: + return PFS_TL_WRITE; + + case TL_WRITE_ONLY: + case TL_IGNORE: + case TL_UNLOCK: + case TL_READ_DEFAULT: + case TL_WRITE_DEFAULT: + default: + assert(false); + } + + /* Dead code */ + return PFS_TL_READ; +} + +static inline PFS_TL_LOCK_TYPE external_lock_flags_to_lock_type(uint flags) +{ + assert(flags == F_RDLCK || flags == F_WRLCK); + return (flags == F_RDLCK ? PFS_TL_READ_EXTERNAL : PFS_TL_WRITE_EXTERNAL); +} + +/** + Implementation of the table instrumentation interface. + @sa PSI_v1::start_table_io_wait_v1 +*/ +PSI_table_locker* +pfs_start_table_io_wait_v1(PSI_table_locker_state *state, + PSI_table *table, + PSI_table_io_operation op, + uint index, + const char *src_file, uint src_line) +{ + assert(static_cast<int> (op) >= 0); + assert(static_cast<uint> (op) < array_elements(table_io_operation_map)); + assert(state != NULL); + PFS_table *pfs_table= reinterpret_cast<PFS_table*> (table); + assert(pfs_table != NULL); + assert(pfs_table->m_share != NULL); + + if (! pfs_table->m_io_enabled) + return NULL; + + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + + uint flags; + ulonglong timer_start= 0; + + if (flag_thread_instrumentation) + { + if (pfs_thread == NULL) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); + flags= STATE_FLAG_THREAD; + + if (pfs_table->m_io_timed) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + flags|= STATE_FLAG_TIMED; + } + + if (flag_events_waits_current) + { + if (unlikely(pfs_thread->m_events_waits_current >= + & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) + { + locker_lost++; + return NULL; + } + PFS_events_waits *wait= pfs_thread->m_events_waits_current; + state->m_wait= wait; + flags|= STATE_FLAG_EVENT; + + PFS_events_waits *parent_event= wait - 1; + wait->m_event_type= EVENT_TYPE_WAIT; + wait->m_nesting_event_id= parent_event->m_event_id; + wait->m_nesting_event_type= parent_event->m_event_type; + + PFS_table_share *share= pfs_table->m_share; + wait->m_thread_internal_id= pfs_thread->m_thread_internal_id; + wait->m_class= &global_table_io_class; + wait->m_timer_start= timer_start; + wait->m_timer_end= 0; + wait->m_object_instance_addr= pfs_table->m_identity; + wait->m_event_id= pfs_thread->m_event_id++; + wait->m_end_event_id= 0; + wait->m_operation= table_io_operation_map[static_cast<int> (op)]; + wait->m_flags= 0; + wait->m_object_type= share->get_object_type(); + wait->m_weak_table_share= share; + wait->m_weak_version= share->get_version(); + wait->m_index= index; + wait->m_source_file= src_file; + wait->m_source_line= src_line; + wait->m_wait_class= WAIT_CLASS_TABLE; + + pfs_thread->m_events_waits_current++; + } + } + else + { + if (pfs_table->m_io_timed) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + flags= STATE_FLAG_TIMED; + } + else + { + /* TODO: consider a shortcut here */ + flags= 0; + } + } + + state->m_flags= flags; + state->m_table= table; + state->m_io_operation= op; + state->m_index= index; + return reinterpret_cast<PSI_table_locker*> (state); +} + +/** + Implementation of the table instrumentation interface. + @sa PSI_v1::start_table_lock_wait. +*/ +PSI_table_locker* +pfs_start_table_lock_wait_v1(PSI_table_locker_state *state, + PSI_table *table, + PSI_table_lock_operation op, + ulong op_flags, + const char *src_file, uint src_line) +{ + assert(state != NULL); + assert((op == PSI_TABLE_LOCK) || (op == PSI_TABLE_EXTERNAL_LOCK)); + + PFS_table *pfs_table= reinterpret_cast<PFS_table*> (table); + + assert(pfs_table != NULL); + assert(pfs_table->m_share != NULL); + + if (! pfs_table->m_lock_enabled) + return NULL; + + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + + PFS_TL_LOCK_TYPE lock_type; + + switch (op) + { + case PSI_TABLE_LOCK: + lock_type= lock_flags_to_lock_type(op_flags); + pfs_table->m_internal_lock= lock_type; + break; + case PSI_TABLE_EXTERNAL_LOCK: + /* + See the handler::external_lock() API design, + there is no handler::external_unlock(). + */ + if (op_flags == F_UNLCK) + { + pfs_table->m_external_lock= PFS_TL_NONE; + return NULL; + } + lock_type= external_lock_flags_to_lock_type(op_flags); + pfs_table->m_external_lock= lock_type; + break; + default: + lock_type= PFS_TL_READ; + assert(false); + } + + assert((uint) lock_type < array_elements(table_lock_operation_map)); + + uint flags; + ulonglong timer_start= 0; + + if (flag_thread_instrumentation) + { + if (pfs_thread == NULL) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); + flags= STATE_FLAG_THREAD; + + if (pfs_table->m_lock_timed) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + flags|= STATE_FLAG_TIMED; + } + + if (flag_events_waits_current) + { + if (unlikely(pfs_thread->m_events_waits_current >= + & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) + { + locker_lost++; + return NULL; + } + PFS_events_waits *wait= pfs_thread->m_events_waits_current; + state->m_wait= wait; + flags|= STATE_FLAG_EVENT; + + PFS_events_waits *parent_event= wait - 1; + wait->m_event_type= EVENT_TYPE_WAIT; + wait->m_nesting_event_id= parent_event->m_event_id; + wait->m_nesting_event_type= parent_event->m_event_type; + + PFS_table_share *share= pfs_table->m_share; + wait->m_thread_internal_id= pfs_thread->m_thread_internal_id; + wait->m_class= &global_table_lock_class; + wait->m_timer_start= timer_start; + wait->m_timer_end= 0; + wait->m_object_instance_addr= pfs_table->m_identity; + wait->m_event_id= pfs_thread->m_event_id++; + wait->m_end_event_id= 0; + wait->m_operation= table_lock_operation_map[lock_type]; + wait->m_flags= 0; + wait->m_object_type= share->get_object_type(); + wait->m_weak_table_share= share; + wait->m_weak_version= share->get_version(); + wait->m_index= 0; + wait->m_source_file= src_file; + wait->m_source_line= src_line; + wait->m_wait_class= WAIT_CLASS_TABLE; + + pfs_thread->m_events_waits_current++; + } + } + else + { + if (pfs_table->m_lock_timed) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + flags= STATE_FLAG_TIMED; + } + else + { + /* TODO: consider a shortcut here */ + flags= 0; + } + } + + state->m_flags= flags; + state->m_table= table; + state->m_index= lock_type; + return reinterpret_cast<PSI_table_locker*> (state); +} + +/** + Implementation of the file instrumentation interface. + @sa PSI_v1::get_thread_file_name_locker. +*/ +PSI_file_locker* +pfs_get_thread_file_name_locker_v1(PSI_file_locker_state *state, + PSI_file_key key, + PSI_file_operation op, + const char *name, const void *identity) +{ + assert(static_cast<int> (op) >= 0); + assert(static_cast<uint> (op) < array_elements(file_operation_map)); + assert(state != NULL); + + if (psi_unlikely(! flag_global_instrumentation)) + return NULL; + PFS_file_class *klass= find_file_class(key); + if (unlikely(klass == NULL)) + return NULL; + if (! klass->m_enabled) + return NULL; + + /* Needed for the LF_HASH */ + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + + if (flag_thread_instrumentation && ! pfs_thread->m_enabled) + return NULL; + + uint flags; + + state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); + flags= STATE_FLAG_THREAD; + + if (klass->m_timed) + flags|= STATE_FLAG_TIMED; + + if (flag_events_waits_current) + { + if (unlikely(pfs_thread->m_events_waits_current >= + & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) + { + locker_lost++; + return NULL; + } + PFS_events_waits *wait= pfs_thread->m_events_waits_current; + state->m_wait= wait; + flags|= STATE_FLAG_EVENT; + + PFS_events_waits *parent_event= wait - 1; + wait->m_event_type= EVENT_TYPE_WAIT; + wait->m_nesting_event_id= parent_event->m_event_id; + wait->m_nesting_event_type= parent_event->m_event_type; + + wait->m_thread_internal_id= pfs_thread->m_thread_internal_id; + wait->m_class= klass; + wait->m_timer_start= 0; + wait->m_timer_end= 0; + wait->m_object_instance_addr= NULL; + wait->m_weak_file= NULL; + wait->m_weak_version= 0; + wait->m_event_id= pfs_thread->m_event_id++; + wait->m_end_event_id= 0; + wait->m_operation= file_operation_map[static_cast<int> (op)]; + wait->m_wait_class= WAIT_CLASS_FILE; + + pfs_thread->m_events_waits_current++; + } + + state->m_flags= flags; + state->m_file= NULL; + state->m_name= name; + state->m_class= klass; + state->m_operation= op; + return reinterpret_cast<PSI_file_locker*> (state); +} + +/** + Implementation of the file instrumentation interface. + @sa PSI_v1::get_thread_file_stream_locker. +*/ +PSI_file_locker* +pfs_get_thread_file_stream_locker_v1(PSI_file_locker_state *state, + PSI_file *file, PSI_file_operation op) +{ + PFS_file *pfs_file= reinterpret_cast<PFS_file*> (file); + assert(static_cast<int> (op) >= 0); + assert(static_cast<uint> (op) < array_elements(file_operation_map)); + assert(state != NULL); + + if (unlikely(pfs_file == NULL)) + return NULL; + assert(pfs_file->m_class != NULL); + PFS_file_class *klass= pfs_file->m_class; + + if (! pfs_file->m_enabled) + return NULL; + + /* Needed for the LF_HASH */ + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + + uint flags; + + /* Always populated */ + state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); + + if (flag_thread_instrumentation) + { + if (! pfs_thread->m_enabled) + return NULL; + flags= STATE_FLAG_THREAD; + + if (pfs_file->m_timed) + flags|= STATE_FLAG_TIMED; + + if (flag_events_waits_current) + { + if (unlikely(pfs_thread->m_events_waits_current >= + & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) + { + locker_lost++; + return NULL; + } + PFS_events_waits *wait= pfs_thread->m_events_waits_current; + state->m_wait= wait; + flags|= STATE_FLAG_EVENT; + + PFS_events_waits *parent_event= wait - 1; + wait->m_event_type= EVENT_TYPE_WAIT; + wait->m_nesting_event_id= parent_event->m_event_id; + wait->m_nesting_event_type= parent_event->m_event_type; + + wait->m_thread_internal_id= pfs_thread->m_thread_internal_id; + wait->m_class= klass; + wait->m_timer_start= 0; + wait->m_timer_end= 0; + wait->m_object_instance_addr= pfs_file; + wait->m_weak_file= pfs_file; + wait->m_weak_version= pfs_file->get_version(); + wait->m_event_id= pfs_thread->m_event_id++; + wait->m_end_event_id= 0; + wait->m_operation= file_operation_map[static_cast<int> (op)]; + wait->m_wait_class= WAIT_CLASS_FILE; + + pfs_thread->m_events_waits_current++; + } + } + else + { + if (pfs_file->m_timed) + { + flags= STATE_FLAG_TIMED; + } + else + { + /* TODO: consider a shortcut. */ + flags= 0; + } + } + + state->m_flags= flags; + state->m_file= reinterpret_cast<PSI_file*> (pfs_file); + state->m_operation= op; + state->m_name= NULL; + state->m_class= klass; + return reinterpret_cast<PSI_file_locker*> (state); +} + +/** + Implementation of the file instrumentation interface. + @sa PSI_v1::get_thread_file_descriptor_locker. +*/ +PSI_file_locker* +pfs_get_thread_file_descriptor_locker_v1(PSI_file_locker_state *state, + File file, PSI_file_operation op) +{ + int index= static_cast<int> (file); + assert(static_cast<int> (op) >= 0); + assert(static_cast<uint> (op) < array_elements(file_operation_map)); + assert(state != NULL); + + if (unlikely((index < 0) || (index >= file_handle_max))) + return NULL; + + PFS_file *pfs_file= file_handle_array[index]; + if (unlikely(pfs_file == NULL)) + return NULL; + + /* + We are about to close a file by descriptor number, + and the calling code still holds the descriptor. + Cleanup the file descriptor <--> file instrument association. + Remove the instrumentation *before* the close to avoid race + conditions with another thread opening a file + (that could be given the same descriptor). + */ + if (op == PSI_FILE_CLOSE) + file_handle_array[index]= NULL; + + if (! pfs_file->m_enabled) + return NULL; + + assert(pfs_file->m_class != NULL); + PFS_file_class *klass= pfs_file->m_class; + + /* Needed for the LF_HASH */ + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + + uint flags; + + /* Always populated */ + state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); + + if (flag_thread_instrumentation) + { + if (! pfs_thread->m_enabled) + return NULL; + flags= STATE_FLAG_THREAD; + + if (pfs_file->m_timed) + flags|= STATE_FLAG_TIMED; + + if (flag_events_waits_current) + { + if (unlikely(pfs_thread->m_events_waits_current >= + & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) + { + locker_lost++; + return NULL; + } + PFS_events_waits *wait= pfs_thread->m_events_waits_current; + state->m_wait= wait; + flags|= STATE_FLAG_EVENT; + + PFS_events_waits *parent_event= wait - 1; + wait->m_event_type= EVENT_TYPE_WAIT; + wait->m_nesting_event_id= parent_event->m_event_id; + wait->m_nesting_event_type= parent_event->m_event_type; + + wait->m_thread_internal_id= pfs_thread->m_thread_internal_id; + wait->m_class= klass; + wait->m_timer_start= 0; + wait->m_timer_end= 0; + wait->m_object_instance_addr= pfs_file; + wait->m_weak_file= pfs_file; + wait->m_weak_version= pfs_file->get_version(); + wait->m_event_id= pfs_thread->m_event_id++; + wait->m_end_event_id= 0; + wait->m_operation= file_operation_map[static_cast<int> (op)]; + wait->m_wait_class= WAIT_CLASS_FILE; + + pfs_thread->m_events_waits_current++; + } + } + else + { + if (pfs_file->m_timed) + { + flags= STATE_FLAG_TIMED; + } + else + { + /* TODO: consider a shortcut. */ + flags= 0; + } + } + + state->m_flags= flags; + state->m_file= reinterpret_cast<PSI_file*> (pfs_file); + state->m_operation= op; + state->m_name= NULL; + state->m_class= klass; + return reinterpret_cast<PSI_file_locker*> (state); +} + +/** Socket locker */ + +PSI_socket_locker* +pfs_start_socket_wait_v1(PSI_socket_locker_state *state, + PSI_socket *socket, + PSI_socket_operation op, + size_t count, + const char *src_file, uint src_line) +{ + assert(static_cast<int> (op) >= 0); + assert(static_cast<uint> (op) < array_elements(socket_operation_map)); + assert(state != NULL); + PFS_socket *pfs_socket= reinterpret_cast<PFS_socket*> (socket); + + assert(pfs_socket != NULL); + assert(pfs_socket->m_class != NULL); + + if (!pfs_socket->m_enabled || pfs_socket->m_idle) + return NULL; + + uint flags= 0; + ulonglong timer_start= 0; + + if (flag_thread_instrumentation) + { + /* + Do not use pfs_socket->m_thread_owner here, + as different threads may use concurrently the same socket, + for example during a KILL. + */ + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + + if (unlikely(pfs_thread == NULL)) + return NULL; + + if (!pfs_thread->m_enabled) + return NULL; + + state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); + flags= STATE_FLAG_THREAD; + + if (pfs_socket->m_timed) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + flags|= STATE_FLAG_TIMED; + } + + if (flag_events_waits_current) + { + if (unlikely(pfs_thread->m_events_waits_current >= + & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) + { + locker_lost++; + return NULL; + } + PFS_events_waits *wait= pfs_thread->m_events_waits_current; + state->m_wait= wait; + flags|= STATE_FLAG_EVENT; + + PFS_events_waits *parent_event= wait - 1; + wait->m_event_type= EVENT_TYPE_WAIT; + wait->m_nesting_event_id= parent_event->m_event_id; + wait->m_nesting_event_type= parent_event->m_event_type; + wait->m_thread_internal_id= pfs_thread->m_thread_internal_id; + wait->m_class= pfs_socket->m_class; + wait->m_timer_start= timer_start; + wait->m_timer_end= 0; + wait->m_object_instance_addr= pfs_socket->m_identity; + wait->m_weak_socket= pfs_socket; + wait->m_weak_version= pfs_socket->get_version(); + wait->m_event_id= pfs_thread->m_event_id++; + wait->m_end_event_id= 0; + wait->m_operation= socket_operation_map[static_cast<int>(op)]; + wait->m_source_file= src_file; + wait->m_source_line= src_line; + wait->m_number_of_bytes= count; + wait->m_wait_class= WAIT_CLASS_SOCKET; + + pfs_thread->m_events_waits_current++; + } + } + else + { + if (pfs_socket->m_timed) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + flags= STATE_FLAG_TIMED; + } + else + { + /* + Even if timing is disabled, end_socket_wait() still needs a locker to + capture the number of bytes sent or received by the socket operation. + For operations that do not have a byte count, then just increment the + event counter and return a NULL locker. + */ + switch (op) + { + case PSI_SOCKET_CONNECT: + case PSI_SOCKET_CREATE: + case PSI_SOCKET_BIND: + case PSI_SOCKET_SEEK: + case PSI_SOCKET_OPT: + case PSI_SOCKET_STAT: + case PSI_SOCKET_SHUTDOWN: + case PSI_SOCKET_CLOSE: + case PSI_SOCKET_SELECT: + pfs_socket->m_socket_stat.m_io_stat.m_misc.aggregate_counted(); + return NULL; + default: + break; + } + } + } + + state->m_flags= flags; + state->m_socket= socket; + state->m_operation= op; + return reinterpret_cast<PSI_socket_locker*> (state); +} + +/** + Implementation of the mutex instrumentation interface. + @sa PSI_v1::unlock_mutex. +*/ +void pfs_unlock_mutex_v1(PSI_mutex *mutex) +{ + PFS_mutex *pfs_mutex= reinterpret_cast<PFS_mutex*> (mutex); + + assert(pfs_mutex != NULL); + + /* + Note that this code is still protected by the instrumented mutex, + and therefore is thread safe. See inline_mysql_mutex_unlock(). + */ + + /* Always update the instrumented state */ + pfs_mutex->m_owner= NULL; + pfs_mutex->m_last_locked= 0; + +#ifdef LATER_WL2333 + /* + See WL#2333: SHOW ENGINE ... LOCK STATUS. + PFS_mutex::m_lock_stat is not exposed in user visible tables + currently, so there is no point spending time computing it. + */ + if (! pfs_mutex->m_enabled) + return; + + if (! pfs_mutex->m_timed) + return; + + ulonglong locked_time; + locked_time= get_timer_pico_value(wait_timer) - pfs_mutex->m_last_locked; + pfs_mutex->m_mutex_stat.m_lock_stat.aggregate_value(locked_time); +#endif +} + +/** + Implementation of the rwlock instrumentation interface. + @sa PSI_v1::unlock_rwlock. +*/ +void pfs_unlock_rwlock_v1(PSI_rwlock *rwlock) +{ + PFS_rwlock *pfs_rwlock= reinterpret_cast<PFS_rwlock*> (rwlock); + assert(pfs_rwlock != NULL); + assert(pfs_rwlock == sanitize_rwlock(pfs_rwlock)); + assert(pfs_rwlock->m_class != NULL); + assert(pfs_rwlock->m_lock.is_populated()); + + bool last_writer= false; + bool last_reader= false; + + /* + Note that this code is still protected by the instrumented rwlock, + and therefore is: + - thread safe for write locks + - almost thread safe for read locks (pfs_rwlock->m_readers is unsafe). + See inline_mysql_rwlock_unlock() + */ + + /* Always update the instrumented state */ + if (pfs_rwlock->m_writer != NULL) + { + /* Nominal case, a writer is unlocking. */ + last_writer= true; + pfs_rwlock->m_writer= NULL; + /* Reset the readers stats, they could be off */ + pfs_rwlock->m_readers= 0; + } + else if (likely(pfs_rwlock->m_readers > 0)) + { + /* Nominal case, a reader is unlocking. */ + if (--(pfs_rwlock->m_readers) == 0) + last_reader= true; + } + else + { + /* + Edge case, we have no writer and no readers, + on an unlock event. + This is possible for: + - partial instrumentation + - instrumentation disabled at runtime, + see when get_thread_rwlock_locker_v1() returns NULL + No further action is taken here, the next + write lock will put the statistics is a valid state. + */ + } + +#ifdef LATER_WL2333 + /* See WL#2333: SHOW ENGINE ... LOCK STATUS. */ + + if (! pfs_rwlock->m_enabled) + return; + + if (! pfs_rwlock->m_timed) + return; + + ulonglong locked_time; + if (last_writer) + { + locked_time= get_timer_pico_value(wait_timer) - pfs_rwlock->m_last_written; + pfs_rwlock->m_rwlock_stat.m_write_lock_stat.aggregate_value(locked_time); + } + else if (last_reader) + { + locked_time= get_timer_pico_value(wait_timer) - pfs_rwlock->m_last_read; + pfs_rwlock->m_rwlock_stat.m_read_lock_stat.aggregate_value(locked_time); + } +#else + (void) last_reader; + (void) last_writer; +#endif +} + +/** + Implementation of the cond instrumentation interface. + @sa PSI_v1::signal_cond. +*/ +void pfs_signal_cond_v1(PSI_cond* cond) +{ +#ifdef PFS_LATER + PFS_cond *pfs_cond= reinterpret_cast<PFS_cond*> (cond); + + assert(pfs_cond != NULL); + + pfs_cond->m_cond_stat.m_signal_count++; +#endif +} + +/** + Implementation of the cond instrumentation interface. + @sa PSI_v1::broadcast_cond. +*/ +void pfs_broadcast_cond_v1(PSI_cond* cond) +{ +#ifdef PFS_LATER + PFS_cond *pfs_cond= reinterpret_cast<PFS_cond*> (cond); + + assert(pfs_cond != NULL); + + pfs_cond->m_cond_stat.m_broadcast_count++; +#endif +} + +/** + Implementation of the idle instrumentation interface. + @sa PSI_v1::start_idle_wait. +*/ +PSI_idle_locker* +pfs_start_idle_wait_v1(PSI_idle_locker_state* state, const char *src_file, uint src_line) +{ + assert(state != NULL); + + if (psi_unlikely(! flag_global_instrumentation)) + return NULL; + + if (!global_idle_class.m_enabled) + return NULL; + + uint flags= 0; + ulonglong timer_start= 0; + + if (flag_thread_instrumentation) + { + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (!pfs_thread->m_enabled) + return NULL; + state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); + flags= STATE_FLAG_THREAD; + + assert(pfs_thread->m_events_statements_count == 0); + + if (global_idle_class.m_timed) + { + timer_start= get_timer_raw_value_and_function(idle_timer, &state->m_timer); + state->m_timer_start= timer_start; + flags|= STATE_FLAG_TIMED; + } + + if (flag_events_waits_current) + { + if (unlikely(pfs_thread->m_events_waits_current >= + & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) + { + locker_lost++; + return NULL; + } + PFS_events_waits *wait= pfs_thread->m_events_waits_current; + state->m_wait= wait; + flags|= STATE_FLAG_EVENT; + + wait->m_event_type= EVENT_TYPE_WAIT; + /* + IDLE events are waits, but by definition we know that + such waits happen outside of any STAGE and STATEMENT, + so they have no parents. + */ + wait->m_nesting_event_id= 0; + /* no need to set wait->m_nesting_event_type */ + + wait->m_thread_internal_id= pfs_thread->m_thread_internal_id; + wait->m_class= &global_idle_class; + wait->m_timer_start= timer_start; + wait->m_timer_end= 0; + wait->m_event_id= pfs_thread->m_event_id++; + wait->m_end_event_id= 0; + wait->m_operation= OPERATION_TYPE_IDLE; + wait->m_source_file= src_file; + wait->m_source_line= src_line; + wait->m_wait_class= WAIT_CLASS_IDLE; + + pfs_thread->m_events_waits_current++; + } + } + else + { + if (global_idle_class.m_timed) + { + timer_start= get_timer_raw_value_and_function(idle_timer, &state->m_timer); + state->m_timer_start= timer_start; + flags= STATE_FLAG_TIMED; + } + } + + state->m_flags= flags; + return reinterpret_cast<PSI_idle_locker*> (state); +} + +/** + Implementation of the mutex instrumentation interface. + @sa PSI_v1::end_idle_wait. +*/ +void pfs_end_idle_wait_v1(PSI_idle_locker* locker) +{ + PSI_idle_locker_state *state= reinterpret_cast<PSI_idle_locker_state*> (locker); + assert(state != NULL); + ulonglong timer_end= 0; + ulonglong wait_time= 0; + + uint flags= state->m_flags; + + if (flags & STATE_FLAG_TIMED) + { + timer_end= state->m_timer(); + wait_time= timer_end - state->m_timer_start; + } + + if (flags & STATE_FLAG_THREAD) + { + PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); + PFS_single_stat *event_name_array; + event_name_array= thread->write_instr_class_waits_stats(); + + if (flags & STATE_FLAG_TIMED) + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ + event_name_array[GLOBAL_IDLE_EVENT_INDEX].aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ + event_name_array[GLOBAL_IDLE_EVENT_INDEX].aggregate_counted(); + } + + if (flags & STATE_FLAG_EVENT) + { + PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); + assert(wait != NULL); + + wait->m_timer_end= timer_end; + wait->m_end_event_id= thread->m_event_id; + if (thread->m_flag_events_waits_history) + insert_events_waits_history(thread, wait); + if (thread->m_flag_events_waits_history_long) + insert_events_waits_history_long(wait); + thread->m_events_waits_current--; + + assert(wait == thread->m_events_waits_current); + } + } + + if (flags & STATE_FLAG_TIMED) + { + /* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME (timed) */ + global_idle_stat.aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME (counted) */ + global_idle_stat.aggregate_counted(); + } +} + +/** + Implementation of the mutex instrumentation interface. + @sa PSI_v1::end_mutex_wait. +*/ +void pfs_end_mutex_wait_v1(PSI_mutex_locker* locker, int rc) +{ + PSI_mutex_locker_state *state= reinterpret_cast<PSI_mutex_locker_state*> (locker); + assert(state != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + + PFS_mutex *mutex= reinterpret_cast<PFS_mutex *> (state->m_mutex); + assert(mutex != NULL); + PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); + + uint flags= state->m_flags; + + if (flags & STATE_FLAG_TIMED) + { + timer_end= state->m_timer(); + wait_time= timer_end - state->m_timer_start; + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */ + mutex->m_mutex_stat.m_wait_stat.aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ + mutex->m_mutex_stat.m_wait_stat.aggregate_counted(); + } + + if (likely(rc == 0)) + { + mutex->m_owner= thread; + mutex->m_last_locked= timer_end; + } + + if (flags & STATE_FLAG_THREAD) + { + PFS_single_stat *event_name_array; + event_name_array= thread->write_instr_class_waits_stats(); + uint index= mutex->m_class->m_event_name_index; + + assert(index <= wait_class_max); + assert(sanitize_thread(thread) != NULL); + + if (flags & STATE_FLAG_TIMED) + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ + event_name_array[index].aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ + event_name_array[index].aggregate_counted(); + } + + if (flags & STATE_FLAG_EVENT) + { + PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); + assert(wait != NULL); + + wait->m_timer_end= timer_end; + wait->m_end_event_id= thread->m_event_id; + if (thread->m_flag_events_waits_history) + insert_events_waits_history(thread, wait); + if (thread->m_flag_events_waits_history_long) + insert_events_waits_history_long(wait); + thread->m_events_waits_current--; + + assert(wait == thread->m_events_waits_current); + } + } +} + +/** + Implementation of the rwlock instrumentation interface. + @sa PSI_v1::end_rwlock_rdwait. +*/ +void pfs_end_rwlock_rdwait_v1(PSI_rwlock_locker* locker, int rc) +{ + PSI_rwlock_locker_state *state= reinterpret_cast<PSI_rwlock_locker_state*> (locker); + assert(state != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + + PFS_rwlock *rwlock= reinterpret_cast<PFS_rwlock *> (state->m_rwlock); + assert(rwlock != NULL); + + if (state->m_flags & STATE_FLAG_TIMED) + { + timer_end= state->m_timer(); + wait_time= timer_end - state->m_timer_start; + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */ + rwlock->m_rwlock_stat.m_wait_stat.aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ + rwlock->m_rwlock_stat.m_wait_stat.aggregate_counted(); + } + + if (rc == 0) + { + /* + Warning: + Multiple threads can execute this section concurrently + (since multiple readers can execute in parallel). + The statistics generated are not safe, which is why they are + just statistics, not facts. + */ + if (rwlock->m_readers == 0) + rwlock->m_last_read= timer_end; + rwlock->m_writer= NULL; + rwlock->m_readers++; + } + + if (state->m_flags & STATE_FLAG_THREAD) + { + PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); + assert(thread != NULL); + + PFS_single_stat *event_name_array; + event_name_array= thread->write_instr_class_waits_stats(); + uint index= rwlock->m_class->m_event_name_index; + + if (state->m_flags & STATE_FLAG_TIMED) + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ + event_name_array[index].aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ + event_name_array[index].aggregate_counted(); + } + + if (state->m_flags & STATE_FLAG_EVENT) + { + PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); + assert(wait != NULL); + + wait->m_timer_end= timer_end; + wait->m_end_event_id= thread->m_event_id; + if (thread->m_flag_events_waits_history) + insert_events_waits_history(thread, wait); + if (thread->m_flag_events_waits_history_long) + insert_events_waits_history_long(wait); + thread->m_events_waits_current--; + + assert(wait == thread->m_events_waits_current); + } + } +} + +/** + Implementation of the rwlock instrumentation interface. + @sa PSI_v1::end_rwlock_wrwait. +*/ +void pfs_end_rwlock_wrwait_v1(PSI_rwlock_locker* locker, int rc) +{ + PSI_rwlock_locker_state *state= reinterpret_cast<PSI_rwlock_locker_state*> (locker); + assert(state != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + + PFS_rwlock *rwlock= reinterpret_cast<PFS_rwlock *> (state->m_rwlock); + assert(rwlock != NULL); + PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); + + if (state->m_flags & STATE_FLAG_TIMED) + { + timer_end= state->m_timer(); + wait_time= timer_end - state->m_timer_start; + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */ + rwlock->m_rwlock_stat.m_wait_stat.aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ + rwlock->m_rwlock_stat.m_wait_stat.aggregate_counted(); + } + + if (likely(rc == 0)) + { + /* Thread safe : we are protected by the instrumented rwlock */ + rwlock->m_writer= thread; + rwlock->m_last_written= timer_end; + + if ((state->m_operation != PSI_RWLOCK_SHAREDEXCLUSIVELOCK) && + (state->m_operation != PSI_RWLOCK_TRYSHAREDEXCLUSIVELOCK)) + { + /* Reset the readers stats, they could be off */ + rwlock->m_readers= 0; + rwlock->m_last_read= 0; + } + } + + if (state->m_flags & STATE_FLAG_THREAD) + { + PFS_single_stat *event_name_array; + event_name_array= thread->write_instr_class_waits_stats(); + uint index= rwlock->m_class->m_event_name_index; + + if (state->m_flags & STATE_FLAG_TIMED) + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ + event_name_array[index].aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ + event_name_array[index].aggregate_counted(); + } + + if (state->m_flags & STATE_FLAG_EVENT) + { + PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); + assert(wait != NULL); + + wait->m_timer_end= timer_end; + wait->m_end_event_id= thread->m_event_id; + if (thread->m_flag_events_waits_history) + insert_events_waits_history(thread, wait); + if (thread->m_flag_events_waits_history_long) + insert_events_waits_history_long(wait); + thread->m_events_waits_current--; + + assert(wait == thread->m_events_waits_current); + } + } +} + +/** + Implementation of the cond instrumentation interface. + @sa PSI_v1::end_cond_wait. +*/ +void pfs_end_cond_wait_v1(PSI_cond_locker* locker, int rc) +{ + PSI_cond_locker_state *state= reinterpret_cast<PSI_cond_locker_state*> (locker); + assert(state != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + + PFS_cond *cond= reinterpret_cast<PFS_cond *> (state->m_cond); + /* PFS_mutex *mutex= reinterpret_cast<PFS_mutex *> (state->m_mutex); */ + + if (state->m_flags & STATE_FLAG_TIMED) + { + timer_end= state->m_timer(); + wait_time= timer_end - state->m_timer_start; + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */ + cond->m_cond_stat.m_wait_stat.aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ + cond->m_cond_stat.m_wait_stat.aggregate_counted(); + } + + if (state->m_flags & STATE_FLAG_THREAD) + { + PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); + assert(thread != NULL); + + PFS_single_stat *event_name_array; + event_name_array= thread->write_instr_class_waits_stats(); + uint index= cond->m_class->m_event_name_index; + + if (state->m_flags & STATE_FLAG_TIMED) + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ + event_name_array[index].aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ + event_name_array[index].aggregate_counted(); + } + + if (state->m_flags & STATE_FLAG_EVENT) + { + PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); + assert(wait != NULL); + + wait->m_timer_end= timer_end; + wait->m_end_event_id= thread->m_event_id; + if (thread->m_flag_events_waits_history) + insert_events_waits_history(thread, wait); + if (thread->m_flag_events_waits_history_long) + insert_events_waits_history_long(wait); + thread->m_events_waits_current--; + + assert(wait == thread->m_events_waits_current); + } + } +} + +/** + Implementation of the table instrumentation interface. + @sa PSI_v1::end_table_io_wait. +*/ +void pfs_end_table_io_wait_v1(PSI_table_locker* locker, ulonglong numrows) +{ + PSI_table_locker_state *state= reinterpret_cast<PSI_table_locker_state*> (locker); + assert(state != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + + PFS_table *table= reinterpret_cast<PFS_table *> (state->m_table); + assert(table != NULL); + + PFS_single_stat *stat; + PFS_table_io_stat *table_io_stat; + + assert((state->m_index < table->m_share->m_key_count) || + (state->m_index == MAX_INDEXES)); + + table_io_stat= & table->m_table_stat.m_index_stat[state->m_index]; + table_io_stat->m_has_data= true; + + switch (state->m_io_operation) + { + case PSI_TABLE_FETCH_ROW: + stat= & table_io_stat->m_fetch; + break; + case PSI_TABLE_WRITE_ROW: + stat= & table_io_stat->m_insert; + break; + case PSI_TABLE_UPDATE_ROW: + stat= & table_io_stat->m_update; + break; + case PSI_TABLE_DELETE_ROW: + stat= & table_io_stat->m_delete; + break; + default: + assert(false); + stat= NULL; + break; + } + + uint flags= state->m_flags; + + if (flags & STATE_FLAG_TIMED) + { + timer_end= state->m_timer(); + wait_time= timer_end - state->m_timer_start; + stat->aggregate_many_value(wait_time, numrows); + } + else + { + stat->aggregate_counted(numrows); + } + + if (flags & STATE_FLAG_THREAD) + { + PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); + assert(thread != NULL); + + PFS_single_stat *event_name_array; + event_name_array= thread->write_instr_class_waits_stats(); + + /* + Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME + (for wait/io/table/sql/handler) + */ + if (flags & STATE_FLAG_TIMED) + { + event_name_array[GLOBAL_TABLE_IO_EVENT_INDEX].aggregate_many_value(wait_time, numrows); + } + else + { + event_name_array[GLOBAL_TABLE_IO_EVENT_INDEX].aggregate_counted(numrows); + } + + if (flags & STATE_FLAG_EVENT) + { + PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); + assert(wait != NULL); + + wait->m_timer_end= timer_end; + wait->m_end_event_id= thread->m_event_id; + wait->m_number_of_bytes= static_cast<size_t>(numrows); + if (thread->m_flag_events_waits_history) + insert_events_waits_history(thread, wait); + if (thread->m_flag_events_waits_history_long) + insert_events_waits_history_long(wait); + thread->m_events_waits_current--; + + assert(wait == thread->m_events_waits_current); + } + } + + table->m_has_io_stats= true; +} + +/** + Implementation of the table instrumentation interface. + @sa PSI_v1::end_table_lock_wait. +*/ +void pfs_end_table_lock_wait_v1(PSI_table_locker* locker) +{ + PSI_table_locker_state *state= reinterpret_cast<PSI_table_locker_state*> (locker); + assert(state != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + + PFS_table *table= reinterpret_cast<PFS_table *> (state->m_table); + assert(table != NULL); + + PFS_single_stat *stat= & table->m_table_stat.m_lock_stat.m_stat[state->m_index]; + + uint flags= state->m_flags; + + if (flags & STATE_FLAG_TIMED) + { + timer_end= state->m_timer(); + wait_time= timer_end - state->m_timer_start; + stat->aggregate_value(wait_time); + } + else + { + stat->aggregate_counted(); + } + + if (flags & STATE_FLAG_THREAD) + { + PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); + assert(thread != NULL); + + PFS_single_stat *event_name_array; + event_name_array= thread->write_instr_class_waits_stats(); + + /* + Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME + (for wait/lock/table/sql/handler) + */ + if (flags & STATE_FLAG_TIMED) + { + event_name_array[GLOBAL_TABLE_LOCK_EVENT_INDEX].aggregate_value(wait_time); + } + else + { + event_name_array[GLOBAL_TABLE_LOCK_EVENT_INDEX].aggregate_counted(); + } + + if (flags & STATE_FLAG_EVENT) + { + PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); + assert(wait != NULL); + + wait->m_timer_end= timer_end; + wait->m_end_event_id= thread->m_event_id; + if (thread->m_flag_events_waits_history) + insert_events_waits_history(thread, wait); + if (thread->m_flag_events_waits_history_long) + insert_events_waits_history_long(wait); + thread->m_events_waits_current--; + + assert(wait == thread->m_events_waits_current); + } + } + + table->m_has_lock_stats= true; +} + +void pfs_start_file_wait_v1(PSI_file_locker *locker, + size_t count, + const char *src_file, + uint src_line); + +void pfs_end_file_wait_v1(PSI_file_locker *locker, + size_t count); + +/** + Implementation of the file instrumentation interface. + @sa PSI_v1::start_file_open_wait. +*/ +void pfs_start_file_open_wait_v1(PSI_file_locker *locker, + const char *src_file, + uint src_line) +{ + pfs_start_file_wait_v1(locker, 0, src_file, src_line); + + return; +} + +/** + Implementation of the file instrumentation interface. + @sa PSI_v1::end_file_open_wait. +*/ +PSI_file* +pfs_end_file_open_wait_v1(PSI_file_locker *locker, + void *result) +{ + PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); + assert(state != NULL); + + switch (state->m_operation) + { + case PSI_FILE_STAT: + case PSI_FILE_RENAME: + break; + case PSI_FILE_STREAM_OPEN: + case PSI_FILE_CREATE: + case PSI_FILE_OPEN: + if (result != NULL) + { + PFS_file_class *klass= reinterpret_cast<PFS_file_class*> (state->m_class); + PFS_thread *thread= reinterpret_cast<PFS_thread*> (state->m_thread); + const char *name= state->m_name; + uint len= (uint)strlen(name); + PFS_file *pfs_file= find_or_create_file(thread, klass, name, len, true); + state->m_file= reinterpret_cast<PSI_file*> (pfs_file); + } + break; + default: + assert(false); + break; + } + + pfs_end_file_wait_v1(locker, 0); + + return state->m_file; +} + +/** + Implementation of the file instrumentation interface. + @sa PSI_v1::end_file_open_wait_and_bind_to_descriptor. +*/ +void pfs_end_file_open_wait_and_bind_to_descriptor_v1 + (PSI_file_locker *locker, File file) +{ + PFS_file *pfs_file= NULL; + int index= (int) file; + PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); + assert(state != NULL); + + if (index >= 0) + { + PFS_file_class *klass= reinterpret_cast<PFS_file_class*> (state->m_class); + PFS_thread *thread= reinterpret_cast<PFS_thread*> (state->m_thread); + const char *name= state->m_name; + uint len= (uint)strlen(name); + pfs_file= find_or_create_file(thread, klass, name, len, true); + state->m_file= reinterpret_cast<PSI_file*> (pfs_file); + } + + pfs_end_file_wait_v1(locker, 0); + + if (likely(index >= 0)) + { + if (likely(index < file_handle_max)) + file_handle_array[index]= pfs_file; + else + { + if (pfs_file != NULL) + release_file(pfs_file); + file_handle_lost++; + } + } +} + +/** + Implementation of the file instrumentation interface. + @sa PSI_v1::end_temp_file_open_wait_and_bind_to_descriptor. +*/ +void pfs_end_temp_file_open_wait_and_bind_to_descriptor_v1 + (PSI_file_locker *locker, File file, const char *filename) +{ + assert(filename != NULL); + PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); + assert(state != NULL); + + /* Set filename that was generated during creation of temporary file. */ + state->m_name= filename; + pfs_end_file_open_wait_and_bind_to_descriptor_v1(locker, file); + + PFS_file *pfs_file= reinterpret_cast<PFS_file *> (state->m_file); + if (pfs_file != NULL) + { + pfs_file->m_temporary= true; + } +} + + +/** + Implementation of the file instrumentation interface. + @sa PSI_v1::start_file_wait. +*/ +void pfs_start_file_wait_v1(PSI_file_locker *locker, + size_t count, + const char *src_file, + uint src_line) +{ + ulonglong timer_start= 0; + PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); + assert(state != NULL); + + uint flags= state->m_flags; + + if (flags & STATE_FLAG_TIMED) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + } + + if (flags & STATE_FLAG_EVENT) + { + PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); + assert(wait != NULL); + + wait->m_timer_start= timer_start; + wait->m_source_file= src_file; + wait->m_source_line= src_line; + wait->m_number_of_bytes= count; + } +} + +/** + Implementation of the file instrumentation interface. + @sa PSI_v1::end_file_wait. +*/ +void pfs_end_file_wait_v1(PSI_file_locker *locker, + size_t byte_count) +{ + PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); + assert(state != NULL); + PFS_file *file= reinterpret_cast<PFS_file *> (state->m_file); + PFS_file_class *klass= reinterpret_cast<PFS_file_class *> (state->m_class); + PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + PFS_byte_stat *byte_stat; + uint flags= state->m_flags; + size_t bytes= ((int)byte_count > -1 ? byte_count : 0); + + PFS_file_stat *file_stat; + + if (file != NULL) + { + file_stat= & file->m_file_stat; + } + else + { + file_stat= & klass->m_file_stat; + } + + switch (state->m_operation) + { + /* Group read operations */ + case PSI_FILE_READ: + byte_stat= &file_stat->m_io_stat.m_read; + break; + /* Group write operations */ + case PSI_FILE_WRITE: + byte_stat= &file_stat->m_io_stat.m_write; + break; + /* Group remaining operations as miscellaneous */ + case PSI_FILE_CREATE: + case PSI_FILE_CREATE_TMP: + case PSI_FILE_OPEN: + case PSI_FILE_STREAM_OPEN: + case PSI_FILE_STREAM_CLOSE: + case PSI_FILE_SEEK: + case PSI_FILE_TELL: + case PSI_FILE_FLUSH: + case PSI_FILE_FSTAT: + case PSI_FILE_CHSIZE: + case PSI_FILE_DELETE: + case PSI_FILE_RENAME: + case PSI_FILE_SYNC: + case PSI_FILE_STAT: + case PSI_FILE_CLOSE: + byte_stat= &file_stat->m_io_stat.m_misc; + break; + default: + assert(false); + byte_stat= NULL; + break; + } + + /* Aggregation for EVENTS_WAITS_SUMMARY_BY_INSTANCE */ + if (flags & STATE_FLAG_TIMED) + { + timer_end= state->m_timer(); + wait_time= timer_end - state->m_timer_start; + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */ + byte_stat->aggregate(wait_time, bytes); + } + else + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ + byte_stat->aggregate_counted(bytes); + } + + if (flags & STATE_FLAG_THREAD) + { + assert(thread != NULL); + + PFS_single_stat *event_name_array; + event_name_array= thread->write_instr_class_waits_stats(); + uint index= klass->m_event_name_index; + + if (flags & STATE_FLAG_TIMED) + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ + event_name_array[index].aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ + event_name_array[index].aggregate_counted(); + } + + if (state->m_flags & STATE_FLAG_EVENT) + { + PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); + assert(wait != NULL); + + wait->m_timer_end= timer_end; + wait->m_number_of_bytes= bytes; + wait->m_end_event_id= thread->m_event_id; + wait->m_object_instance_addr= file; + wait->m_weak_file= file; + wait->m_weak_version= (file ? file->get_version() : 0); + + if (thread->m_flag_events_waits_history) + insert_events_waits_history(thread, wait); + if (thread->m_flag_events_waits_history_long) + insert_events_waits_history_long(wait); + thread->m_events_waits_current--; + + assert(wait == thread->m_events_waits_current); + } + } +} + +/** + Implementation of the file instrumentation interface. + @sa PSI_v1::start_file_close_wait. +*/ +void pfs_start_file_close_wait_v1(PSI_file_locker *locker, + const char *src_file, + uint src_line) +{ + PFS_thread *thread; + const char *name; + uint len; + PFS_file *pfs_file; + PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); + assert(state != NULL); + + switch (state->m_operation) + { + case PSI_FILE_DELETE: + thread= reinterpret_cast<PFS_thread*> (state->m_thread); + name= state->m_name; + len= (uint)strlen(name); + pfs_file= find_or_create_file(thread, NULL, name, len, false); + state->m_file= reinterpret_cast<PSI_file*> (pfs_file); + break; + case PSI_FILE_STREAM_CLOSE: + case PSI_FILE_CLOSE: + break; + default: + assert(false); + break; + } + + pfs_start_file_wait_v1(locker, 0, src_file, src_line); + + return; +} + +/** + Implementation of the file instrumentation interface. + @sa PSI_v1::end_file_close_wait. +*/ +void pfs_end_file_close_wait_v1(PSI_file_locker *locker, int rc) +{ + PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); + assert(state != NULL); + + pfs_end_file_wait_v1(locker, 0); + + if (rc == 0) + { + PFS_thread *thread= reinterpret_cast<PFS_thread*> (state->m_thread); + PFS_file *file= reinterpret_cast<PFS_file*> (state->m_file); + + /* Release or destroy the file if necessary */ + switch(state->m_operation) + { + case PSI_FILE_CLOSE: + if (file != NULL) + { + if (file->m_temporary) + { + assert(file->m_file_stat.m_open_count <= 1); + destroy_file(thread, file); + } + else + release_file(file); + } + break; + case PSI_FILE_STREAM_CLOSE: + if (file != NULL) + release_file(file); + break; + case PSI_FILE_DELETE: + if (file != NULL) + destroy_file(thread, file); + break; + default: + assert(false); + break; + } + } + return; +} + +/** + Implementation of the file instrumentation interface. + @sa PSI_v1::end_file_rename_wait. +*/ +void pfs_end_file_rename_wait_v1(PSI_file_locker *locker, const char *old_name, + const char *new_name, int rc) +{ + PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); + assert(state != NULL); + assert(state->m_operation == PSI_FILE_RENAME); + + if (rc == 0) + { + PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); + + uint old_len= (uint)strlen(old_name); + uint new_len= (uint)strlen(new_name); + + find_and_rename_file(thread, old_name, old_len, new_name, new_len); + } + + pfs_end_file_wait_v1(locker, 0); + return; +} + +PSI_stage_progress* +pfs_start_stage_v1(PSI_stage_key key, const char *src_file, int src_line) +{ + ulonglong timer_value= 0; + + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + + /* Always update column threads.processlist_state. */ + pfs_thread->m_stage= key; + /* Default value when the stage is not instrumented for progress */ + pfs_thread->m_stage_progress= NULL; + + if (psi_unlikely(! flag_global_instrumentation)) + return NULL; + + if (flag_thread_instrumentation && ! pfs_thread->m_enabled) + return NULL; + + PFS_events_stages *pfs= & pfs_thread->m_stage_current; + PFS_events_waits *child_wait= & pfs_thread->m_events_waits_stack[0]; + PFS_events_statements *parent_statement= & pfs_thread->m_statement_stack[0]; + + PFS_instr_class *old_class= pfs->m_class; + if (old_class != NULL) + { + PFS_stage_stat *event_name_array; + event_name_array= pfs_thread->write_instr_class_stages_stats(); + uint index= old_class->m_event_name_index; + + /* Finish old event */ + if (old_class->m_timed) + { + timer_value= get_timer_raw_value(stage_timer);; + pfs->m_timer_end= timer_value; + + /* Aggregate to EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ + ulonglong stage_time= timer_value - pfs->m_timer_start; + event_name_array[index].aggregate_value(stage_time); + } + else + { + /* Aggregate to EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ + event_name_array[index].aggregate_counted(); + } + + if (flag_events_stages_current) + { + pfs->m_end_event_id= pfs_thread->m_event_id; + if (pfs_thread->m_flag_events_stages_history) + insert_events_stages_history(pfs_thread, pfs); + if (pfs_thread->m_flag_events_stages_history_long) + insert_events_stages_history_long(pfs); + } + + /* This stage event is now complete. */ + pfs->m_class= NULL; + + /* New waits will now be attached directly to the parent statement. */ + child_wait->m_event_id= parent_statement->m_event.m_event_id; + child_wait->m_event_type= parent_statement->m_event.m_event_type; + /* See below for new stages, that may overwrite this. */ + } + + /* Start new event */ + + PFS_stage_class *new_klass= find_stage_class(key); + if (unlikely(new_klass == NULL)) + return NULL; + + if (! new_klass->m_enabled) + return NULL; + + pfs->m_class= new_klass; + if (new_klass->m_timed) + { + /* + Do not call the timer again if we have a + TIMER_END for the previous stage already. + */ + if (timer_value == 0) + timer_value= get_timer_raw_value(stage_timer); + pfs->m_timer_start= timer_value; + } + else + pfs->m_timer_start= 0; + pfs->m_timer_end= 0; + + if (flag_events_stages_current) + { + pfs->m_thread_internal_id= pfs_thread->m_thread_internal_id; + pfs->m_event_id= pfs_thread->m_event_id++; + pfs->m_end_event_id= 0; + pfs->m_source_file= src_file; + pfs->m_source_line= src_line; + + /* New wait events will have this new stage as parent. */ + child_wait->m_event_id= pfs->m_event_id; + child_wait->m_event_type= EVENT_TYPE_STAGE; + } + + if (new_klass->is_progress()) + { + pfs_thread->m_stage_progress= & pfs->m_progress; + pfs->m_progress.m_work_completed= 0; + pfs->m_progress.m_work_estimated= 0; + } + + return pfs_thread->m_stage_progress; +} + +PSI_stage_progress* +pfs_get_current_stage_progress_v1(void) +{ + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + + return pfs_thread->m_stage_progress; +} + +void pfs_end_stage_v1() +{ + ulonglong timer_value= 0; + + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return; + + pfs_thread->m_stage= 0; + pfs_thread->m_stage_progress= NULL; + + if (psi_unlikely(! flag_global_instrumentation)) + return; + + if (flag_thread_instrumentation && ! pfs_thread->m_enabled) + return; + + PFS_events_stages *pfs= & pfs_thread->m_stage_current; + + PFS_instr_class *old_class= pfs->m_class; + if (old_class != NULL) + { + PFS_stage_stat *event_name_array; + event_name_array= pfs_thread->write_instr_class_stages_stats(); + uint index= old_class->m_event_name_index; + + /* Finish old event */ + if (old_class->m_timed) + { + timer_value= get_timer_raw_value(stage_timer);; + pfs->m_timer_end= timer_value; + + /* Aggregate to EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ + ulonglong stage_time= timer_value - pfs->m_timer_start; + event_name_array[index].aggregate_value(stage_time); + } + else + { + /* Aggregate to EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ + event_name_array[index].aggregate_counted(); + } + + if (flag_events_stages_current) + { + pfs->m_end_event_id= pfs_thread->m_event_id; + if (pfs_thread->m_flag_events_stages_history) + insert_events_stages_history(pfs_thread, pfs); + if (pfs_thread->m_flag_events_stages_history_long) + insert_events_stages_history_long(pfs); + } + + /* New waits will now be attached directly to the parent statement. */ + PFS_events_waits *child_wait= & pfs_thread->m_events_waits_stack[0]; + PFS_events_statements *parent_statement= & pfs_thread->m_statement_stack[0]; + child_wait->m_event_id= parent_statement->m_event.m_event_id; + child_wait->m_event_type= parent_statement->m_event.m_event_type; + + /* This stage is completed */ + pfs->m_class= NULL; + } +} + +PSI_statement_locker* +pfs_get_thread_statement_locker_v1(PSI_statement_locker_state *state, + PSI_statement_key key, + const void *charset, PSI_sp_share *sp_share) +{ + DBUG_ASSERT(state != NULL); + DBUG_ASSERT(charset != NULL); + + if (psi_unlikely(! flag_global_instrumentation)) + return NULL; + PFS_statement_class *klass= find_statement_class(key); + if (unlikely(klass == NULL)) + return NULL; + if (! klass->m_enabled) + return NULL; + + uint flags; + + if (flag_thread_instrumentation) + { + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); + flags= STATE_FLAG_THREAD; + + if (klass->m_timed) + flags|= STATE_FLAG_TIMED; + + if (flag_events_statements_current) + { + ulonglong event_id= pfs_thread->m_event_id++; + + if (pfs_thread->m_events_statements_count >= statement_stack_max) + { + nested_statement_lost++; + return NULL; + } + + pfs_dirty_state dirty_state; + pfs_thread->m_stmt_lock.allocated_to_dirty(& dirty_state); + PFS_events_statements *pfs= & pfs_thread->m_statement_stack[pfs_thread->m_events_statements_count]; + pfs->m_event.m_thread_internal_id= pfs_thread->m_thread_internal_id; + pfs->m_event.m_event_id= event_id; + pfs->m_event.m_event_type= EVENT_TYPE_STATEMENT; + pfs->m_event.m_end_event_id= 0; + pfs->m_event.m_class= klass; + pfs->m_event.m_timer_start= 0; + pfs->m_event.m_timer_end= 0; + pfs->m_lock_time= 0; + pfs->m_current_schema_name_length= 0; + pfs->m_sqltext_length= 0; + pfs->m_sqltext_truncated= false; + pfs->m_sqltext_cs_number= system_charset_info->number; /* default */ + + pfs->m_message_text[0]= '\0'; + pfs->m_sql_errno= 0; + pfs->m_sqlstate[0]= '\0'; + pfs->m_error_count= 0; + pfs->m_warning_count= 0; + pfs->m_rows_affected= 0; + + pfs->m_rows_sent= 0; + pfs->m_rows_examined= 0; + pfs->m_created_tmp_disk_tables= 0; + pfs->m_created_tmp_tables= 0; + pfs->m_select_full_join= 0; + pfs->m_select_full_range_join= 0; + pfs->m_select_range= 0; + pfs->m_select_range_check= 0; + pfs->m_select_scan= 0; + pfs->m_sort_merge_passes= 0; + pfs->m_sort_range= 0; + pfs->m_sort_rows= 0; + pfs->m_sort_scan= 0; + pfs->m_no_index_used= 0; + pfs->m_no_good_index_used= 0; + pfs->m_digest_storage.reset(); + + /* New stages will have this statement as parent */ + PFS_events_stages *child_stage= & pfs_thread->m_stage_current; + child_stage->m_nesting_event_id= event_id; + child_stage->m_nesting_event_type= EVENT_TYPE_STATEMENT; + + /* New waits will have this statement as parent, if no stage is instrumented */ + PFS_events_waits *child_wait= & pfs_thread->m_events_waits_stack[0]; + child_wait->m_event_id= event_id; + child_wait->m_event_type= EVENT_TYPE_STATEMENT; + + PFS_events_statements *parent_statement= NULL; + PFS_events_transactions *parent_transaction= &pfs_thread->m_transaction_current; + ulonglong parent_event= 0; + enum_event_type parent_type= EVENT_TYPE_STATEMENT; + uint parent_level= 0; + + if (pfs_thread->m_events_statements_count > 0) + { + parent_statement= pfs - 1; + parent_event= parent_statement->m_event.m_event_id; + parent_type= parent_statement->m_event.m_event_type; + parent_level= parent_statement->m_event.m_nesting_event_level + 1; + } + + if (parent_transaction->m_state == TRANS_STATE_ACTIVE && + parent_transaction->m_event_id > parent_event) + { + parent_event= parent_transaction->m_event_id; + parent_type= parent_transaction->m_event_type; + } + + pfs->m_event.m_nesting_event_id= parent_event; + pfs->m_event.m_nesting_event_type= parent_type; + pfs->m_event.m_nesting_event_level= parent_level; + + /* Set parent Stored Procedure information for this statement. */ + if(sp_share) + { + PFS_program *parent_sp= reinterpret_cast<PFS_program*>(sp_share); + pfs->m_sp_type= parent_sp->m_type; + memcpy(pfs->m_schema_name, parent_sp->m_schema_name, + parent_sp->m_schema_name_length); + pfs->m_schema_name_length= parent_sp->m_schema_name_length; + memcpy(pfs->m_object_name, parent_sp->m_object_name, + parent_sp->m_object_name_length); + pfs->m_object_name_length= parent_sp->m_object_name_length; + } + else + { + pfs->m_sp_type= NO_OBJECT_TYPE; + pfs->m_schema_name_length= 0; + pfs->m_object_name_length= 0; + } + + state->m_statement= pfs; + flags|= STATE_FLAG_EVENT; + + pfs_thread->m_events_statements_count++; + pfs_thread->m_stmt_lock.dirty_to_allocated(& dirty_state); + } + else + { + state->m_statement= NULL; + } + } + else + { + state->m_statement= NULL; + + if (klass->m_timed) + flags= STATE_FLAG_TIMED; + else + flags= 0; + } + + if (flag_statements_digest) + { + flags|= STATE_FLAG_DIGEST; + } + + state->m_discarded= false; + state->m_class= klass; + state->m_flags= flags; + + state->m_lock_time= 0; + state->m_rows_sent= 0; + state->m_rows_examined= 0; + state->m_created_tmp_disk_tables= 0; + state->m_created_tmp_tables= 0; + state->m_select_full_join= 0; + state->m_select_full_range_join= 0; + state->m_select_range= 0; + state->m_select_range_check= 0; + state->m_select_scan= 0; + state->m_sort_merge_passes= 0; + state->m_sort_range= 0; + state->m_sort_rows= 0; + state->m_sort_scan= 0; + state->m_no_index_used= 0; + state->m_no_good_index_used= 0; + + state->m_digest= NULL; + state->m_cs_number= ((CHARSET_INFO *)charset)->number; + + state->m_schema_name_length= 0; + state->m_parent_sp_share= sp_share; + state->m_parent_prepared_stmt= NULL; + + return reinterpret_cast<PSI_statement_locker*> (state); +} + +PSI_statement_locker* +pfs_refine_statement_v1(PSI_statement_locker *locker, + PSI_statement_key key) +{ + PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker); + if (state == NULL) + return NULL; + assert(state->m_class != NULL); + PFS_statement_class *klass; + /* Only refine statements for mutable instrumentation */ + klass= reinterpret_cast<PFS_statement_class*> (state->m_class); + assert(klass->is_mutable()); + klass= find_statement_class(key); + + uint flags= state->m_flags; + + if (unlikely(klass == NULL) || !klass->m_enabled) + { + /* pop statement stack */ + if (flags & STATE_FLAG_THREAD) + { + PFS_thread *pfs_thread= reinterpret_cast<PFS_thread *> (state->m_thread); + assert(pfs_thread != NULL); + if (pfs_thread->m_events_statements_count > 0) + pfs_thread->m_events_statements_count--; + } + + state->m_discarded= true; + return NULL; + } + + if ((flags & STATE_FLAG_TIMED) && ! klass->m_timed) + flags= flags & ~STATE_FLAG_TIMED; + + if (flags & STATE_FLAG_EVENT) + { + PFS_events_statements *pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement); + assert(pfs != NULL); + + /* mutate EVENTS_STATEMENTS_CURRENT.EVENT_NAME */ + pfs->m_event.m_class= klass; + } + + state->m_class= klass; + state->m_flags= flags; + return reinterpret_cast<PSI_statement_locker*> (state); +} + +void pfs_start_statement_v1(PSI_statement_locker *locker, + const char *db, uint db_len, + const char *src_file, uint src_line) +{ + PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker); + assert(state != NULL); + + uint flags= state->m_flags; + ulonglong timer_start= 0; + + if (flags & STATE_FLAG_TIMED) + { + timer_start= get_timer_raw_value_and_function(statement_timer, & state->m_timer); + state->m_timer_start= timer_start; + } + + compile_time_assert(PSI_SCHEMA_NAME_LEN == NAME_LEN); + assert(db_len <= sizeof(state->m_schema_name)); + + if (db_len > 0) + memcpy(state->m_schema_name, db, db_len); + state->m_schema_name_length= db_len; + + if (flags & STATE_FLAG_EVENT) + { + PFS_events_statements *pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement); + assert(pfs != NULL); + + pfs->m_event.m_timer_start= timer_start; + pfs->m_event.m_source_file= src_file; + pfs->m_event.m_source_line= src_line; + + assert(db_len <= sizeof(pfs->m_current_schema_name)); + if (db_len > 0) + memcpy(pfs->m_current_schema_name, db, db_len); + pfs->m_current_schema_name_length= db_len; + } +} + +void pfs_set_statement_text_v1(PSI_statement_locker *locker, + const char *text, uint text_len) +{ + PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker); + assert(state != NULL); + + if (state->m_discarded) + return; + + if (state->m_flags & STATE_FLAG_EVENT) + { + PFS_events_statements *pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement); + assert(pfs != NULL); + if (text_len > pfs_max_sqltext) + { + text_len= (uint)pfs_max_sqltext; + pfs->m_sqltext_truncated= true; + } + if (text_len) + memcpy(pfs->m_sqltext, text, text_len); + pfs->m_sqltext_length= text_len; + pfs->m_sqltext_cs_number= state->m_cs_number; + } + + return; +} + +#define SET_STATEMENT_ATTR_BODY(LOCKER, ATTR, VALUE) \ + PSI_statement_locker_state *state; \ + state= reinterpret_cast<PSI_statement_locker_state*> (LOCKER); \ + if (unlikely(state == NULL)) \ + return; \ + if (state->m_discarded) \ + return; \ + state->ATTR= VALUE; \ + if (state->m_flags & STATE_FLAG_EVENT) \ + { \ + PFS_events_statements *pfs; \ + pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement); \ + assert(pfs != NULL); \ + pfs->ATTR= VALUE; \ + } \ + return; + +#define INC_STATEMENT_ATTR_BODY(LOCKER, ATTR, VALUE) \ + PSI_statement_locker_state *state; \ + state= reinterpret_cast<PSI_statement_locker_state*> (LOCKER); \ + if (unlikely(state == NULL)) \ + return; \ + if (state->m_discarded) \ + return; \ + state->ATTR+= VALUE; \ + if (state->m_flags & STATE_FLAG_EVENT) \ + { \ + PFS_events_statements *pfs; \ + pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement); \ + assert(pfs != NULL); \ + pfs->ATTR+= VALUE; \ + } \ + return; + +void pfs_set_statement_lock_time_v1(PSI_statement_locker *locker, + ulonglong count) +{ + SET_STATEMENT_ATTR_BODY(locker, m_lock_time, count); +} + +void pfs_set_statement_rows_sent_v1(PSI_statement_locker *locker, + ulonglong count) +{ + SET_STATEMENT_ATTR_BODY(locker, m_rows_sent, count); +} + +void pfs_set_statement_rows_examined_v1(PSI_statement_locker *locker, + ulonglong count) +{ + SET_STATEMENT_ATTR_BODY(locker, m_rows_examined, count); +} + +void pfs_inc_statement_created_tmp_disk_tables_v1(PSI_statement_locker *locker, + ulong count) +{ + INC_STATEMENT_ATTR_BODY(locker, m_created_tmp_disk_tables, count); +} + +void pfs_inc_statement_created_tmp_tables_v1(PSI_statement_locker *locker, + ulong count) +{ + INC_STATEMENT_ATTR_BODY(locker, m_created_tmp_tables, count); +} + +void pfs_inc_statement_select_full_join_v1(PSI_statement_locker *locker, + ulong count) +{ + INC_STATEMENT_ATTR_BODY(locker, m_select_full_join, count); +} + +void pfs_inc_statement_select_full_range_join_v1(PSI_statement_locker *locker, + ulong count) +{ + INC_STATEMENT_ATTR_BODY(locker, m_select_full_range_join, count); +} + +void pfs_inc_statement_select_range_v1(PSI_statement_locker *locker, + ulong count) +{ + INC_STATEMENT_ATTR_BODY(locker, m_select_range, count); +} + +void pfs_inc_statement_select_range_check_v1(PSI_statement_locker *locker, + ulong count) +{ + INC_STATEMENT_ATTR_BODY(locker, m_select_range_check, count); +} + +void pfs_inc_statement_select_scan_v1(PSI_statement_locker *locker, + ulong count) +{ + INC_STATEMENT_ATTR_BODY(locker, m_select_scan, count); +} + +void pfs_inc_statement_sort_merge_passes_v1(PSI_statement_locker *locker, + ulong count) +{ + INC_STATEMENT_ATTR_BODY(locker, m_sort_merge_passes, count); +} + +void pfs_inc_statement_sort_range_v1(PSI_statement_locker *locker, + ulong count) +{ + INC_STATEMENT_ATTR_BODY(locker, m_sort_range, count); +} + +void pfs_inc_statement_sort_rows_v1(PSI_statement_locker *locker, + ulong count) +{ + INC_STATEMENT_ATTR_BODY(locker, m_sort_rows, count); +} + +void pfs_inc_statement_sort_scan_v1(PSI_statement_locker *locker, + ulong count) +{ + INC_STATEMENT_ATTR_BODY(locker, m_sort_scan, count); +} + +void pfs_set_statement_no_index_used_v1(PSI_statement_locker *locker) +{ + SET_STATEMENT_ATTR_BODY(locker, m_no_index_used, 1); +} + +void pfs_set_statement_no_good_index_used_v1(PSI_statement_locker *locker) +{ + SET_STATEMENT_ATTR_BODY(locker, m_no_good_index_used, 1); +} + +void pfs_end_statement_v1(PSI_statement_locker *locker, void *stmt_da) +{ + PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker); + Diagnostics_area *da= reinterpret_cast<Diagnostics_area*> (stmt_da); + assert(state != NULL); + assert(da != NULL); + + if (state->m_discarded) + return; + + PFS_statement_class *klass= reinterpret_cast<PFS_statement_class *> (state->m_class); + assert(klass != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + uint flags= state->m_flags; + + if (flags & STATE_FLAG_TIMED) + { + timer_end= state->m_timer(); + wait_time= timer_end - state->m_timer_start; + } + + PFS_statement_stat *event_name_array; + uint index= klass->m_event_name_index; + PFS_statement_stat *stat; + + /* + Capture statement stats by digest. + */ + const sql_digest_storage *digest_storage= NULL; + PFS_statement_stat *digest_stat= NULL; + PFS_program *pfs_program= NULL; + PFS_prepared_stmt *pfs_prepared_stmt= NULL; + + if (flags & STATE_FLAG_THREAD) + { + PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); + assert(thread != NULL); + event_name_array= thread->write_instr_class_statements_stats(); + /* Aggregate to EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME */ + stat= & event_name_array[index]; + + if (flags & STATE_FLAG_DIGEST) + { + digest_storage= state->m_digest; + + if (digest_storage != NULL) + { + /* Populate PFS_statements_digest_stat with computed digest information.*/ + digest_stat= find_or_create_digest(thread, digest_storage, + state->m_schema_name, + state->m_schema_name_length); + } + } + + if (flags & STATE_FLAG_EVENT) + { + PFS_events_statements *pfs= reinterpret_cast<PFS_events_statements*> (state->m_statement); + assert(pfs != NULL); + + pfs_dirty_state dirty_state; + thread->m_stmt_lock.allocated_to_dirty(& dirty_state); + + switch(da->status()) + { + case Diagnostics_area::DA_OK_BULK: + case Diagnostics_area::DA_EOF_BULK: + case Diagnostics_area::DA_EMPTY: + break; + case Diagnostics_area::DA_OK: + memcpy(pfs->m_message_text, da->message(), + MYSQL_ERRMSG_SIZE); + pfs->m_message_text[MYSQL_ERRMSG_SIZE]= 0; + pfs->m_rows_affected= da->affected_rows(); + pfs->m_warning_count= da->statement_warn_count(); + memcpy(pfs->m_sqlstate, "00000", SQLSTATE_LENGTH); + break; + case Diagnostics_area::DA_EOF: + pfs->m_warning_count= da->statement_warn_count(); + break; + case Diagnostics_area::DA_ERROR: + memcpy(pfs->m_message_text, da->message(), + MYSQL_ERRMSG_SIZE); + pfs->m_message_text[MYSQL_ERRMSG_SIZE]= 0; + pfs->m_sql_errno= da->sql_errno(); + memcpy(pfs->m_sqlstate, da->get_sqlstate(), SQLSTATE_LENGTH); + pfs->m_error_count++; + break; + case Diagnostics_area::DA_DISABLED: + break; + } + + pfs->m_event.m_timer_end= timer_end; + pfs->m_event.m_end_event_id= thread->m_event_id; + + if (digest_storage != NULL) + { + /* + The following columns in events_statement_current: + - DIGEST, + - DIGEST_TEXT + are computed from the digest storage. + */ + pfs->m_digest_storage.copy(digest_storage); + } + + pfs_program= reinterpret_cast<PFS_program*>(state->m_parent_sp_share); + pfs_prepared_stmt= reinterpret_cast<PFS_prepared_stmt*>(state->m_parent_prepared_stmt); + + if (thread->m_flag_events_statements_history) + insert_events_statements_history(thread, pfs); + if (thread->m_flag_events_statements_history_long) + insert_events_statements_history_long(pfs); + + assert(thread->m_events_statements_count > 0); + thread->m_events_statements_count--; + thread->m_stmt_lock.dirty_to_allocated(& dirty_state); + } + } + else + { + if (flags & STATE_FLAG_DIGEST) + { + PFS_thread *thread= my_thread_get_THR_PFS(); + + /* An instrumented thread is required, for LF_PINS. */ + if (thread != NULL) + { + /* Set digest stat. */ + digest_storage= state->m_digest; + + if (digest_storage != NULL) + { + /* Populate statements_digest_stat with computed digest information. */ + digest_stat= find_or_create_digest(thread, digest_storage, + state->m_schema_name, + state->m_schema_name_length); + } + } + } + + event_name_array= global_instr_class_statements_array; + /* Aggregate to EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME */ + stat= & event_name_array[index]; + } + + stat->mark_used(); + + if (flags & STATE_FLAG_TIMED) + { + /* Aggregate to EVENTS_STATEMENTS_SUMMARY_..._BY_EVENT_NAME (timed) */ + stat->aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_STATEMENTS_SUMMARY_..._BY_EVENT_NAME (counted) */ + stat->aggregate_counted(); + } + + stat->m_lock_time+= state->m_lock_time; + stat->m_rows_sent+= state->m_rows_sent; + stat->m_rows_examined+= state->m_rows_examined; + stat->m_created_tmp_disk_tables+= state->m_created_tmp_disk_tables; + stat->m_created_tmp_tables+= state->m_created_tmp_tables; + stat->m_select_full_join+= state->m_select_full_join; + stat->m_select_full_range_join+= state->m_select_full_range_join; + stat->m_select_range+= state->m_select_range; + stat->m_select_range_check+= state->m_select_range_check; + stat->m_select_scan+= state->m_select_scan; + stat->m_sort_merge_passes+= state->m_sort_merge_passes; + stat->m_sort_range+= state->m_sort_range; + stat->m_sort_rows+= state->m_sort_rows; + stat->m_sort_scan+= state->m_sort_scan; + stat->m_no_index_used+= state->m_no_index_used; + stat->m_no_good_index_used+= state->m_no_good_index_used; + + if (digest_stat != NULL) + { + digest_stat->mark_used(); + + if (flags & STATE_FLAG_TIMED) + { + digest_stat->aggregate_value(wait_time); + } + else + { + digest_stat->aggregate_counted(); + } + + digest_stat->m_lock_time+= state->m_lock_time; + digest_stat->m_rows_sent+= state->m_rows_sent; + digest_stat->m_rows_examined+= state->m_rows_examined; + digest_stat->m_created_tmp_disk_tables+= state->m_created_tmp_disk_tables; + digest_stat->m_created_tmp_tables+= state->m_created_tmp_tables; + digest_stat->m_select_full_join+= state->m_select_full_join; + digest_stat->m_select_full_range_join+= state->m_select_full_range_join; + digest_stat->m_select_range+= state->m_select_range; + digest_stat->m_select_range_check+= state->m_select_range_check; + digest_stat->m_select_scan+= state->m_select_scan; + digest_stat->m_sort_merge_passes+= state->m_sort_merge_passes; + digest_stat->m_sort_range+= state->m_sort_range; + digest_stat->m_sort_rows+= state->m_sort_rows; + digest_stat->m_sort_scan+= state->m_sort_scan; + digest_stat->m_no_index_used+= state->m_no_index_used; + digest_stat->m_no_good_index_used+= state->m_no_good_index_used; + } + + if(pfs_program != NULL) + { + PFS_statement_stat *sub_stmt_stat= NULL; + sub_stmt_stat= &pfs_program->m_stmt_stat; + if(sub_stmt_stat != NULL) + { + sub_stmt_stat->mark_used(); + + if (flags & STATE_FLAG_TIMED) + { + sub_stmt_stat->aggregate_value(wait_time); + } + else + { + sub_stmt_stat->aggregate_counted(); + } + + sub_stmt_stat->m_lock_time+= state->m_lock_time; + sub_stmt_stat->m_rows_sent+= state->m_rows_sent; + sub_stmt_stat->m_rows_examined+= state->m_rows_examined; + sub_stmt_stat->m_created_tmp_disk_tables+= state->m_created_tmp_disk_tables; + sub_stmt_stat->m_created_tmp_tables+= state->m_created_tmp_tables; + sub_stmt_stat->m_select_full_join+= state->m_select_full_join; + sub_stmt_stat->m_select_full_range_join+= state->m_select_full_range_join; + sub_stmt_stat->m_select_range+= state->m_select_range; + sub_stmt_stat->m_select_range_check+= state->m_select_range_check; + sub_stmt_stat->m_select_scan+= state->m_select_scan; + sub_stmt_stat->m_sort_merge_passes+= state->m_sort_merge_passes; + sub_stmt_stat->m_sort_range+= state->m_sort_range; + sub_stmt_stat->m_sort_rows+= state->m_sort_rows; + sub_stmt_stat->m_sort_scan+= state->m_sort_scan; + sub_stmt_stat->m_no_index_used+= state->m_no_index_used; + sub_stmt_stat->m_no_good_index_used+= state->m_no_good_index_used; + } + } + + if (pfs_prepared_stmt != NULL) + { + if(state->m_in_prepare) + { + PFS_single_stat *prepared_stmt_stat= NULL; + prepared_stmt_stat= &pfs_prepared_stmt->m_prepare_stat; + if(prepared_stmt_stat != NULL) + { + if (flags & STATE_FLAG_TIMED) + { + prepared_stmt_stat->aggregate_value(wait_time); + } + else + { + prepared_stmt_stat->aggregate_counted(); + } + } + } + else + { + PFS_statement_stat *prepared_stmt_stat= NULL; + prepared_stmt_stat= &pfs_prepared_stmt->m_execute_stat; + if(prepared_stmt_stat != NULL) + { + if (flags & STATE_FLAG_TIMED) + { + prepared_stmt_stat->aggregate_value(wait_time); + } + else + { + prepared_stmt_stat->aggregate_counted(); + } + + prepared_stmt_stat->m_lock_time+= state->m_lock_time; + prepared_stmt_stat->m_rows_sent+= state->m_rows_sent; + prepared_stmt_stat->m_rows_examined+= state->m_rows_examined; + prepared_stmt_stat->m_created_tmp_disk_tables+= state->m_created_tmp_disk_tables; + prepared_stmt_stat->m_created_tmp_tables+= state->m_created_tmp_tables; + prepared_stmt_stat->m_select_full_join+= state->m_select_full_join; + prepared_stmt_stat->m_select_full_range_join+= state->m_select_full_range_join; + prepared_stmt_stat->m_select_range+= state->m_select_range; + prepared_stmt_stat->m_select_range_check+= state->m_select_range_check; + prepared_stmt_stat->m_select_scan+= state->m_select_scan; + prepared_stmt_stat->m_sort_merge_passes+= state->m_sort_merge_passes; + prepared_stmt_stat->m_sort_range+= state->m_sort_range; + prepared_stmt_stat->m_sort_rows+= state->m_sort_rows; + prepared_stmt_stat->m_sort_scan+= state->m_sort_scan; + prepared_stmt_stat->m_no_index_used+= state->m_no_index_used; + prepared_stmt_stat->m_no_good_index_used+= state->m_no_good_index_used; + } + } + } + + PFS_statement_stat *sub_stmt_stat= NULL; + if (pfs_program != NULL) + sub_stmt_stat= &pfs_program->m_stmt_stat; + + PFS_statement_stat *prepared_stmt_stat= NULL; + if (pfs_prepared_stmt != NULL && !state->m_in_prepare) + prepared_stmt_stat= &pfs_prepared_stmt->m_execute_stat; + + switch (da->status()) + { + case Diagnostics_area::DA_OK_BULK: + case Diagnostics_area::DA_EOF_BULK: + case Diagnostics_area::DA_EMPTY: + break; + case Diagnostics_area::DA_OK: + stat->m_rows_affected+= da->affected_rows(); + stat->m_warning_count+= da->statement_warn_count(); + if (digest_stat != NULL) + { + digest_stat->m_rows_affected+= da->affected_rows(); + digest_stat->m_warning_count+= da->statement_warn_count(); + } + if(sub_stmt_stat != NULL) + { + sub_stmt_stat->m_rows_affected+= da->affected_rows(); + sub_stmt_stat->m_warning_count+= da->statement_warn_count(); + } + if (prepared_stmt_stat != NULL) + { + prepared_stmt_stat->m_rows_affected+= da->affected_rows(); + prepared_stmt_stat->m_warning_count+= da->statement_warn_count(); + } + break; + case Diagnostics_area::DA_EOF: + stat->m_warning_count+= da->statement_warn_count(); + if (digest_stat != NULL) + { + digest_stat->m_warning_count+= da->statement_warn_count(); + } + if(sub_stmt_stat != NULL) + { + sub_stmt_stat->m_warning_count+= da->statement_warn_count(); + } + if (prepared_stmt_stat != NULL) + { + prepared_stmt_stat->m_warning_count+= da->statement_warn_count(); + } + break; + case Diagnostics_area::DA_ERROR: + stat->m_error_count++; + if (digest_stat != NULL) + { + digest_stat->m_error_count++; + } + if (sub_stmt_stat != NULL) + { + sub_stmt_stat->m_error_count++; + } + if (prepared_stmt_stat != NULL) + { + prepared_stmt_stat->m_error_count++; + } + break; + case Diagnostics_area::DA_DISABLED: + break; + } +} + +static inline enum_object_type sp_type_to_object_type(uint sp_type) +{ + enum enum_sp_type value= static_cast<enum enum_sp_type> (sp_type); + + switch (value) + { + case SP_TYPE_FUNCTION: + return OBJECT_TYPE_FUNCTION; + case SP_TYPE_PROCEDURE: + return OBJECT_TYPE_PROCEDURE; + case SP_TYPE_PACKAGE: + return OBJECT_TYPE_PACKAGE; + case SP_TYPE_PACKAGE_BODY: + return OBJECT_TYPE_PACKAGE_BODY; + case SP_TYPE_TRIGGER: + return OBJECT_TYPE_TRIGGER; + case SP_TYPE_EVENT: + return OBJECT_TYPE_EVENT; + default: + assert(false); + /* Dead code */ + return NO_OBJECT_TYPE; + } +} + +/** + Implementation of the stored program instrumentation interface. + @sa PSI_v1::get_sp_share. +*/ +PSI_sp_share *pfs_get_sp_share_v1(uint sp_type, + const char* schema_name, + uint schema_name_length, + const char* object_name, + uint object_name_length) +{ + + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + + if (object_name_length > COL_OBJECT_NAME_SIZE) + object_name_length= COL_OBJECT_NAME_SIZE; + if (schema_name_length > COL_OBJECT_SCHEMA_SIZE) + schema_name_length= COL_OBJECT_SCHEMA_SIZE; + + PFS_program *pfs_program; + pfs_program= find_or_create_program(pfs_thread, + sp_type_to_object_type(sp_type), + object_name, + object_name_length, + schema_name, + schema_name_length); + + return reinterpret_cast<PSI_sp_share *>(pfs_program); +} + +void pfs_release_sp_share_v1(PSI_sp_share* sp_share) +{ + /* Unused */ + return; +} + +PSI_sp_locker* pfs_start_sp_v1(PSI_sp_locker_state *state, + PSI_sp_share *sp_share) +{ + assert(state != NULL); + if (! flag_global_instrumentation) + return NULL; + + if (flag_thread_instrumentation) + { + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + } + + /* + sp share might be null in case when stat array is full and no new + stored program stats are being inserted into it. + */ + PFS_program *pfs_program= reinterpret_cast<PFS_program*>(sp_share); + if (pfs_program == NULL || !pfs_program->m_enabled) + return NULL; + + state->m_flags= 0; + + if(pfs_program->m_timed) + { + state->m_flags|= STATE_FLAG_TIMED; + state->m_timer_start= get_timer_raw_value_and_function(statement_timer, + & state->m_timer); + } + + state->m_sp_share= sp_share; + + return reinterpret_cast<PSI_sp_locker*> (state); +} + +void pfs_end_sp_v1(PSI_sp_locker *locker) +{ + PSI_sp_locker_state *state= reinterpret_cast<PSI_sp_locker_state*> (locker); + assert(state != NULL); + + ulonglong timer_end; + ulonglong wait_time; + + PFS_program *pfs_program= reinterpret_cast<PFS_program *>(state->m_sp_share); + PFS_sp_stat *stat= &pfs_program->m_sp_stat; + + if (state->m_flags & STATE_FLAG_TIMED) + { + timer_end= state->m_timer(); + wait_time= timer_end - state->m_timer_start; + + /* Now use this timer_end and wait_time for timing information. */ + stat->aggregate_value(wait_time); + } + else + { + stat->aggregate_counted(); + } +} + +void pfs_drop_sp_v1(uint sp_type, + const char* schema_name, + uint schema_name_length, + const char* object_name, + uint object_name_length) +{ + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return; + + if (object_name_length > COL_OBJECT_NAME_SIZE) + object_name_length= COL_OBJECT_NAME_SIZE; + if (schema_name_length > COL_OBJECT_SCHEMA_SIZE) + schema_name_length= COL_OBJECT_SCHEMA_SIZE; + + drop_program(pfs_thread, + sp_type_to_object_type(sp_type), + object_name, object_name_length, + schema_name, schema_name_length); +} + +PSI_transaction_locker* +pfs_get_thread_transaction_locker_v1(PSI_transaction_locker_state *state, + const void *xid, + ulonglong trxid, + int isolation_level, + my_bool read_only, + my_bool autocommit) +{ + assert(state != NULL); + + if (!flag_global_instrumentation) + return NULL; + + if (!global_transaction_class.m_enabled) + return NULL; + + uint flags; + + if (flag_thread_instrumentation) + { + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (!pfs_thread->m_enabled) + return NULL; + state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); + flags= STATE_FLAG_THREAD; + + if (global_transaction_class.m_timed) + flags|= STATE_FLAG_TIMED; + + if (flag_events_transactions_current) + { + ulonglong event_id= pfs_thread->m_event_id++; + + PFS_events_transactions *pfs= &pfs_thread->m_transaction_current; + pfs->m_thread_internal_id = pfs_thread->m_thread_internal_id; + pfs->m_event_id= event_id; + pfs->m_event_type= EVENT_TYPE_TRANSACTION; + pfs->m_end_event_id= 0; + pfs->m_class= &global_transaction_class; + pfs->m_timer_start= 0; + pfs->m_timer_end= 0; + if (xid != NULL) + pfs->m_xid= *(PSI_xid *)xid; + pfs->m_xa= false; + pfs->m_xa_state= TRANS_STATE_XA_NOTR; + pfs->m_trxid= trxid; + pfs->m_isolation_level= (enum_isolation_level)isolation_level; + pfs->m_read_only= read_only; + pfs->m_autocommit= autocommit; + pfs->m_savepoint_count= 0; + pfs->m_rollback_to_savepoint_count= 0; + pfs->m_release_savepoint_count= 0; + + uint statements_count= pfs_thread->m_events_statements_count; + if (statements_count > 0) + { + PFS_events_statements *pfs_statement= + &pfs_thread->m_statement_stack[statements_count - 1]; + pfs->m_nesting_event_id= pfs_statement->m_event.m_event_id; + pfs->m_nesting_event_type= pfs_statement->m_event.m_event_type; + } + else + { + pfs->m_nesting_event_id= 0; + /* pfs->m_nesting_event_type not used when m_nesting_event_id is 0 */ + } + + state->m_transaction= pfs; + flags|= STATE_FLAG_EVENT; + } + } + else + { + if (global_transaction_class.m_timed) + flags= STATE_FLAG_TIMED; + else + flags= 0; + } + + state->m_class= &global_transaction_class; + state->m_flags= flags; + state->m_autocommit= autocommit; + state->m_read_only= read_only; + state->m_savepoint_count= 0; + state->m_rollback_to_savepoint_count= 0; + state->m_release_savepoint_count= 0; + + return reinterpret_cast<PSI_transaction_locker*> (state); +} + +void pfs_start_transaction_v1(PSI_transaction_locker *locker, + const char *src_file, uint src_line) +{ + PSI_transaction_locker_state *state= reinterpret_cast<PSI_transaction_locker_state*> (locker); + assert(state != NULL); + + uint flags= state->m_flags; + ulonglong timer_start= 0; + + if (flags & STATE_FLAG_TIMED) + { + timer_start= get_timer_raw_value_and_function(transaction_timer, &state->m_timer); + state->m_timer_start= timer_start; + } + + if (flags & STATE_FLAG_EVENT) + { + PFS_events_transactions *pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction); + assert(pfs != NULL); + + pfs->m_timer_start= timer_start; + pfs->m_source_file= src_file; + pfs->m_source_line= src_line; + pfs->m_state= TRANS_STATE_ACTIVE; + //pfs->m_sid.clear(); + bzero(&pfs->m_gtid_spec, sizeof(pfs->m_gtid_spec)); + } +} + +void pfs_set_transaction_gtid_v1(PSI_transaction_locker *locker, + const void *sid, + const void *gtid_spec) +{ + PSI_transaction_locker_state *state= reinterpret_cast<PSI_transaction_locker_state*> (locker); + assert(state != NULL); + assert(sid != NULL); + assert(gtid_spec != NULL); + + if (state->m_flags & STATE_FLAG_EVENT) + { + PFS_events_transactions *pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction); + DBUG_ASSERT(pfs != NULL); + //pfs->m_sid= *(rpl_sid *)sid; + pfs->m_gtid_spec= *(Gtid_specification*)gtid_spec; + } +} + +void pfs_set_transaction_xid_v1(PSI_transaction_locker *locker, + const void *xid, + int xa_state) +{ + PSI_transaction_locker_state *state= reinterpret_cast<PSI_transaction_locker_state*> (locker); + assert(state != NULL); + + if (state->m_flags & STATE_FLAG_EVENT) + { + PFS_events_transactions *pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction); + assert(pfs != NULL); + assert(xid != NULL); + + pfs->m_xid= *(PSI_xid *)xid; + pfs->m_xa_state= (enum_xa_transaction_state)xa_state; + pfs->m_xa= true; + } + return; +} + +void pfs_set_transaction_xa_state_v1(PSI_transaction_locker *locker, + int xa_state) +{ + PSI_transaction_locker_state *state= reinterpret_cast<PSI_transaction_locker_state*> (locker); + assert(state != NULL); + + if (state->m_flags & STATE_FLAG_EVENT) + { + PFS_events_transactions *pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction); + assert(pfs != NULL); + + pfs->m_xa_state= (enum_xa_transaction_state)xa_state; + pfs->m_xa= true; + } + return; +} + +void pfs_set_transaction_trxid_v1(PSI_transaction_locker *locker, + const ulonglong *trxid) +{ + assert(trxid != NULL); + + PSI_transaction_locker_state *state= reinterpret_cast<PSI_transaction_locker_state*> (locker); + assert(state != NULL); + + if (state->m_flags & STATE_FLAG_EVENT) + { + PFS_events_transactions *pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction); + assert(pfs != NULL); + + if (pfs->m_trxid == 0) + pfs->m_trxid= *trxid; + } +} + +#define INC_TRANSACTION_ATTR_BODY(LOCKER, ATTR, VALUE) \ + PSI_transaction_locker_state *state; \ + state= reinterpret_cast<PSI_transaction_locker_state*> (LOCKER); \ + if (unlikely(state == NULL)) \ + return; \ + state->ATTR+= VALUE; \ + if (state->m_flags & STATE_FLAG_EVENT) \ + { \ + PFS_events_transactions *pfs; \ + pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction); \ + assert(pfs != NULL); \ + pfs->ATTR+= VALUE; \ + } \ + return; + + +void pfs_inc_transaction_savepoints_v1(PSI_transaction_locker *locker, + ulong count) +{ + INC_TRANSACTION_ATTR_BODY(locker, m_savepoint_count, count); +} + +void pfs_inc_transaction_rollback_to_savepoint_v1(PSI_transaction_locker *locker, + ulong count) +{ + INC_TRANSACTION_ATTR_BODY(locker, m_rollback_to_savepoint_count, count); +} + +void pfs_inc_transaction_release_savepoint_v1(PSI_transaction_locker *locker, + ulong count) +{ + INC_TRANSACTION_ATTR_BODY(locker, m_release_savepoint_count, count); +} + +void pfs_end_transaction_v1(PSI_transaction_locker *locker, my_bool commit) +{ + PSI_transaction_locker_state *state= reinterpret_cast<PSI_transaction_locker_state*> (locker); + assert(state != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + uint flags= state->m_flags; + + if (flags & STATE_FLAG_TIMED) + { + timer_end= state->m_timer(); + wait_time= timer_end - state->m_timer_start; + } + + PFS_transaction_stat *stat; + + if (flags & STATE_FLAG_THREAD) + { + PFS_thread *pfs_thread= reinterpret_cast<PFS_thread *> (state->m_thread); + assert(pfs_thread != NULL); + + /* Aggregate to EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME */ + stat= &pfs_thread->write_instr_class_transactions_stats()[GLOBAL_TRANSACTION_INDEX]; + + if (flags & STATE_FLAG_EVENT) + { + PFS_events_transactions *pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction); + assert(pfs != NULL); + + /* events_transactions_current may have been cleared while the transaction was active */ + if (unlikely(pfs->m_class == NULL)) + return; + + pfs->m_timer_end= timer_end; + pfs->m_end_event_id= pfs_thread->m_event_id; + + pfs->m_state= (commit ? TRANS_STATE_COMMITTED : TRANS_STATE_ROLLED_BACK); + + if (pfs->m_xa) + pfs->m_xa_state= (commit ? TRANS_STATE_XA_COMMITTED : TRANS_STATE_XA_ROLLBACK_ONLY); + + if (pfs_thread->m_flag_events_transactions_history) + insert_events_transactions_history(pfs_thread, pfs); + if (pfs_thread->m_flag_events_transactions_history_long) + insert_events_transactions_history_long(pfs); + } + } + else + { + /* Aggregate to EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME */ + stat= &global_transaction_stat; + } + + if (flags & STATE_FLAG_TIMED) + { + /* Aggregate to EVENTS_TRANSACTIONS_SUMMARY_..._BY_EVENT_NAME (timed) */ + if(state->m_read_only) + stat->m_read_only_stat.aggregate_value(wait_time); + else + stat->m_read_write_stat.aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_TRANSACTIONS_SUMMARY_..._BY_EVENT_NAME (counted) */ + if(state->m_read_only) + stat->m_read_only_stat.aggregate_counted(); + else + stat->m_read_write_stat.aggregate_counted(); + } + + stat->m_savepoint_count+= state->m_savepoint_count; + stat->m_rollback_to_savepoint_count+= state->m_rollback_to_savepoint_count; + stat->m_release_savepoint_count+= state->m_release_savepoint_count; +} + + +/** + Implementation of the socket instrumentation interface. + @sa PSI_v1::end_socket_wait. +*/ +void pfs_end_socket_wait_v1(PSI_socket_locker *locker, size_t byte_count) +{ + PSI_socket_locker_state *state= reinterpret_cast<PSI_socket_locker_state*> (locker); + assert(state != NULL); + + PFS_socket *socket= reinterpret_cast<PFS_socket *>(state->m_socket); + assert(socket != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + PFS_byte_stat *byte_stat; + uint flags= state->m_flags; + size_t bytes= ((int)byte_count > -1 ? byte_count : 0); + + switch (state->m_operation) + { + /* Group read operations */ + case PSI_SOCKET_RECV: + case PSI_SOCKET_RECVFROM: + case PSI_SOCKET_RECVMSG: + byte_stat= &socket->m_socket_stat.m_io_stat.m_read; + break; + /* Group write operations */ + case PSI_SOCKET_SEND: + case PSI_SOCKET_SENDTO: + case PSI_SOCKET_SENDMSG: + byte_stat= &socket->m_socket_stat.m_io_stat.m_write; + break; + /* Group remaining operations as miscellaneous */ + case PSI_SOCKET_CONNECT: + case PSI_SOCKET_CREATE: + case PSI_SOCKET_BIND: + case PSI_SOCKET_SEEK: + case PSI_SOCKET_OPT: + case PSI_SOCKET_STAT: + case PSI_SOCKET_SHUTDOWN: + case PSI_SOCKET_SELECT: + case PSI_SOCKET_CLOSE: + byte_stat= &socket->m_socket_stat.m_io_stat.m_misc; + break; + default: + assert(false); + byte_stat= NULL; + break; + } + + /* Aggregation for EVENTS_WAITS_SUMMARY_BY_INSTANCE */ + if (flags & STATE_FLAG_TIMED) + { + timer_end= state->m_timer(); + wait_time= timer_end - state->m_timer_start; + + /* Aggregate to the socket instrument for now (timed) */ + byte_stat->aggregate(wait_time, bytes); + } + else + { + /* Aggregate to the socket instrument (event count and byte count) */ + byte_stat->aggregate_counted(bytes); + } + + /* Aggregate to EVENTS_WAITS_HISTORY and EVENTS_WAITS_HISTORY_LONG */ + if (flags & STATE_FLAG_EVENT) + { + PFS_thread *thread= reinterpret_cast<PFS_thread *>(state->m_thread); + assert(thread != NULL); + PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); + assert(wait != NULL); + + wait->m_timer_end= timer_end; + wait->m_end_event_id= thread->m_event_id; + wait->m_number_of_bytes= bytes; + + if (thread->m_flag_events_waits_history) + insert_events_waits_history(thread, wait); + if (thread->m_flag_events_waits_history_long) + insert_events_waits_history_long(wait); + thread->m_events_waits_current--; + + assert(wait == thread->m_events_waits_current); + } +} + +void pfs_set_socket_state_v1(PSI_socket *socket, PSI_socket_state state) +{ + assert((state == PSI_SOCKET_STATE_IDLE) || (state == PSI_SOCKET_STATE_ACTIVE)); + PFS_socket *pfs= reinterpret_cast<PFS_socket*>(socket); + assert(pfs != NULL); + assert(pfs->m_idle || (state == PSI_SOCKET_STATE_IDLE)); + assert(!pfs->m_idle || (state == PSI_SOCKET_STATE_ACTIVE)); + pfs->m_idle= (state == PSI_SOCKET_STATE_IDLE); +} + +/** + Set socket descriptor and address info. +*/ +void pfs_set_socket_info_v1(PSI_socket *socket, + const my_socket *fd, + const struct sockaddr *addr, + socklen_t addr_len) +{ + PFS_socket *pfs= reinterpret_cast<PFS_socket*>(socket); + assert(pfs != NULL); + + /** Set socket descriptor */ + if (fd != NULL) + pfs->m_fd= (uint)*fd; + + /** Set raw socket address and length */ + if (likely(addr != NULL && addr_len > 0)) + { + pfs->m_addr_len= addr_len; + + /** Restrict address length to size of struct */ + if (unlikely(pfs->m_addr_len > sizeof(sockaddr_storage))) + pfs->m_addr_len= sizeof(struct sockaddr_storage); + + memcpy(&pfs->m_sock_addr, addr, pfs->m_addr_len); + } +} + +/** + Implementation of the socket instrumentation interface. + @sa PSI_v1::set_socket_info. +*/ +void pfs_set_socket_thread_owner_v1(PSI_socket *socket) +{ + PFS_socket *pfs_socket= reinterpret_cast<PFS_socket*>(socket); + assert(pfs_socket != NULL); + pfs_socket->m_thread_owner= my_thread_get_THR_PFS(); +} + +struct PSI_digest_locker* +pfs_digest_start_v1(PSI_statement_locker *locker) +{ + PSI_statement_locker_state *statement_state; + statement_state= reinterpret_cast<PSI_statement_locker_state*> (locker); + assert(statement_state != NULL); + + if (statement_state->m_discarded) + return NULL; + + if (statement_state->m_flags & STATE_FLAG_DIGEST) + { + return reinterpret_cast<PSI_digest_locker*> (locker); + } + + return NULL; +} + +void pfs_digest_end_v1(PSI_digest_locker *locker, const sql_digest_storage *digest) +{ + PSI_statement_locker_state *statement_state; + statement_state= reinterpret_cast<PSI_statement_locker_state*> (locker); + assert(statement_state != NULL); + assert(digest != NULL); + + if (statement_state->m_discarded) + return; + + if (statement_state->m_flags & STATE_FLAG_DIGEST) + { + statement_state->m_digest= digest; + } +} + +PSI_prepared_stmt* +pfs_create_prepared_stmt_v1(void *identity, uint stmt_id, + PSI_statement_locker *locker, + const char *stmt_name, size_t stmt_name_length) +{ + PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker); + PFS_events_statements *pfs_stmt= reinterpret_cast<PFS_events_statements*> (state->m_statement); + PFS_program *pfs_program= reinterpret_cast<PFS_program *>(state->m_parent_sp_share); + + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + + PFS_prepared_stmt *pfs= create_prepared_stmt(identity, + pfs_thread, pfs_program, + pfs_stmt, stmt_id, + stmt_name, static_cast<uint>(stmt_name_length)); + + state->m_parent_prepared_stmt= reinterpret_cast<PSI_prepared_stmt*>(pfs); + state->m_in_prepare= true; + + return reinterpret_cast<PSI_prepared_stmt*>(pfs); +} + +void pfs_execute_prepared_stmt_v1 (PSI_statement_locker *locker, + PSI_prepared_stmt* ps) +{ + PSI_statement_locker_state *state= reinterpret_cast<PSI_statement_locker_state*> (locker); + assert(state != NULL); + + state->m_parent_prepared_stmt= ps; + state->m_in_prepare= false; +} + +void pfs_destroy_prepared_stmt_v1(PSI_prepared_stmt* prepared_stmt) +{ + PFS_prepared_stmt *pfs_prepared_stmt= reinterpret_cast<PFS_prepared_stmt*>(prepared_stmt); + delete_prepared_stmt(pfs_prepared_stmt); + return; +} + +void pfs_reprepare_prepared_stmt_v1(PSI_prepared_stmt* prepared_stmt) +{ + PFS_prepared_stmt *pfs_prepared_stmt= reinterpret_cast<PFS_prepared_stmt*>(prepared_stmt); + PFS_single_stat *prepared_stmt_stat= &pfs_prepared_stmt->m_reprepare_stat; + + if (prepared_stmt_stat != NULL) + prepared_stmt_stat->aggregate_counted(); + return; +} + +/** + Implementation of the thread attribute connection interface + @sa PSI_v1::set_thread_connect_attr. +*/ +int pfs_set_thread_connect_attrs_v1(const char *buffer, uint length, + const void *from_cs) +{ + PFS_thread *thd= my_thread_get_THR_PFS(); + + assert(buffer != NULL); + + if (likely(thd != NULL) && session_connect_attrs_size_per_thread > 0) + { + pfs_dirty_state dirty_state; + const CHARSET_INFO *cs = static_cast<const CHARSET_INFO *> (from_cs); + + /* copy from the input buffer as much as we can fit */ + uint copy_size= (uint)(length < session_connect_attrs_size_per_thread ? + length : session_connect_attrs_size_per_thread); + thd->m_session_lock.allocated_to_dirty(& dirty_state); + memcpy(thd->m_session_connect_attrs, buffer, copy_size); + thd->m_session_connect_attrs_length= copy_size; + thd->m_session_connect_attrs_cs_number= cs->number; + thd->m_session_lock.dirty_to_allocated(& dirty_state); + + if (copy_size == length) + return 0; + + session_connect_attrs_lost++; + return 1; + } + return 0; +} + +void pfs_register_memory_v1(const char *category, + PSI_memory_info_v1 *info, + int count) +{ + REGISTER_BODY_V1(PSI_memory_key, + memory_instrument_prefix, + register_memory_class) +} + +PSI_memory_key pfs_memory_alloc_v1(PSI_memory_key key, size_t size, PSI_thread **owner) +{ + PFS_thread ** owner_thread= reinterpret_cast<PFS_thread**>(owner); + assert(owner_thread != NULL); + + if (! flag_global_instrumentation) + { + *owner_thread= NULL; + return PSI_NOT_INSTRUMENTED; + } + + PFS_memory_class *klass= find_memory_class(key); + if (klass == NULL) + { + *owner_thread= NULL; + return PSI_NOT_INSTRUMENTED; + } + + if (! klass->m_enabled) + { + *owner_thread= NULL; + return PSI_NOT_INSTRUMENTED; + } + + PFS_memory_stat *event_name_array; + PFS_memory_stat *stat; + uint index= klass->m_event_name_index; + PFS_memory_stat_delta delta_buffer; + PFS_memory_stat_delta *delta; + + if (flag_thread_instrumentation && ! klass->is_global()) + { + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + { + *owner_thread= NULL; + return PSI_NOT_INSTRUMENTED; + } + if (! pfs_thread->m_enabled) + { + *owner_thread= NULL; + return PSI_NOT_INSTRUMENTED; + } + + /* Aggregate to MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME */ + event_name_array= pfs_thread->write_instr_class_memory_stats(); + stat= & event_name_array[index]; + delta= stat->count_alloc(size, &delta_buffer); + + if (delta != NULL) + { + pfs_thread->carry_memory_stat_delta(delta, index); + } + + /* Flag this memory as owned by the current thread. */ + *owner_thread= pfs_thread; + } + else + { + /* Aggregate to MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME */ + event_name_array= global_instr_class_memory_array; + stat= & event_name_array[index]; + (void) stat->count_alloc(size, &delta_buffer); + + *owner_thread= NULL; + } + + return key; +} + +PSI_memory_key pfs_memory_realloc_v1(PSI_memory_key key, size_t old_size, size_t new_size, PSI_thread **owner) +{ + PFS_thread ** owner_thread_hdl= reinterpret_cast<PFS_thread**>(owner); + assert(owner != NULL); + + PFS_memory_class *klass= find_memory_class(key); + if (klass == NULL) + { + *owner_thread_hdl= NULL; + return PSI_NOT_INSTRUMENTED; + } + + PFS_memory_stat *event_name_array; + PFS_memory_stat *stat; + uint index= klass->m_event_name_index; + PFS_memory_stat_delta delta_buffer; + PFS_memory_stat_delta *delta; + + if (flag_thread_instrumentation && ! klass->is_global()) + { + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (likely(pfs_thread != NULL)) + { +#ifdef PFS_PARANOID + PFS_thread *owner_thread= *owner_thread_hdl; + if (owner_thread != pfs_thread) + { + owner_thread= sanitize_thread(owner_thread); + if (owner_thread != NULL) + { + report_memory_accounting_error("pfs_memory_realloc_v1", + pfs_thread, old_size, klass, owner_thread); + } + } +#endif /* PFS_PARANOID */ + + /* Aggregate to MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME */ + event_name_array= pfs_thread->write_instr_class_memory_stats(); + stat= & event_name_array[index]; + + if (flag_global_instrumentation && klass->m_enabled) + { + delta= stat->count_realloc(old_size, new_size, &delta_buffer); + *owner_thread_hdl= pfs_thread; + } + else + { + delta= stat->count_free(old_size, &delta_buffer); + *owner_thread_hdl= NULL; + key= PSI_NOT_INSTRUMENTED; + } + + if (delta != NULL) + { + pfs_thread->carry_memory_stat_delta(delta, index); + } + return key; + } + } + + /* Aggregate to MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME */ + event_name_array= global_instr_class_memory_array; + stat= & event_name_array[index]; + + if (flag_global_instrumentation && klass->m_enabled) + { + (void) stat->count_realloc(old_size, new_size, &delta_buffer); + } + else + { + (void) stat->count_free(old_size, &delta_buffer); + key= PSI_NOT_INSTRUMENTED; + } + + *owner_thread_hdl= NULL; + return key; +} + +PSI_memory_key pfs_memory_claim_v1(PSI_memory_key key, size_t size, PSI_thread **owner) +{ + PFS_thread ** owner_thread= reinterpret_cast<PFS_thread**>(owner); + assert(owner_thread != NULL); + + PFS_memory_class *klass= find_memory_class(key); + if (klass == NULL) + { + *owner_thread= NULL; + return PSI_NOT_INSTRUMENTED; + } + + /* + Do not check klass->m_enabled. + Do not check flag_global_instrumentation. + If a memory alloc was instrumented, + the corresponding free must be instrumented. + */ + + PFS_memory_stat *event_name_array; + PFS_memory_stat *stat; + uint index= klass->m_event_name_index; + PFS_memory_stat_delta delta_buffer; + PFS_memory_stat_delta *delta; + + if (flag_thread_instrumentation) + { + PFS_thread *old_thread= sanitize_thread(*owner_thread); + PFS_thread *new_thread= my_thread_get_THR_PFS(); + if (old_thread != new_thread) + { + if (old_thread != NULL) + { + event_name_array= old_thread->write_instr_class_memory_stats(); + stat= & event_name_array[index]; + delta= stat->count_free(size, &delta_buffer); + + if (delta != NULL) + { + old_thread->carry_memory_stat_delta(delta, index); + } + } + + if (new_thread != NULL) + { + event_name_array= new_thread->write_instr_class_memory_stats(); + stat= & event_name_array[index]; + delta= stat->count_alloc(size, &delta_buffer); + + if (delta != NULL) + { + new_thread->carry_memory_stat_delta(delta, index); + } + } + + *owner_thread= new_thread; + } + + return key; + } + + *owner_thread= NULL; + return key; +} + +void pfs_memory_free_v1(PSI_memory_key key, size_t size, PSI_thread *owner) +{ + PFS_memory_class *klass= find_memory_class(key); + if (klass == NULL) + return; + + /* + Do not check klass->m_enabled. + Do not check flag_global_instrumentation. + If a memory alloc was instrumented, + the corresponding free must be instrumented. + */ + + PFS_memory_stat *event_name_array; + PFS_memory_stat *stat; + uint index= klass->m_event_name_index; + PFS_memory_stat_delta delta_buffer; + PFS_memory_stat_delta *delta; + + if (flag_thread_instrumentation && ! klass->is_global()) + { + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (likely(pfs_thread != NULL)) + { +#ifdef PFS_PARANOID + PFS_thread *owner_thread= reinterpret_cast<PFS_thread*>(owner); + + if (owner_thread != pfs_thread) + { + owner_thread= sanitize_thread(owner_thread); + if (owner_thread != NULL) + { + report_memory_accounting_error("pfs_memory_free_v1", + pfs_thread, size, klass, owner_thread); + } + } +#endif /* PFS_PARANOID */ + + /* + Do not check pfs_thread->m_enabled. + If a memory alloc was instrumented, + the corresponding free must be instrumented. + */ + /* Aggregate to MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME */ + event_name_array= pfs_thread->write_instr_class_memory_stats(); + stat= & event_name_array[index]; + delta= stat->count_free(size, &delta_buffer); + + if (delta != NULL) + { + pfs_thread->carry_memory_stat_delta(delta, index); + } + return; + } + } + + /* Aggregate to MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME */ + event_name_array= global_instr_class_memory_array; + if (event_name_array) + { + stat= & event_name_array[index]; + (void) stat->count_free(size, &delta_buffer); + } + return; +} + +void pfs_unlock_table_v1(PSI_table *table) +{ + PFS_table *pfs_table= reinterpret_cast<PFS_table*> (table); + + assert(pfs_table != NULL); + + pfs_table->m_internal_lock= PFS_TL_NONE; + return; +} + +PSI_metadata_lock * +pfs_create_metadata_lock_v1( + 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) +{ + if (! flag_global_instrumentation) + return NULL; + + if (! global_metadata_class.m_enabled) + return NULL; + + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (pfs_thread == NULL) + return NULL; + + PFS_metadata_lock *pfs; + pfs= create_metadata_lock(identity, mdl_key, + mdl_type, mdl_duration, mdl_status, + src_file, src_line); + + if (pfs != NULL) + { + pfs->m_owner_thread_id= pfs_thread->m_thread_internal_id; + pfs->m_owner_event_id= pfs_thread->m_event_id; + } + + return reinterpret_cast<PSI_metadata_lock *> (pfs); +} + +void +pfs_set_metadata_lock_status_v1(PSI_metadata_lock *lock, opaque_mdl_status mdl_status) +{ + PFS_metadata_lock *pfs= reinterpret_cast<PFS_metadata_lock*> (lock); + assert(pfs != NULL); + pfs->m_mdl_status= mdl_status; +} + +void +pfs_destroy_metadata_lock_v1(PSI_metadata_lock *lock) +{ + PFS_metadata_lock *pfs= reinterpret_cast<PFS_metadata_lock*> (lock); + assert(pfs != NULL); + destroy_metadata_lock(pfs); +} + +PSI_metadata_locker * +pfs_start_metadata_wait_v1(PSI_metadata_locker_state *state, + PSI_metadata_lock *lock, + const char *src_file, + uint src_line) +{ + PFS_metadata_lock *pfs_lock= reinterpret_cast<PFS_metadata_lock*> (lock); + assert(state != NULL); + assert(pfs_lock != NULL); + + if (! pfs_lock->m_enabled) + return NULL; + + uint flags; + ulonglong timer_start= 0; + + if (flag_thread_instrumentation) + { + PFS_thread *pfs_thread= my_thread_get_THR_PFS(); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + state->m_thread= reinterpret_cast<PSI_thread *> (pfs_thread); + flags= STATE_FLAG_THREAD; + + if (pfs_lock->m_timed) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + flags|= STATE_FLAG_TIMED; + } + + if (flag_events_waits_current) + { + if (unlikely(pfs_thread->m_events_waits_current >= + & pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) + { + locker_lost++; + return NULL; + } + PFS_events_waits *wait= pfs_thread->m_events_waits_current; + state->m_wait= wait; + flags|= STATE_FLAG_EVENT; + + PFS_events_waits *parent_event= wait - 1; + wait->m_event_type= EVENT_TYPE_WAIT; + wait->m_nesting_event_id= parent_event->m_event_id; + wait->m_nesting_event_type= parent_event->m_event_type; + + wait->m_thread_internal_id= pfs_thread->m_thread_internal_id; + wait->m_class= &global_metadata_class; + wait->m_timer_start= timer_start; + wait->m_timer_end= 0; + wait->m_object_instance_addr= pfs_lock->m_identity; + wait->m_event_id= pfs_thread->m_event_id++; + wait->m_end_event_id= 0; + wait->m_weak_metadata_lock= pfs_lock; + wait->m_weak_version= pfs_lock->get_version(); + wait->m_operation= OPERATION_TYPE_METADATA; + wait->m_source_file= src_file; + wait->m_source_line= src_line; + wait->m_wait_class= WAIT_CLASS_METADATA; + + pfs_thread->m_events_waits_current++; + } + } + else + { + if (pfs_lock->m_timed) + { + timer_start= get_timer_raw_value_and_function(wait_timer, & state->m_timer); + state->m_timer_start= timer_start; + flags= STATE_FLAG_TIMED; + state->m_thread= NULL; + } + else + { + /* + Complete shortcut. + */ + /* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME (counted) */ + global_metadata_stat.aggregate_counted(); + return NULL; + } + } + + state->m_flags= flags; + state->m_metadata_lock= lock; + return reinterpret_cast<PSI_metadata_locker*> (state); +} + +void +pfs_end_metadata_wait_v1(PSI_metadata_locker *locker, + int rc) +{ + PSI_metadata_locker_state *state= reinterpret_cast<PSI_metadata_locker_state*> (locker); + assert(state != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + + PFS_thread *thread= reinterpret_cast<PFS_thread *> (state->m_thread); + + uint flags= state->m_flags; + + if (flags & STATE_FLAG_TIMED) + { + timer_end= state->m_timer(); + wait_time= timer_end - state->m_timer_start; + } + + if (flags & STATE_FLAG_THREAD) + { + PFS_single_stat *event_name_array; + event_name_array= thread->write_instr_class_waits_stats(); + + if (flags & STATE_FLAG_TIMED) + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ + event_name_array[GLOBAL_METADATA_EVENT_INDEX].aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ + event_name_array[GLOBAL_METADATA_EVENT_INDEX].aggregate_counted(); + } + + if (flags & STATE_FLAG_EVENT) + { + PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); + assert(wait != NULL); + + wait->m_timer_end= timer_end; + wait->m_end_event_id= thread->m_event_id; + if (thread->m_flag_events_waits_history) + insert_events_waits_history(thread, wait); + if (thread->m_flag_events_waits_history_long) + insert_events_waits_history_long(wait); + thread->m_events_waits_current--; + + assert(wait == thread->m_events_waits_current); + } + } + else + { + if (flags & STATE_FLAG_TIMED) + { + /* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME (timed) */ + global_metadata_stat.aggregate_value(wait_time); + } + else + { + /* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME (counted) */ + global_metadata_stat.aggregate_counted(); + } + } +} + +void pfs_set_prepared_stmt_text_v1(PSI_prepared_stmt *prepared_stmt, + const char *text, + uint text_len) +{ + PFS_prepared_stmt *pfs_prepared_stmt = + reinterpret_cast<PFS_prepared_stmt *>(prepared_stmt); + DBUG_ASSERT(pfs_prepared_stmt != NULL); + + uint max_len = COL_INFO_SIZE; + if (text_len > max_len) + { + text_len = max_len; + } + + memcpy(pfs_prepared_stmt->m_sqltext, text, text_len); + pfs_prepared_stmt->m_sqltext_length = text_len; + + return; +} + +/** + Implementation of the instrumentation interface. + @sa PSI_v1. +*/ +PSI_v1 PFS_v1= +{ + pfs_register_mutex_v1, + pfs_register_rwlock_v1, + pfs_register_cond_v1, + pfs_register_thread_v1, + pfs_register_file_v1, + pfs_register_stage_v1, + pfs_register_statement_v1, + pfs_register_socket_v1, + pfs_init_mutex_v1, + pfs_destroy_mutex_v1, + pfs_init_rwlock_v1, + pfs_destroy_rwlock_v1, + pfs_init_cond_v1, + pfs_destroy_cond_v1, + pfs_init_socket_v1, + pfs_destroy_socket_v1, + pfs_get_table_share_v1, + pfs_release_table_share_v1, + pfs_drop_table_share_v1, + pfs_open_table_v1, + pfs_unbind_table_v1, + pfs_rebind_table_v1, + pfs_close_table_v1, + pfs_create_file_v1, + pfs_spawn_thread_v1, + pfs_new_thread_v1, + pfs_set_thread_id_v1, + pfs_set_thread_THD_v1, + pfs_set_thread_os_id_v1, + pfs_get_thread_v1, + pfs_set_thread_user_v1, + pfs_set_thread_account_v1, + pfs_set_thread_db_v1, + pfs_set_thread_command_v1, + pfs_set_connection_type_v1, + pfs_set_thread_start_time_v1, + pfs_set_thread_state_v1, + pfs_set_thread_info_v1, + pfs_set_thread_v1, + pfs_delete_current_thread_v1, + pfs_delete_thread_v1, + pfs_get_thread_file_name_locker_v1, + pfs_get_thread_file_stream_locker_v1, + pfs_get_thread_file_descriptor_locker_v1, + pfs_unlock_mutex_v1, + pfs_unlock_rwlock_v1, + pfs_signal_cond_v1, + pfs_broadcast_cond_v1, + pfs_start_idle_wait_v1, + pfs_end_idle_wait_v1, + pfs_start_mutex_wait_v1, + pfs_end_mutex_wait_v1, + pfs_start_rwlock_rdwait_v1, + pfs_end_rwlock_rdwait_v1, + pfs_start_rwlock_wrwait_v1, + pfs_end_rwlock_wrwait_v1, + pfs_start_cond_wait_v1, + pfs_end_cond_wait_v1, + pfs_start_table_io_wait_v1, + pfs_end_table_io_wait_v1, + pfs_start_table_lock_wait_v1, + pfs_end_table_lock_wait_v1, + pfs_start_file_open_wait_v1, + pfs_end_file_open_wait_v1, + pfs_end_file_open_wait_and_bind_to_descriptor_v1, + pfs_end_temp_file_open_wait_and_bind_to_descriptor_v1, + pfs_start_file_wait_v1, + pfs_end_file_wait_v1, + pfs_start_file_close_wait_v1, + pfs_end_file_close_wait_v1, + pfs_end_file_rename_wait_v1, + pfs_start_stage_v1, + pfs_get_current_stage_progress_v1, + pfs_end_stage_v1, + pfs_get_thread_statement_locker_v1, + pfs_refine_statement_v1, + pfs_start_statement_v1, + pfs_set_statement_text_v1, + pfs_set_statement_lock_time_v1, + pfs_set_statement_rows_sent_v1, + pfs_set_statement_rows_examined_v1, + pfs_inc_statement_created_tmp_disk_tables_v1, + pfs_inc_statement_created_tmp_tables_v1, + pfs_inc_statement_select_full_join_v1, + pfs_inc_statement_select_full_range_join_v1, + pfs_inc_statement_select_range_v1, + pfs_inc_statement_select_range_check_v1, + pfs_inc_statement_select_scan_v1, + pfs_inc_statement_sort_merge_passes_v1, + pfs_inc_statement_sort_range_v1, + pfs_inc_statement_sort_rows_v1, + pfs_inc_statement_sort_scan_v1, + pfs_set_statement_no_index_used_v1, + pfs_set_statement_no_good_index_used_v1, + pfs_end_statement_v1, + pfs_get_thread_transaction_locker_v1, + pfs_start_transaction_v1, + pfs_set_transaction_xid_v1, + pfs_set_transaction_xa_state_v1, + pfs_set_transaction_gtid_v1, + pfs_set_transaction_trxid_v1, + pfs_inc_transaction_savepoints_v1, + pfs_inc_transaction_rollback_to_savepoint_v1, + pfs_inc_transaction_release_savepoint_v1, + pfs_end_transaction_v1, + pfs_start_socket_wait_v1, + pfs_end_socket_wait_v1, + pfs_set_socket_state_v1, + pfs_set_socket_info_v1, + pfs_set_socket_thread_owner_v1, + pfs_create_prepared_stmt_v1, + pfs_destroy_prepared_stmt_v1, + pfs_reprepare_prepared_stmt_v1, + pfs_execute_prepared_stmt_v1, + pfs_set_prepared_stmt_text_v1, + pfs_digest_start_v1, + pfs_digest_end_v1, + pfs_set_thread_connect_attrs_v1, + pfs_start_sp_v1, + pfs_end_sp_v1, + pfs_drop_sp_v1, + pfs_get_sp_share_v1, + pfs_release_sp_share_v1, + pfs_register_memory_v1, + pfs_memory_alloc_v1, + pfs_memory_realloc_v1, + pfs_memory_claim_v1, + pfs_memory_free_v1, + pfs_unlock_table_v1, + pfs_create_metadata_lock_v1, + pfs_set_metadata_lock_status_v1, + pfs_destroy_metadata_lock_v1, + pfs_start_metadata_wait_v1, + pfs_end_metadata_wait_v1, + pfs_set_thread_peer_port_v1 +}; + +static void* get_interface(int version) +{ + switch (version) + { + case PSI_VERSION_1: + return &PFS_v1; + default: + return NULL; + } +} + +C_MODE_END + +struct PSI_bootstrap PFS_bootstrap= +{ + get_interface +}; diff --git a/storage/perfschema/pfs.h b/storage/perfschema/pfs.h new file mode 100644 index 00000000..6e0cf258 --- /dev/null +++ b/storage/perfschema/pfs.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_H +#define PFS_H + +/** + @file storage/perfschema/pfs.h + Performance schema instrumentation (declarations). +*/ + +#define HAVE_PSI_1 + +#include <my_global.h> +#include "my_thread.h" +#include <mysql/psi/psi.h> + +/** + Entry point to the performance schema implementation. + This singleton is used to discover the performance schema services. +*/ +extern struct PSI_bootstrap PFS_bootstrap; +/** Performance schema Thread Local Storage key. */ +extern pthread_key_t THR_PFS; +extern pthread_key_t THR_PFS_VG; // global_variables +extern pthread_key_t THR_PFS_SV; // session_variables +extern pthread_key_t THR_PFS_VBT; // variables_by_thread +extern pthread_key_t THR_PFS_SG; // global_status +extern pthread_key_t THR_PFS_SS; // session_status +extern pthread_key_t THR_PFS_SBT; // status_by_thread +extern pthread_key_t THR_PFS_SBU; // status_by_user +extern pthread_key_t THR_PFS_SBA; // status_by_host +extern pthread_key_t THR_PFS_SBH; // status_by_account + +/** True when @c THR_PFS and all other Performance Schema TLS keys are initialized. */ +extern bool THR_PFS_initialized; + +#define PSI_VOLATILITY_UNKNOWN 0 +#define PSI_VOLATILITY_SESSION 1 + +#define PSI_COUNT_VOLATILITY 2 + +#endif + diff --git a/storage/perfschema/pfs_account.cc b/storage/perfschema/pfs_account.cc new file mode 100644 index 00000000..ecf26cd7 --- /dev/null +++ b/storage/perfschema/pfs_account.cc @@ -0,0 +1,728 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_account.cc + Performance schema account (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "pfs.h" +#include "pfs_stat.h" +#include "pfs_instr.h" +#include "pfs_setup_actor.h" +#include "pfs_host.h" +#include "pfs_host.h" +#include "pfs_user.h" +#include "pfs_account.h" +#include "pfs_global.h" +#include "pfs_instr_class.h" +#include "pfs_buffer_container.h" + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +LF_HASH account_hash; +static bool account_hash_inited= false; + +/** + Initialize the user buffers. + @param param sizing parameters + @return 0 on success +*/ +int init_account(const PFS_global_param *param) +{ + if (global_account_container.init(param->m_account_sizing)) + return 1; + + return 0; +} + +/** Cleanup all the account buffers. */ +void cleanup_account(void) +{ + global_account_container.cleanup(); +} + +C_MODE_START +static uchar *account_hash_get_key(const uchar *entry, size_t *length, + my_bool) +{ + const PFS_account * const *typed_entry; + const PFS_account *account; + const void *result; + typed_entry= reinterpret_cast<const PFS_account* const *> (entry); + assert(typed_entry != NULL); + account= *typed_entry; + assert(account != NULL); + *length= account->m_key.m_key_length; + result= account->m_key.m_hash_key; + return const_cast<uchar*> (reinterpret_cast<const uchar*> (result)); +} +C_MODE_END + +/** + Initialize the user hash. + @return 0 on success +*/ +int init_account_hash(const PFS_global_param *param) +{ + if ((! account_hash_inited) && (param->m_account_sizing != 0)) + { + lf_hash_init(&account_hash, sizeof(PFS_account*), LF_HASH_UNIQUE, + 0, 0, account_hash_get_key, &my_charset_bin); + account_hash_inited= true; + } + return 0; +} + +/** Cleanup the user hash. */ +void cleanup_account_hash(void) +{ + if (account_hash_inited) + { + lf_hash_destroy(&account_hash); + account_hash_inited= false; + } +} + +static LF_PINS* get_account_hash_pins(PFS_thread *thread) +{ + if (unlikely(thread->m_account_hash_pins == NULL)) + { + if (! account_hash_inited) + return NULL; + thread->m_account_hash_pins= lf_hash_get_pins(&account_hash); + } + return thread->m_account_hash_pins; +} + +static void set_account_key(PFS_account_key *key, + const char *user, uint user_length, + const char *host, uint host_length) +{ + assert(user_length <= USERNAME_LENGTH); + assert(host_length <= HOSTNAME_LENGTH); + + char *ptr= &key->m_hash_key[0]; + if (user_length > 0) + { + memcpy(ptr, user, user_length); + ptr+= user_length; + } + ptr[0]= 0; + ptr++; + if (host_length > 0) + { + memcpy(ptr, host, host_length); + ptr+= host_length; + } + ptr[0]= 0; + ptr++; + key->m_key_length= (uint)(ptr - &key->m_hash_key[0]); +} + +PFS_account * +find_or_create_account(PFS_thread *thread, + const char *username, uint username_length, + const char *hostname, uint hostname_length) +{ + LF_PINS *pins= get_account_hash_pins(thread); + if (unlikely(pins == NULL)) + { + global_account_container.m_lost++; + return NULL; + } + + PFS_account_key key; + set_account_key(&key, username, username_length, + hostname, hostname_length); + + PFS_account **entry; + PFS_account *pfs; + uint retry_count= 0; + const uint retry_max= 3; + pfs_dirty_state dirty_state; + +search: + entry= reinterpret_cast<PFS_account**> + (lf_hash_search(&account_hash, pins, + key.m_hash_key, key.m_key_length)); + if (entry && (entry != MY_ERRPTR)) + { + pfs= *entry; + pfs->inc_refcount(); + lf_hash_search_unpin(pins); + return pfs; + } + + lf_hash_search_unpin(pins); + + pfs= global_account_container.allocate(& dirty_state); + if (pfs != NULL) + { + pfs->m_key= key; + if (username_length > 0) + pfs->m_username= &pfs->m_key.m_hash_key[0]; + else + pfs->m_username= NULL; + pfs->m_username_length= username_length; + + if (hostname_length > 0) + pfs->m_hostname= &pfs->m_key.m_hash_key[username_length + 1]; + else + pfs->m_hostname= NULL; + pfs->m_hostname_length= hostname_length; + + pfs->m_user= find_or_create_user(thread, username, username_length); + pfs->m_host= find_or_create_host(thread, hostname, hostname_length); + + pfs->init_refcount(); + pfs->reset_stats(); + pfs->m_disconnected_count= 0; + + if (username_length > 0 && hostname_length > 0) + { + lookup_setup_actor(thread, username, username_length, hostname, hostname_length, + & pfs->m_enabled, & pfs->m_history); + } + else + { + pfs->m_enabled= true; + pfs->m_history= true; + } + + int res; + pfs->m_lock.dirty_to_allocated(& dirty_state); + res= lf_hash_insert(&account_hash, pins, &pfs); + if (likely(res == 0)) + { + return pfs; + } + + if (pfs->m_user) + { + pfs->m_user->release(); + pfs->m_user= NULL; + } + if (pfs->m_host) + { + pfs->m_host->release(); + pfs->m_host= NULL; + } + + global_account_container.deallocate(pfs); + + if (res > 0) + { + if (++retry_count > retry_max) + { + global_account_container.m_lost++; + return NULL; + } + goto search; + } + + global_account_container.m_lost++; + return NULL; + } + + return NULL; +} + +void PFS_account::aggregate(bool alive, PFS_user *safe_user, PFS_host *safe_host) +{ + aggregate_waits(safe_user, safe_host); + aggregate_stages(safe_user, safe_host); + aggregate_statements(safe_user, safe_host); + aggregate_transactions(safe_user, safe_host); + aggregate_memory(alive, safe_user, safe_host); + aggregate_status(safe_user, safe_host); + aggregate_stats(safe_user, safe_host); +} + +void PFS_account::aggregate_waits(PFS_user *safe_user, PFS_host *safe_host) +{ + if (read_instr_class_waits_stats() == NULL) + return; + + if (likely(safe_user != NULL && safe_host != NULL)) + { + /* + Aggregate EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME + in parallel. + */ + aggregate_all_event_names(write_instr_class_waits_stats(), + safe_user->write_instr_class_waits_stats(), + safe_host->write_instr_class_waits_stats()); + return; + } + + if (safe_user != NULL) + { + /* + Aggregate EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME + */ + aggregate_all_event_names(write_instr_class_waits_stats(), + safe_user->write_instr_class_waits_stats()); + return; + } + + if (safe_host != NULL) + { + /* + Aggregate EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME + */ + aggregate_all_event_names(write_instr_class_waits_stats(), + safe_host->write_instr_class_waits_stats()); + return; + } + + /* Orphan account, no parent to aggregate to. */ + reset_waits_stats(); + return; +} + +void PFS_account::aggregate_stages(PFS_user *safe_user, PFS_host *safe_host) +{ + if (read_instr_class_stages_stats() == NULL) + return; + + if (likely(safe_user != NULL && safe_host != NULL)) + { + /* + Aggregate EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME + in parallel. + */ + aggregate_all_stages(write_instr_class_stages_stats(), + safe_user->write_instr_class_stages_stats(), + safe_host->write_instr_class_stages_stats()); + return; + } + + if (safe_user != NULL) + { + /* + Aggregate EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME + in parallel. + */ + aggregate_all_stages(write_instr_class_stages_stats(), + safe_user->write_instr_class_stages_stats(), + global_instr_class_stages_array); + return; + } + + if (safe_host != NULL) + { + /* + Aggregate EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME + */ + aggregate_all_stages(write_instr_class_stages_stats(), + safe_host->write_instr_class_stages_stats()); + return; + } + + /* + Aggregate EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME + */ + aggregate_all_stages(write_instr_class_stages_stats(), + global_instr_class_stages_array); + return; +} + +void PFS_account::aggregate_statements(PFS_user *safe_user, PFS_host *safe_host) +{ + if (read_instr_class_statements_stats() == NULL) + return; + + if (likely(safe_user != NULL && safe_host != NULL)) + { + /* + Aggregate EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME + in parallel. + */ + aggregate_all_statements(write_instr_class_statements_stats(), + safe_user->write_instr_class_statements_stats(), + safe_host->write_instr_class_statements_stats()); + return; + } + + if (safe_user != NULL) + { + /* + Aggregate EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME + in parallel. + */ + aggregate_all_statements(write_instr_class_statements_stats(), + safe_user->write_instr_class_statements_stats(), + global_instr_class_statements_array); + return; + } + + if (safe_host != NULL) + { + /* + Aggregate EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME + */ + aggregate_all_statements(write_instr_class_statements_stats(), + safe_host->write_instr_class_statements_stats()); + return; + } + + /* + Aggregate EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME + */ + aggregate_all_statements(write_instr_class_statements_stats(), + global_instr_class_statements_array); + return; +} + +void PFS_account::aggregate_transactions(PFS_user *safe_user, PFS_host *safe_host) +{ + if (read_instr_class_transactions_stats() == NULL) + return; + + if (likely(safe_user != NULL && safe_host != NULL)) + { + /* + Aggregate EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_TRANSACTIONS_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME + in parallel. + */ + aggregate_all_transactions(write_instr_class_transactions_stats(), + safe_user->write_instr_class_transactions_stats(), + safe_host->write_instr_class_transactions_stats()); + return; + } + + if (safe_user != NULL) + { + /* + Aggregate EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_TRANSACTIONS_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME + in parallel. + */ + aggregate_all_transactions(write_instr_class_transactions_stats(), + safe_user->write_instr_class_transactions_stats(), + &global_transaction_stat); + return; + } + + if (safe_host != NULL) + { + /* + Aggregate EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME + */ + aggregate_all_transactions(write_instr_class_transactions_stats(), + safe_host->write_instr_class_transactions_stats()); + return; + } + + /* + Aggregate EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME + */ + aggregate_all_transactions(write_instr_class_transactions_stats(), + &global_transaction_stat); + return; +} + +void PFS_account::aggregate_memory(bool alive, PFS_user *safe_user, PFS_host *safe_host) +{ + if (read_instr_class_memory_stats() == NULL) + return; + + if (likely(safe_user != NULL && safe_host != NULL)) + { + /* + Aggregate MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - MEMORY_SUMMARY_BY_USER_BY_EVENT_NAME + - MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME + in parallel. + */ + aggregate_all_memory(alive, + write_instr_class_memory_stats(), + safe_user->write_instr_class_memory_stats(), + safe_host->write_instr_class_memory_stats()); + return; + } + + if (safe_user != NULL) + { + /* + Aggregate MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - MEMORY_SUMMARY_BY_USER_BY_EVENT_NAME + - MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME + in parallel. + */ + aggregate_all_memory(alive, + write_instr_class_memory_stats(), + safe_user->write_instr_class_memory_stats(), + global_instr_class_memory_array); + return; + } + + if (safe_host != NULL) + { + /* + Aggregate MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME + */ + aggregate_all_memory(alive, + write_instr_class_memory_stats(), + safe_host->write_instr_class_memory_stats()); + return; + } + + /* + Aggregate MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME to: + - MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME + */ + aggregate_all_memory(alive, + write_instr_class_memory_stats(), + global_instr_class_memory_array); + return; +} + +void PFS_account::aggregate_status(PFS_user *safe_user, PFS_host *safe_host) +{ + /* + Never aggregate to global_status_var, + because of the parallel THD -> global_status_var flow. + */ + + if (safe_user != NULL) + { + /* + Aggregate STATUS_BY_ACCOUNT to: + - STATUS_BY_USER + */ + safe_user->m_status_stats.aggregate(& m_status_stats); + } + + if (safe_host != NULL) + { + /* + Aggregate STATUS_BY_ACCOUNT to: + - STATUS_BY_HOST + */ + safe_host->m_status_stats.aggregate(& m_status_stats); + } + + m_status_stats.reset(); + return; +} + +void PFS_account::aggregate_stats(PFS_user *safe_user, PFS_host *safe_host) +{ + if (likely(safe_user != NULL && safe_host != NULL)) + { + safe_user->m_disconnected_count+= m_disconnected_count; + safe_host->m_disconnected_count+= m_disconnected_count; + m_disconnected_count= 0; + return; + } + + if (safe_user != NULL) + { + safe_user->m_disconnected_count+= m_disconnected_count; + m_disconnected_count= 0; + return; + } + + if (safe_host != NULL) + { + safe_host->m_disconnected_count+= m_disconnected_count; + m_disconnected_count= 0; + return; + } + + m_disconnected_count= 0; + return; +} + +void PFS_account::release() +{ + dec_refcount(); +} + +void PFS_account::carry_memory_stat_delta(PFS_memory_stat_delta *delta, uint index) +{ + PFS_memory_stat *event_name_array; + PFS_memory_stat *stat; + PFS_memory_stat_delta delta_buffer; + PFS_memory_stat_delta *remaining_delta; + + event_name_array= write_instr_class_memory_stats(); + stat= & event_name_array[index]; + remaining_delta= stat->apply_delta(delta, &delta_buffer); + + if (remaining_delta == NULL) + return; + + if (m_user != NULL) + { + m_user->carry_memory_stat_delta(remaining_delta, index); + /* do not return, need to process m_host below */ + } + + if (m_host != NULL) + { + m_host->carry_memory_stat_delta(remaining_delta, index); + return; + } + + carry_global_memory_stat_delta(remaining_delta, index); +} + +PFS_account *sanitize_account(PFS_account *unsafe) +{ + return global_account_container.sanitize(unsafe); +} + +void purge_account(PFS_thread *thread, PFS_account *account) +{ + LF_PINS *pins= get_account_hash_pins(thread); + if (unlikely(pins == NULL)) + return; + + PFS_account **entry; + entry= reinterpret_cast<PFS_account**> + (lf_hash_search(&account_hash, pins, + account->m_key.m_hash_key, + account->m_key.m_key_length)); + if (entry && (entry != MY_ERRPTR)) + { + assert(*entry == account); + if (account->get_refcount() == 0) + { + lf_hash_delete(&account_hash, pins, + account->m_key.m_hash_key, + account->m_key.m_key_length); + account->aggregate(false, account->m_user, account->m_host); + if (account->m_user != NULL) + { + account->m_user->release(); + account->m_user= NULL; + } + if (account->m_host != NULL) + { + account->m_host->release(); + account->m_host= NULL; + } + global_account_container.deallocate(account); + } + } + + lf_hash_search_unpin(pins); +} + +class Proc_purge_account + : public PFS_buffer_processor<PFS_account> +{ +public: + Proc_purge_account(PFS_thread *thread) + : m_thread(thread) + {} + + virtual void operator()(PFS_account *pfs) + { + PFS_user *user= sanitize_user(pfs->m_user); + PFS_host *host= sanitize_host(pfs->m_host); + pfs->aggregate(true, user, host); + + if (pfs->get_refcount() == 0) + purge_account(m_thread, pfs); + } + +private: + PFS_thread *m_thread; +}; + +/** Purge non connected accounts, reset stats of connected account. */ +void purge_all_account(void) +{ + PFS_thread *thread= PFS_thread::get_current_thread(); + if (unlikely(thread == NULL)) + return; + + Proc_purge_account proc(thread); + global_account_container.apply(proc); +} + +class Proc_update_accounts_derived_flags + : public PFS_buffer_processor<PFS_account> +{ +public: + Proc_update_accounts_derived_flags(PFS_thread *thread) + : m_thread(thread) + {} + + virtual void operator()(PFS_account *pfs) + { + if (pfs->m_username_length > 0 && pfs->m_hostname_length > 0) + { + lookup_setup_actor(m_thread, + pfs->m_username, pfs->m_username_length, + pfs->m_hostname, pfs->m_hostname_length, + & pfs->m_enabled, & pfs->m_history); + } + else + { + pfs->m_enabled= true; + pfs->m_history= true; + } + } + +private: + PFS_thread *m_thread; +}; + +void update_accounts_derived_flags(PFS_thread *thread) +{ + Proc_update_accounts_derived_flags proc(thread); + global_account_container.apply(proc); +} + +/** @} */ diff --git a/storage/perfschema/pfs_account.h b/storage/perfschema/pfs_account.h new file mode 100644 index 00000000..0aa36204 --- /dev/null +++ b/storage/perfschema/pfs_account.h @@ -0,0 +1,136 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_ACCOUNT_H +#define PFS_ACCOUNT_H + +/** + @file storage/perfschema/pfs_account.h + Performance schema account (declarations). +*/ + +#include "pfs_lock.h" +#include "lf.h" +#include "pfs_con_slice.h" +#include "mysql_com.h" /* USERNAME_LENGTH */ + +struct PFS_global_param; +struct PFS_user; +struct PFS_host; +struct PFS_thread; +struct PFS_memory_stat_delta; + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +/** Hash key for an account. */ +struct PFS_account_key +{ + /** + Hash search key. + This has to be a string for LF_HASH, + the format is "<username><0x00><hostname><0x00>" + */ + char m_hash_key[USERNAME_LENGTH + 1 + HOSTNAME_LENGTH + 1]; + uint m_key_length; +}; + +/** Per account statistics. */ +struct PFS_ALIGNED PFS_account : PFS_connection_slice +{ +public: + inline void init_refcount(void) + { + PFS_atomic::store_32(& m_refcount, 1); + } + + inline int get_refcount(void) + { + return PFS_atomic::load_32(& m_refcount); + } + + inline void inc_refcount(void) + { + PFS_atomic::add_32(& m_refcount, 1); + } + + inline void dec_refcount(void) + { + PFS_atomic::add_32(& m_refcount, -1); + } + + void aggregate(bool alive, PFS_user *safe_user, PFS_host *safe_host); + void aggregate_waits(PFS_user *safe_user, PFS_host *safe_host); + void aggregate_stages(PFS_user *safe_user, PFS_host *safe_host); + void aggregate_statements(PFS_user *safe_user, PFS_host *safe_host); + void aggregate_transactions(PFS_user *safe_user, PFS_host *safe_host); + void aggregate_memory(bool alive, PFS_user *safe_user, PFS_host *safe_host); + void aggregate_status(PFS_user *safe_user, PFS_host *safe_host); + void aggregate_stats(PFS_user *safe_user, PFS_host *safe_host); + void release(void); + + void carry_memory_stat_delta(PFS_memory_stat_delta *delta, uint index); + + /** Internal lock. */ + pfs_lock m_lock; + PFS_account_key m_key; + /** True if this account is enabled, per rules in table SETUP_ACTORS. */ + bool m_enabled; + /** True if this account has history enabled, per rules in table SETUP_ACTORS. */ + bool m_history; + const char *m_username; + uint m_username_length; + const char *m_hostname; + uint m_hostname_length; + PFS_user *m_user; + PFS_host *m_host; + + ulonglong m_disconnected_count; + +private: + int m_refcount; +}; + +int init_account(const PFS_global_param *param); +void cleanup_account(void); +int init_account_hash(const PFS_global_param *param); +void cleanup_account_hash(void); + +PFS_account * +find_or_create_account(PFS_thread *thread, + const char *username, uint username_length, + const char *hostname, uint hostname_length); + +PFS_account *sanitize_account(PFS_account *unsafe); +void purge_all_account(void); + +void update_accounts_derived_flags(PFS_thread *thread); + +/* For show status. */ + +extern LF_HASH account_hash; + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_atomic.h b/storage/perfschema/pfs_atomic.h new file mode 100644 index 00000000..8543cdab --- /dev/null +++ b/storage/perfschema/pfs_atomic.h @@ -0,0 +1,141 @@ +/* Copyright (c) 2009, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_ATOMIC_H +#define PFS_ATOMIC_H + +/** + @file storage/perfschema/pfs_atomic.h + Atomic operations (declarations). +*/ + +#include <my_atomic.h> + +/** Helper for atomic operations. */ +class PFS_atomic +{ +public: + /** Atomic load. */ + static inline int32 load_32(int32 *ptr) + { + return my_atomic_load32(ptr); + } + + /** Atomic load. */ + static inline int64 load_64(int64 *ptr) + { + return my_atomic_load64(ptr); + } + + /** Atomic load. */ + static inline uint32 load_u32(uint32 *ptr) + { + return (uint32) my_atomic_load32((int32*) ptr); + } + + /** Atomic load. */ + static inline uint64 load_u64(uint64 *ptr) + { + return (uint64) my_atomic_load64((int64*) ptr); + } + + /** Atomic store. */ + static inline void store_32(int32 *ptr, int32 value) + { + my_atomic_store32(ptr, value); + } + + /** Atomic store. */ + static inline void store_64(int64 *ptr, int64 value) + { + my_atomic_store64(ptr, value); + } + + /** Atomic store. */ + static inline void store_u32(uint32 *ptr, uint32 value) + { + my_atomic_store32((int32*) ptr, (int32) value); + } + + /** Atomic store. */ + static inline void store_u64(uint64 *ptr, uint64 value) + { + my_atomic_store64((int64*) ptr, (int64) value); + } + + /** Atomic add. */ + static inline int32 add_32(int32 *ptr, int32 value) + { + return my_atomic_add32(ptr, value); + } + + /** Atomic add. */ + static inline int64 add_64(int64 *ptr, int64 value) + { + return my_atomic_add64(ptr, value); + } + + /** Atomic add. */ + static inline uint32 add_u32(uint32 *ptr, uint32 value) + { + return (uint32) my_atomic_add32((int32*) ptr, (int32) value); + } + + /** Atomic add. */ + static inline uint64 add_u64(uint64 *ptr, uint64 value) + { + return (uint64) my_atomic_add64((int64*) ptr, (int64) value); + } + + /** Atomic compare and swap. */ + static inline bool cas_32(int32 *ptr, int32 *old_value, + int32 new_value) + { + return my_atomic_cas32(ptr, old_value, new_value); + } + + /** Atomic compare and swap. */ + static inline bool cas_64(int64 *ptr, int64 *old_value, + int64 new_value) + { + return my_atomic_cas64(ptr, old_value, new_value); + } + + /** Atomic compare and swap. */ + static inline bool cas_u32(uint32 *ptr, uint32 *old_value, + uint32 new_value) + { + return my_atomic_cas32((int32*) ptr, (int32*) old_value, + (uint32) new_value); + } + + /** Atomic compare and swap. */ + static inline bool cas_u64(uint64 *ptr, uint64 *old_value, + uint64 new_value) + { + return my_atomic_cas64((int64*) ptr, (int64*) old_value, + (uint64) new_value); + } +}; + +#endif + diff --git a/storage/perfschema/pfs_autosize.cc b/storage/perfschema/pfs_autosize.cc new file mode 100644 index 00000000..36087d0c --- /dev/null +++ b/storage/perfschema/pfs_autosize.cc @@ -0,0 +1,315 @@ +/* Copyright (c) 2012, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_autosize.cc + Private interface for the server (implementation). +*/ + +#include "my_global.h" +#include "sql_const.h" +#include "pfs_server.h" +#include "set_var.h" + +#include "my_thread.h" /* For pthread_t */ +/* Make sure HAVE_PSI_XXX_INTERFACE flags are set */ +#include "mysql/psi/psi.h" + +#include <algorithm> +using std::min; +using std::max; + +/** Performance schema sizing heuristics. */ +struct PFS_sizing_data +{ + /** Default value for @c PFS_param.m_events_waits_history_sizing. */ + ulong m_events_waits_history_sizing; + /** Default value for @c PFS_param.m_events_waits_history_long_sizing. */ + ulong m_events_waits_history_long_sizing; + /** Default value for @c PFS_param.m_events_stages_history_sizing. */ + ulong m_events_stages_history_sizing; + /** Default value for @c PFS_param.m_events_stages_history_long_sizing. */ + ulong m_events_stages_history_long_sizing; + /** Default value for @c PFS_param.m_events_statements_history_sizing. */ + ulong m_events_statements_history_sizing; + /** Default value for @c PFS_param.m_events_statements_history_long_sizing. */ + ulong m_events_statements_history_long_sizing; + /** Default value for @c PFS_param.m_events_transactions_history_sizing. */ + ulong m_events_transactions_history_sizing; + /** Default value for @c PFS_param.m_events_transactions_history_long_sizing. */ + ulong m_events_transactions_history_long_sizing; + /** Default value for @c PFS_param.m_digest_sizing. */ + ulong m_digest_sizing; + /** Default value for @c PFS_param.m_session_connect_attrs_sizing. */ + ulong m_session_connect_attrs_sizing; +}; + +PFS_sizing_data small_data= +{ + /* History sizes */ + 5, 100, 5, 100, 5, 100, 5, 100, + /* Digests */ + 1000, + /* Session connect attrs. */ + 512 +}; + +PFS_sizing_data medium_data= +{ + /* History sizes */ + 10, 1000, 10, 1000, 10, 1000, 10, 1000, + /* Digests */ + 5000, + /* Session connect attrs. */ + 512 +}; + +PFS_sizing_data large_data= +{ + /* History sizes */ + 10, 10000, 10, 10000, 10, 10000, 10, 10000, + /* Digests */ + 10000, + /* Session connect attrs. */ + 512 +}; + +PFS_sizing_data *estimate_hints(PFS_global_param *param) +{ + if ((param->m_hints.m_max_connections <= MAX_CONNECTIONS_DEFAULT) && + (param->m_hints.m_table_definition_cache <= TABLE_DEF_CACHE_DEFAULT) && + (param->m_hints.m_table_open_cache <= TABLE_OPEN_CACHE_DEFAULT)) + { + /* The my.cnf used is either unchanged, or lower than factory defaults. */ + return & small_data; + } + + if ((param->m_hints.m_max_connections <= MAX_CONNECTIONS_DEFAULT * 2) && + (param->m_hints.m_table_definition_cache <= TABLE_DEF_CACHE_DEFAULT * 2) && + (param->m_hints.m_table_open_cache <= TABLE_OPEN_CACHE_DEFAULT * 2)) + { + /* Some defaults have been increased, to "moderate" values. */ + return & medium_data; + } + + /* Looks like a server in production. */ + return & large_data; +} + +static void apply_heuristic(PFS_global_param *p, PFS_sizing_data *h) +{ + if (p->m_events_waits_history_sizing < 0) + { + SYSVAR_AUTOSIZE(p->m_events_waits_history_sizing, + h->m_events_waits_history_sizing); + } + + if (p->m_events_waits_history_long_sizing < 0) + { + SYSVAR_AUTOSIZE(p->m_events_waits_history_long_sizing, + h->m_events_waits_history_long_sizing); + } + + if (p->m_events_stages_history_sizing < 0) + { + SYSVAR_AUTOSIZE(p->m_events_stages_history_sizing, + h->m_events_stages_history_sizing); + } + + if (p->m_events_stages_history_long_sizing < 0) + { + SYSVAR_AUTOSIZE(p->m_events_stages_history_long_sizing, + h->m_events_stages_history_long_sizing); + } + + if (p->m_events_statements_history_sizing < 0) + { + SYSVAR_AUTOSIZE(p->m_events_statements_history_sizing, + h->m_events_statements_history_sizing); + } + + if (p->m_events_statements_history_long_sizing < 0) + { + SYSVAR_AUTOSIZE(p->m_events_statements_history_long_sizing, + h->m_events_statements_history_long_sizing); + } + + if (p->m_digest_sizing < 0) + { + SYSVAR_AUTOSIZE(p->m_digest_sizing, + h->m_digest_sizing); + } + + if (p->m_events_transactions_history_sizing < 0) + { + SYSVAR_AUTOSIZE(p->m_events_transactions_history_sizing, + h->m_events_transactions_history_sizing); + } + + if (p->m_events_transactions_history_long_sizing < 0) + { + SYSVAR_AUTOSIZE(p->m_events_transactions_history_long_sizing, + h->m_events_transactions_history_long_sizing); + } + + if (p->m_session_connect_attrs_sizing < 0) + { + SYSVAR_AUTOSIZE(p->m_session_connect_attrs_sizing, + h->m_session_connect_attrs_sizing); + } +} + +void pfs_automated_sizing(PFS_global_param *param) +{ + if (param->m_enabled) + { +#ifndef HAVE_PSI_MUTEX_INTERFACE + param->m_mutex_class_sizing= 0; + param->m_mutex_sizing= 0; +#endif + +#ifndef HAVE_PSI_RWLOCK_INTERFACE + param->m_rwlock_class_sizing= 0; + param->m_rwlock_sizing= 0; +#endif + +#ifndef HAVE_PSI_COND_INTERFACE + param->m_cond_class_sizing= 0; + param->m_cond_sizing= 0; +#endif + +#ifndef HAVE_PSI_FILE_INTERFACE + param->m_file_class_sizing= 0; + param->m_file_sizing= 0; + param->m_file_handle_sizing= 0; +#endif + +#ifndef HAVE_PSI_TABLE_INTERFACE + param->m_table_share_sizing= 0; + param->m_table_sizing= 0; + param->m_table_lock_stat_sizing= 0; + param->m_index_stat_sizing= 0; +#endif + +#ifndef HAVE_PSI_SOCKET_INTERFACE + param->m_socket_class_sizing= 0; + param->m_socket_sizing= 0; +#endif + +#ifndef HAVE_PSI_STAGE_INTERFACE + param->m_stage_class_sizing= 0; + param->m_events_stages_history_sizing= 0; + param->m_events_stages_history_long_sizing= 0; +#endif + +#ifndef HAVE_PSI_STATEMENT_INTERFACE + param->m_statement_class_sizing= 0; + param->m_events_statements_history_sizing= 0; + param->m_events_statements_history_long_sizing= 0; +#endif + +#ifndef HAVE_PSI_SP_INTERFACE + param->m_program_sizing= 0; + if (param->m_statement_stack_sizing > 1) + param->m_statement_stack_sizing= 1; +#endif + +#ifndef HAVE_PSI_PS_INTERFACE + param->m_prepared_stmt_sizing= 0; +#endif + +#ifndef HAVE_PSI_STATEMENT_DIGEST_INTERFACE + param->m_digest_sizing= 0; +#endif + +#ifndef HAVE_PSI_METADATA_INTERFACE + param->m_metadata_lock_sizing= 0; +#endif + +#ifndef HAVE_PSI_MEMORY_INTERFACE + param->m_memory_class_sizing= 0; +#endif + + PFS_sizing_data *heuristic; + heuristic= estimate_hints(param); + apply_heuristic(param, heuristic); + + assert(param->m_events_waits_history_sizing >= 0); + assert(param->m_events_waits_history_long_sizing >= 0); + assert(param->m_events_stages_history_sizing >= 0); + assert(param->m_events_stages_history_long_sizing >= 0); + assert(param->m_events_statements_history_sizing >= 0); + assert(param->m_events_statements_history_long_sizing >= 0); + assert(param->m_events_transactions_history_sizing >= 0); + assert(param->m_events_transactions_history_long_sizing >= 0); + assert(param->m_session_connect_attrs_sizing >= 0); + } + else + { + /* + The Performance Schema is disabled. Set the instrument sizings to zero to + disable all instrumentation while retaining support for the status and + system variable tables, the host cache table and the replication tables. + */ + SYSVAR_AUTOSIZE(param->m_mutex_class_sizing, 0); + SYSVAR_AUTOSIZE(param->m_rwlock_class_sizing, 0); + SYSVAR_AUTOSIZE(param->m_cond_class_sizing, 0); + SYSVAR_AUTOSIZE(param->m_thread_class_sizing, 0); + SYSVAR_AUTOSIZE(param->m_table_share_sizing, 0); + SYSVAR_AUTOSIZE(param->m_table_lock_stat_sizing, 0); + SYSVAR_AUTOSIZE(param->m_index_stat_sizing, 0); + SYSVAR_AUTOSIZE(param->m_file_class_sizing, 0); + SYSVAR_AUTOSIZE(param->m_mutex_sizing, 0); + SYSVAR_AUTOSIZE(param->m_rwlock_sizing, 0); + SYSVAR_AUTOSIZE(param->m_cond_sizing, 0); + SYSVAR_AUTOSIZE(param->m_thread_sizing, 0); + SYSVAR_AUTOSIZE(param->m_table_sizing, 0); + SYSVAR_AUTOSIZE(param->m_file_sizing, 0); + SYSVAR_AUTOSIZE(param->m_file_handle_sizing, 0); + SYSVAR_AUTOSIZE(param->m_socket_sizing, 0); + SYSVAR_AUTOSIZE(param->m_socket_class_sizing, 0); + SYSVAR_AUTOSIZE(param->m_events_waits_history_sizing, 0); + SYSVAR_AUTOSIZE(param->m_events_waits_history_long_sizing, 0); + SYSVAR_AUTOSIZE(param->m_setup_actor_sizing, 0); + SYSVAR_AUTOSIZE(param->m_setup_object_sizing, 0); + SYSVAR_AUTOSIZE(param->m_host_sizing, 0); + SYSVAR_AUTOSIZE(param->m_user_sizing, 0); + SYSVAR_AUTOSIZE(param->m_account_sizing, 0); + SYSVAR_AUTOSIZE(param->m_stage_class_sizing, 0); + SYSVAR_AUTOSIZE(param->m_events_stages_history_sizing, 0); + SYSVAR_AUTOSIZE(param->m_events_stages_history_long_sizing, 0); + SYSVAR_AUTOSIZE(param->m_statement_class_sizing, 0); + SYSVAR_AUTOSIZE(param->m_events_statements_history_sizing, 0); + SYSVAR_AUTOSIZE(param->m_events_statements_history_long_sizing, 0); + SYSVAR_AUTOSIZE(param->m_digest_sizing, 0); + SYSVAR_AUTOSIZE(param->m_program_sizing, 0); + SYSVAR_AUTOSIZE(param->m_prepared_stmt_sizing, 0); + SYSVAR_AUTOSIZE(param->m_events_transactions_history_sizing, 0); + SYSVAR_AUTOSIZE(param->m_events_transactions_history_long_sizing, 0); + SYSVAR_AUTOSIZE(param->m_session_connect_attrs_sizing, 0); + SYSVAR_AUTOSIZE(param->m_statement_stack_sizing, 0); + SYSVAR_AUTOSIZE(param->m_memory_class_sizing, 0); + SYSVAR_AUTOSIZE(param->m_metadata_lock_sizing, 0); + SYSVAR_AUTOSIZE(param->m_max_digest_length, 0); + SYSVAR_AUTOSIZE(param->m_max_sql_text_length, 0); + } +} diff --git a/storage/perfschema/pfs_buffer_container.cc b/storage/perfschema/pfs_buffer_container.cc new file mode 100644 index 00000000..04f6f521 --- /dev/null +++ b/storage/perfschema/pfs_buffer_container.cc @@ -0,0 +1,883 @@ +/* Copyright (c) 2014, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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 */ + +#include "my_global.h" +#include "pfs_global.h" +#include "pfs_lock.h" +#include "pfs_account.h" +#include "pfs_user.h" +#include "pfs_host.h" +#include "pfs_buffer_container.h" +#include "pfs_builtin_memory.h" + +PFS_buffer_default_allocator<PFS_mutex> default_mutex_allocator(& builtin_memory_mutex); +PFS_mutex_container global_mutex_container(& default_mutex_allocator); + +PFS_buffer_default_allocator<PFS_rwlock> default_rwlock_allocator(& builtin_memory_rwlock); +PFS_rwlock_container global_rwlock_container(& default_rwlock_allocator); + +PFS_buffer_default_allocator<PFS_cond> default_cond_allocator(& builtin_memory_cond); +PFS_cond_container global_cond_container(& default_cond_allocator); + +PFS_buffer_default_allocator<PFS_file> default_file_allocator(& builtin_memory_file); +PFS_file_container global_file_container(& default_file_allocator); + +PFS_buffer_default_allocator<PFS_socket> default_socket_allocator(& builtin_memory_socket); +PFS_socket_container global_socket_container(& default_socket_allocator); + +PFS_buffer_default_allocator<PFS_metadata_lock> default_mdl_allocator(& builtin_memory_mdl); +PFS_mdl_container global_mdl_container(& default_mdl_allocator); + +PFS_buffer_default_allocator<PFS_setup_actor> default_setup_actor_allocator(& builtin_memory_setup_actor); +PFS_setup_actor_container global_setup_actor_container(& default_setup_actor_allocator); + +PFS_buffer_default_allocator<PFS_setup_object> default_setup_object_allocator(& builtin_memory_setup_object); +PFS_setup_object_container global_setup_object_container(& default_setup_object_allocator); + +PFS_buffer_default_allocator<PFS_table> default_table_allocator(& builtin_memory_table); +PFS_table_container global_table_container(& default_table_allocator); + +PFS_buffer_default_allocator<PFS_table_share> default_table_share_allocator(& builtin_memory_table_share); +PFS_table_share_container global_table_share_container(& default_table_share_allocator); + +PFS_buffer_default_allocator<PFS_table_share_index> default_table_share_index_allocator(& builtin_memory_table_share_index); +PFS_table_share_index_container global_table_share_index_container(& default_table_share_index_allocator); + +PFS_buffer_default_allocator<PFS_table_share_lock> default_table_share_lock_allocator(& builtin_memory_table_share_lock); +PFS_table_share_lock_container global_table_share_lock_container(& default_table_share_lock_allocator); + +PFS_buffer_default_allocator<PFS_program> default_program_allocator(& builtin_memory_program); +PFS_program_container global_program_container(& default_program_allocator); + +PFS_buffer_default_allocator<PFS_prepared_stmt> default_prepared_stmt_allocator(& builtin_memory_prepared_stmt); +PFS_prepared_stmt_container global_prepared_stmt_container(& default_prepared_stmt_allocator); + +int PFS_account_allocator::alloc_array(PFS_account_array *array) +{ + size_t size= array->m_max; + size_t index; + size_t waits_sizing= size * wait_class_max; + size_t stages_sizing= size * stage_class_max; + size_t statements_sizing= size * statement_class_max; + size_t transactions_sizing= size * transaction_class_max; + size_t memory_sizing= size * memory_class_max; + + array->m_ptr= NULL; + array->m_full= true; + array->m_instr_class_waits_array= NULL; + array->m_instr_class_stages_array= NULL; + array->m_instr_class_statements_array= NULL; + array->m_instr_class_transactions_array= NULL; + array->m_instr_class_memory_array= NULL; + + if (size > 0) + { + array->m_ptr= + PFS_MALLOC_ARRAY(& builtin_memory_account, + size, sizeof(PFS_account), PFS_account, MYF(MY_ZEROFILL)); + if (array->m_ptr == NULL) + return 1; + } + + if (waits_sizing > 0) + { + array->m_instr_class_waits_array= + PFS_MALLOC_ARRAY(& builtin_memory_account_waits, + waits_sizing, sizeof(PFS_single_stat), PFS_single_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_waits_array == NULL) + return 1; + + for (index=0; index < waits_sizing; index++) + array->m_instr_class_waits_array[index].reset(); + } + + if (stages_sizing > 0) + { + array->m_instr_class_stages_array= + PFS_MALLOC_ARRAY(& builtin_memory_account_stages, + stages_sizing, sizeof(PFS_stage_stat), PFS_stage_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_stages_array == NULL) + return 1; + + for (index=0; index < stages_sizing; index++) + array->m_instr_class_stages_array[index].reset(); + } + + if (statements_sizing > 0) + { + array->m_instr_class_statements_array= + PFS_MALLOC_ARRAY(& builtin_memory_account_statements, + statements_sizing, sizeof(PFS_statement_stat), PFS_statement_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_statements_array == NULL) + return 1; + + for (index=0; index < statements_sizing; index++) + array->m_instr_class_statements_array[index].reset(); + } + + if (transactions_sizing > 0) + { + array->m_instr_class_transactions_array= + PFS_MALLOC_ARRAY(& builtin_memory_account_transactions, + transactions_sizing, sizeof(PFS_transaction_stat), PFS_transaction_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_transactions_array == NULL) + return 1; + + for (index=0; index < transactions_sizing; index++) + array->m_instr_class_transactions_array[index].reset(); + } + + if (memory_sizing > 0) + { + array->m_instr_class_memory_array= + PFS_MALLOC_ARRAY(& builtin_memory_account_memory, + memory_sizing, sizeof(PFS_memory_stat), PFS_memory_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_memory_array == NULL) + return 1; + + for (index=0; index < memory_sizing; index++) + array->m_instr_class_memory_array[index].reset(); + } + + for (index= 0; index < size; index++) + { + array->m_ptr[index].set_instr_class_waits_stats( + & array->m_instr_class_waits_array[index * wait_class_max]); + array->m_ptr[index].set_instr_class_stages_stats( + & array->m_instr_class_stages_array[index * stage_class_max]); + array->m_ptr[index].set_instr_class_statements_stats( + & array->m_instr_class_statements_array[index * statement_class_max]); + array->m_ptr[index].set_instr_class_transactions_stats( + & array->m_instr_class_transactions_array[index * transaction_class_max]); + array->m_ptr[index].set_instr_class_memory_stats( + & array->m_instr_class_memory_array[index * memory_class_max]); + } + + array->m_full= false; + return 0; +} + +void PFS_account_allocator::free_array(PFS_account_array *array) +{ + size_t size= array->m_max; + size_t waits_sizing= size * wait_class_max; + size_t stages_sizing= size * stage_class_max; + size_t statements_sizing= size * statement_class_max; + size_t transactions_sizing= size * transaction_class_max; + size_t memory_sizing= size * memory_class_max; + + PFS_FREE_ARRAY(& builtin_memory_account, + size, sizeof(PFS_account), array->m_ptr); + array->m_ptr= NULL; + + PFS_FREE_ARRAY(& builtin_memory_account_waits, + waits_sizing, sizeof(PFS_single_stat), + array->m_instr_class_waits_array); + array->m_instr_class_waits_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_account_stages, + stages_sizing, sizeof(PFS_stage_stat), + array->m_instr_class_stages_array); + array->m_instr_class_stages_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_account_statements, + statements_sizing, sizeof(PFS_statement_stat), + array->m_instr_class_statements_array); + array->m_instr_class_statements_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_account_transactions, + transactions_sizing, sizeof(PFS_transaction_stat), + array->m_instr_class_transactions_array); + array->m_instr_class_transactions_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_account_memory, + memory_sizing, sizeof(PFS_memory_stat), + array->m_instr_class_memory_array); + array->m_instr_class_memory_array= NULL; +} + +PFS_account_allocator account_allocator; +PFS_account_container global_account_container(& account_allocator); + +int PFS_host_allocator::alloc_array(PFS_host_array *array) +{ + size_t size= array->m_max; + PFS_host *pfs; + size_t index; + size_t waits_sizing= size * wait_class_max; + size_t stages_sizing= size * stage_class_max; + size_t statements_sizing= size * statement_class_max; + size_t transactions_sizing= size * transaction_class_max; + size_t memory_sizing= size * memory_class_max; + + array->m_ptr= NULL; + array->m_full= true; + array->m_instr_class_waits_array= NULL; + array->m_instr_class_stages_array= NULL; + array->m_instr_class_statements_array= NULL; + array->m_instr_class_transactions_array= NULL; + array->m_instr_class_memory_array= NULL; + + if (size > 0) + { + array->m_ptr= + PFS_MALLOC_ARRAY(& builtin_memory_host, + size, sizeof(PFS_host), PFS_host, MYF(MY_ZEROFILL)); + if (array->m_ptr == NULL) + return 1; + } + + if (waits_sizing > 0) + { + array->m_instr_class_waits_array= + PFS_MALLOC_ARRAY(& builtin_memory_host_waits, + waits_sizing, sizeof(PFS_single_stat), PFS_single_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_waits_array == NULL) + return 1; + + for (index=0; index < waits_sizing; index++) + array->m_instr_class_waits_array[index].reset(); + } + + if (stages_sizing > 0) + { + array->m_instr_class_stages_array= + PFS_MALLOC_ARRAY(& builtin_memory_host_stages, + stages_sizing, sizeof(PFS_stage_stat), PFS_stage_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_stages_array == NULL) + return 1; + + for (index=0; index < stages_sizing; index++) + array->m_instr_class_stages_array[index].reset(); + } + + if (statements_sizing > 0) + { + array->m_instr_class_statements_array= + PFS_MALLOC_ARRAY(& builtin_memory_host_statements, + statements_sizing, sizeof(PFS_statement_stat), PFS_statement_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_statements_array == NULL) + return 1; + + for (index=0; index < statements_sizing; index++) + array->m_instr_class_statements_array[index].reset(); + } + + if (transactions_sizing > 0) + { + array->m_instr_class_transactions_array= + PFS_MALLOC_ARRAY(& builtin_memory_host_transactions, + transactions_sizing, sizeof(PFS_transaction_stat), PFS_transaction_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_transactions_array == NULL) + return 1; + + for (index=0; index < transactions_sizing; index++) + array->m_instr_class_transactions_array[index].reset(); + } + + if (memory_sizing > 0) + { + array->m_instr_class_memory_array= + PFS_MALLOC_ARRAY(& builtin_memory_host_memory, + memory_sizing, sizeof(PFS_memory_stat), PFS_memory_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_memory_array == NULL) + return 1; + + for (index=0; index < memory_sizing; index++) + array->m_instr_class_memory_array[index].reset(); + } + + for (index= 0; index < size; index++) + { + pfs= & array->m_ptr[index]; + + pfs->set_instr_class_waits_stats( + & array->m_instr_class_waits_array[index * wait_class_max]); + pfs->set_instr_class_stages_stats( + & array->m_instr_class_stages_array[index * stage_class_max]); + pfs->set_instr_class_statements_stats( + & array->m_instr_class_statements_array[index * statement_class_max]); + pfs->set_instr_class_transactions_stats( + & array->m_instr_class_transactions_array[index * transaction_class_max]); + pfs->set_instr_class_memory_stats( + & array->m_instr_class_memory_array[index * memory_class_max]); + } + + array->m_full= false; + return 0; +} + +void PFS_host_allocator::free_array(PFS_host_array *array) +{ + size_t size= array->m_max; + size_t waits_sizing= size * wait_class_max; + size_t stages_sizing= size * stage_class_max; + size_t statements_sizing= size * statement_class_max; + size_t transactions_sizing= size * transaction_class_max; + size_t memory_sizing= size * memory_class_max; + + PFS_FREE_ARRAY(& builtin_memory_host, + size, sizeof(PFS_host), array->m_ptr); + array->m_ptr= NULL; + + PFS_FREE_ARRAY(& builtin_memory_host_waits, + waits_sizing, sizeof(PFS_single_stat), + array->m_instr_class_waits_array); + array->m_instr_class_waits_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_host_stages, + stages_sizing, sizeof(PFS_stage_stat), + array->m_instr_class_stages_array); + array->m_instr_class_stages_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_host_statements, + statements_sizing, sizeof(PFS_statement_stat), + array->m_instr_class_statements_array); + array->m_instr_class_statements_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_host_transactions, + transactions_sizing, sizeof(PFS_transaction_stat), + array->m_instr_class_transactions_array); + array->m_instr_class_transactions_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_host_memory, + memory_sizing, sizeof(PFS_memory_stat), + array->m_instr_class_memory_array); + array->m_instr_class_memory_array= NULL; +} + +PFS_host_allocator host_allocator; +PFS_host_container global_host_container(& host_allocator); + +int PFS_thread_allocator::alloc_array(PFS_thread_array *array) +{ + size_t size= array->m_max; + PFS_thread *pfs; + PFS_events_statements *pfs_stmt; + unsigned char *pfs_tokens; + + size_t index; + size_t waits_sizing= size * wait_class_max; + size_t stages_sizing= size * stage_class_max; + size_t statements_sizing= size * statement_class_max; + size_t transactions_sizing= size * transaction_class_max; + size_t memory_sizing= size * memory_class_max; + + size_t waits_history_sizing= size * events_waits_history_per_thread; + size_t stages_history_sizing= size * events_stages_history_per_thread; + size_t statements_history_sizing= size * events_statements_history_per_thread; + size_t statements_stack_sizing= size * statement_stack_max; + size_t transactions_history_sizing= size * events_transactions_history_per_thread; + size_t session_connect_attrs_sizing= size * session_connect_attrs_size_per_thread; + + size_t current_sqltext_sizing= size * pfs_max_sqltext * statement_stack_max; + size_t history_sqltext_sizing= size * pfs_max_sqltext * events_statements_history_per_thread; + size_t current_digest_tokens_sizing= size * pfs_max_digest_length * statement_stack_max; + size_t history_digest_tokens_sizing= size * pfs_max_digest_length * events_statements_history_per_thread; + + array->m_ptr= NULL; + array->m_full= true; + array->m_instr_class_waits_array= NULL; + array->m_instr_class_stages_array= NULL; + array->m_instr_class_statements_array= NULL; + array->m_instr_class_transactions_array= NULL; + array->m_instr_class_memory_array= NULL; + + array->m_waits_history_array= NULL; + array->m_stages_history_array= NULL; + array->m_statements_history_array= NULL; + array->m_statements_stack_array= NULL; + array->m_transactions_history_array= NULL; + array->m_session_connect_attrs_array= NULL; + + array->m_current_stmts_text_array= NULL; + array->m_current_stmts_digest_token_array= NULL; + array->m_history_stmts_text_array= NULL; + array->m_history_stmts_digest_token_array= NULL; + + if (size > 0) + { + array->m_ptr= + PFS_MALLOC_ARRAY(& builtin_memory_thread, + size, sizeof(PFS_thread), PFS_thread, MYF(MY_ZEROFILL)); + if (array->m_ptr == NULL) + return 1; + } + + if (waits_sizing > 0) + { + array->m_instr_class_waits_array= + PFS_MALLOC_ARRAY(& builtin_memory_thread_waits, + waits_sizing, sizeof(PFS_single_stat), PFS_single_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_waits_array == NULL) + return 1; + + for (index=0; index < waits_sizing; index++) + array->m_instr_class_waits_array[index].reset(); + } + + if (stages_sizing > 0) + { + array->m_instr_class_stages_array= + PFS_MALLOC_ARRAY(& builtin_memory_thread_stages, + stages_sizing, sizeof(PFS_stage_stat), PFS_stage_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_stages_array == NULL) + return 1; + + for (index=0; index < stages_sizing; index++) + array->m_instr_class_stages_array[index].reset(); + } + + if (statements_sizing > 0) + { + array->m_instr_class_statements_array= + PFS_MALLOC_ARRAY(& builtin_memory_thread_statements, + statements_sizing, sizeof(PFS_statement_stat), PFS_statement_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_statements_array == NULL) + return 1; + + for (index=0; index < statements_sizing; index++) + array->m_instr_class_statements_array[index].reset(); + } + + if (transactions_sizing > 0) + { + array->m_instr_class_transactions_array= + PFS_MALLOC_ARRAY(& builtin_memory_thread_transactions, + transactions_sizing, sizeof(PFS_transaction_stat), PFS_transaction_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_transactions_array == NULL) + return 1; + + for (index=0; index < transactions_sizing; index++) + array->m_instr_class_transactions_array[index].reset(); + } + + if (memory_sizing > 0) + { + array->m_instr_class_memory_array= + PFS_MALLOC_ARRAY(& builtin_memory_thread_memory, + memory_sizing, sizeof(PFS_memory_stat), PFS_memory_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_memory_array == NULL) + return 1; + + for (index=0; index < memory_sizing; index++) + array->m_instr_class_memory_array[index].reset(); + } + + if (waits_history_sizing > 0) + { + array->m_waits_history_array= + PFS_MALLOC_ARRAY(& builtin_memory_thread_waits_history, + waits_history_sizing, sizeof(PFS_events_waits), PFS_events_waits, MYF(MY_ZEROFILL)); + if (unlikely(array->m_waits_history_array == NULL)) + return 1; + } + + if (stages_history_sizing > 0) + { + array->m_stages_history_array= + PFS_MALLOC_ARRAY(& builtin_memory_thread_stages_history, + stages_history_sizing, sizeof(PFS_events_stages), PFS_events_stages, MYF(MY_ZEROFILL)); + if (unlikely(array->m_stages_history_array == NULL)) + return 1; + } + + if (statements_history_sizing > 0) + { + array->m_statements_history_array= + PFS_MALLOC_ARRAY(& builtin_memory_thread_statements_history, + statements_history_sizing, sizeof(PFS_events_statements), PFS_events_statements, MYF(MY_ZEROFILL)); + if (unlikely(array->m_statements_history_array == NULL)) + return 1; + } + + if (statements_stack_sizing > 0) + { + array->m_statements_stack_array= + PFS_MALLOC_ARRAY(& builtin_memory_thread_statements_stack, + statements_stack_sizing, sizeof(PFS_events_statements), PFS_events_statements, MYF(MY_ZEROFILL)); + if (unlikely(array->m_statements_stack_array == NULL)) + return 1; + } + + if (transactions_history_sizing > 0) + { + array->m_transactions_history_array= + PFS_MALLOC_ARRAY(& builtin_memory_thread_transaction_history, + transactions_history_sizing, sizeof(PFS_events_transactions), PFS_events_transactions, MYF(MY_ZEROFILL)); + if (unlikely(array->m_transactions_history_array == NULL)) + return 1; + } + + if (session_connect_attrs_sizing > 0) + { + array->m_session_connect_attrs_array= + (char *)pfs_malloc(& builtin_memory_thread_session_connect_attrs, + session_connect_attrs_sizing, MYF(MY_ZEROFILL)); + if (unlikely(array->m_session_connect_attrs_array == NULL)) + return 1; + } + + if (current_sqltext_sizing > 0) + { + array->m_current_stmts_text_array= + (char *)pfs_malloc(& builtin_memory_thread_statements_stack_sqltext, + current_sqltext_sizing, MYF(MY_ZEROFILL)); + if (unlikely(array->m_current_stmts_text_array == NULL)) + return 1; + } + + if (history_sqltext_sizing > 0) + { + array->m_history_stmts_text_array= + (char *)pfs_malloc(& builtin_memory_thread_statements_history_sqltext, + history_sqltext_sizing, MYF(MY_ZEROFILL)); + if (unlikely(array->m_history_stmts_text_array == NULL)) + return 1; + } + + if (current_digest_tokens_sizing > 0) + { + array->m_current_stmts_digest_token_array= + (unsigned char *)pfs_malloc(& builtin_memory_thread_statements_stack_tokens, + current_digest_tokens_sizing, MYF(MY_ZEROFILL)); + if (unlikely(array->m_current_stmts_digest_token_array == NULL)) + return 1; + } + + if (history_digest_tokens_sizing > 0) + { + array->m_history_stmts_digest_token_array= + (unsigned char *)pfs_malloc(& builtin_memory_thread_statements_history_tokens, + history_digest_tokens_sizing, MYF(MY_ZEROFILL)); + if (unlikely(array->m_history_stmts_digest_token_array == NULL)) + return 1; + } + + for (index= 0; index < size; index++) + { + pfs= & array->m_ptr[index]; + + pfs->set_instr_class_waits_stats( + & array->m_instr_class_waits_array[index * wait_class_max]); + pfs->set_instr_class_stages_stats( + & array->m_instr_class_stages_array[index * stage_class_max]); + pfs->set_instr_class_statements_stats( + & array->m_instr_class_statements_array[index * statement_class_max]); + pfs->set_instr_class_transactions_stats( + & array->m_instr_class_transactions_array[index * transaction_class_max]); + pfs->set_instr_class_memory_stats( + & array->m_instr_class_memory_array[index * memory_class_max]); + + pfs->m_waits_history= + & array->m_waits_history_array[index * events_waits_history_per_thread]; + pfs->m_stages_history= + & array->m_stages_history_array[index * events_stages_history_per_thread]; + pfs->m_statements_history= + & array->m_statements_history_array[index * events_statements_history_per_thread]; + pfs->m_statement_stack= + & array->m_statements_stack_array[index * statement_stack_max]; + pfs->m_transactions_history= + & array->m_transactions_history_array[index * events_transactions_history_per_thread]; + pfs->m_session_connect_attrs= + & array->m_session_connect_attrs_array[index * session_connect_attrs_size_per_thread]; + } + + for (index= 0; index < statements_stack_sizing; index++) + { + pfs_stmt= & array->m_statements_stack_array[index]; + + pfs_stmt->m_sqltext= & array->m_current_stmts_text_array[index * pfs_max_sqltext]; + + pfs_tokens= & array->m_current_stmts_digest_token_array[index * pfs_max_digest_length]; + pfs_stmt->m_digest_storage.reset(pfs_tokens, pfs_max_digest_length); + } + + for (index= 0; index < statements_history_sizing; index++) + { + pfs_stmt= & array->m_statements_history_array[index]; + + pfs_stmt->m_sqltext= & array->m_history_stmts_text_array[index * pfs_max_sqltext]; + + pfs_tokens= & array->m_history_stmts_digest_token_array[index * pfs_max_digest_length]; + pfs_stmt->m_digest_storage.reset(pfs_tokens, pfs_max_digest_length); + } + + array->m_full= false; + return 0; +} + +void PFS_thread_allocator::free_array(PFS_thread_array *array) +{ + size_t size= array->m_max; + size_t waits_sizing= size * wait_class_max; + size_t stages_sizing= size * stage_class_max; + size_t statements_sizing= size * statement_class_max; + size_t transactions_sizing= size * transaction_class_max; + size_t memory_sizing= size * memory_class_max; + + size_t waits_history_sizing= size * events_waits_history_per_thread; + size_t stages_history_sizing= size * events_stages_history_per_thread; + size_t statements_history_sizing= size * events_statements_history_per_thread; + size_t statements_stack_sizing= size * statement_stack_max; + size_t transactions_history_sizing= size * events_transactions_history_per_thread; + size_t session_connect_attrs_sizing= size * session_connect_attrs_size_per_thread; + + size_t current_sqltext_sizing= size * pfs_max_sqltext * statement_stack_max; + size_t history_sqltext_sizing= size * pfs_max_sqltext * events_statements_history_per_thread; + size_t current_digest_tokens_sizing= size * pfs_max_digest_length * statement_stack_max; + size_t history_digest_tokens_sizing= size * pfs_max_digest_length * events_statements_history_per_thread; + + PFS_FREE_ARRAY(& builtin_memory_thread, + size, sizeof(PFS_thread), array->m_ptr); + array->m_ptr= NULL; + + PFS_FREE_ARRAY(& builtin_memory_thread_waits, + waits_sizing, sizeof(PFS_single_stat), + array->m_instr_class_waits_array); + array->m_instr_class_waits_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_thread_stages, + stages_sizing, sizeof(PFS_stage_stat), + array->m_instr_class_stages_array); + array->m_instr_class_stages_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_thread_statements, + statements_sizing, sizeof(PFS_statement_stat), + array->m_instr_class_statements_array); + array->m_instr_class_statements_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_thread_transactions, + transactions_sizing, sizeof(PFS_transaction_stat), + array->m_instr_class_transactions_array); + array->m_instr_class_transactions_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_thread_memory, + memory_sizing, sizeof(PFS_memory_stat), + array->m_instr_class_memory_array); + array->m_instr_class_memory_array= NULL; + + + PFS_FREE_ARRAY(& builtin_memory_thread_waits_history, + waits_history_sizing, sizeof(PFS_events_waits), + array->m_waits_history_array); + array->m_waits_history_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_thread_stages_history, + stages_history_sizing, sizeof(PFS_events_stages), + array->m_stages_history_array); + array->m_stages_history_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_thread_statements_history, + statements_history_sizing, sizeof(PFS_events_statements), + array->m_statements_history_array); + array->m_statements_history_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_thread_statements_stack, + statements_stack_sizing, sizeof(PFS_events_statements), + array->m_statements_stack_array); + array->m_statements_stack_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_thread_transaction_history, + transactions_history_sizing, sizeof(PFS_events_transactions), + array->m_transactions_history_array); + array->m_transactions_history_array= NULL; + + pfs_free(& builtin_memory_thread_session_connect_attrs, + session_connect_attrs_sizing, + array->m_session_connect_attrs_array); + array->m_session_connect_attrs_array= NULL; + + pfs_free(& builtin_memory_thread_statements_stack_sqltext, + current_sqltext_sizing, + array->m_current_stmts_text_array); + array->m_current_stmts_text_array= NULL; + + pfs_free(& builtin_memory_thread_statements_history_sqltext, + history_sqltext_sizing, + array->m_history_stmts_text_array); + array->m_history_stmts_text_array= NULL; + + pfs_free(& builtin_memory_thread_statements_stack_tokens, + current_digest_tokens_sizing, + array->m_current_stmts_digest_token_array); + array->m_current_stmts_digest_token_array= NULL; + + pfs_free(& builtin_memory_thread_statements_history_tokens, + history_digest_tokens_sizing, + array->m_history_stmts_digest_token_array); + array->m_history_stmts_digest_token_array= NULL; +} + +PFS_thread_allocator thread_allocator; +PFS_thread_container global_thread_container(& thread_allocator); + +int PFS_user_allocator::alloc_array(PFS_user_array *array) +{ + size_t size= array->m_max; + PFS_user *pfs; + size_t index; + size_t waits_sizing= size * wait_class_max; + size_t stages_sizing= size * stage_class_max; + size_t statements_sizing= size * statement_class_max; + size_t transactions_sizing= size * transaction_class_max; + size_t memory_sizing= size * memory_class_max; + + array->m_ptr= NULL; + array->m_full= true; + array->m_instr_class_waits_array= NULL; + array->m_instr_class_stages_array= NULL; + array->m_instr_class_statements_array= NULL; + array->m_instr_class_transactions_array= NULL; + array->m_instr_class_memory_array= NULL; + + if (size > 0) + { + array->m_ptr= + PFS_MALLOC_ARRAY(& builtin_memory_user, + size, sizeof(PFS_user), PFS_user, MYF(MY_ZEROFILL)); + if (array->m_ptr == NULL) + return 1; + } + + if (waits_sizing > 0) + { + array->m_instr_class_waits_array= + PFS_MALLOC_ARRAY(& builtin_memory_user_waits, + waits_sizing, sizeof(PFS_single_stat), PFS_single_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_waits_array == NULL) + return 1; + + for (index=0; index < waits_sizing; index++) + array->m_instr_class_waits_array[index].reset(); + } + + if (stages_sizing > 0) + { + array->m_instr_class_stages_array= + PFS_MALLOC_ARRAY(& builtin_memory_user_stages, + stages_sizing, sizeof(PFS_stage_stat), PFS_stage_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_stages_array == NULL) + return 1; + + for (index=0; index < stages_sizing; index++) + array->m_instr_class_stages_array[index].reset(); + } + + if (statements_sizing > 0) + { + array->m_instr_class_statements_array= + PFS_MALLOC_ARRAY(& builtin_memory_user_statements, + statements_sizing, sizeof(PFS_statement_stat), PFS_statement_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_statements_array == NULL) + return 1; + + for (index=0; index < statements_sizing; index++) + array->m_instr_class_statements_array[index].reset(); + } + + if (transactions_sizing > 0) + { + array->m_instr_class_transactions_array= + PFS_MALLOC_ARRAY(& builtin_memory_user_transactions, + transactions_sizing, sizeof(PFS_transaction_stat), PFS_transaction_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_transactions_array == NULL) + return 1; + + for (index=0; index < transactions_sizing; index++) + array->m_instr_class_transactions_array[index].reset(); + } + + if (memory_sizing > 0) + { + array->m_instr_class_memory_array= + PFS_MALLOC_ARRAY(& builtin_memory_user_memory, + memory_sizing, sizeof(PFS_memory_stat), PFS_memory_stat, MYF(MY_ZEROFILL)); + if (array->m_instr_class_memory_array == NULL) + return 1; + + for (index=0; index < memory_sizing; index++) + array->m_instr_class_memory_array[index].reset(); + } + + for (index= 0; index < size; index++) + { + pfs= & array->m_ptr[index]; + + pfs->set_instr_class_waits_stats( + & array->m_instr_class_waits_array[index * wait_class_max]); + pfs->set_instr_class_stages_stats( + & array->m_instr_class_stages_array[index * stage_class_max]); + pfs->set_instr_class_statements_stats( + & array->m_instr_class_statements_array[index * statement_class_max]); + pfs->set_instr_class_transactions_stats( + & array->m_instr_class_transactions_array[index * transaction_class_max]); + pfs->set_instr_class_memory_stats( + & array->m_instr_class_memory_array[index * memory_class_max]); + } + + array->m_full= false; + return 0; +} + +void PFS_user_allocator::free_array(PFS_user_array *array) +{ + size_t size= array->m_max; + size_t waits_sizing= size * wait_class_max; + size_t stages_sizing= size * stage_class_max; + size_t statements_sizing= size * statement_class_max; + size_t transactions_sizing= size * transaction_class_max; + size_t memory_sizing= size * memory_class_max; + + PFS_FREE_ARRAY(& builtin_memory_user, + size, sizeof(PFS_user), array->m_ptr); + array->m_ptr= NULL; + + PFS_FREE_ARRAY(& builtin_memory_user_waits, + waits_sizing, sizeof(PFS_single_stat), + array->m_instr_class_waits_array); + array->m_instr_class_waits_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_user_stages, + stages_sizing, sizeof(PFS_stage_stat), + array->m_instr_class_stages_array); + array->m_instr_class_stages_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_user_statements, + statements_sizing, sizeof(PFS_statement_stat), + array->m_instr_class_statements_array); + array->m_instr_class_statements_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_user_transactions, + transactions_sizing, sizeof(PFS_transaction_stat), + array->m_instr_class_transactions_array); + array->m_instr_class_transactions_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_user_memory, + memory_sizing, sizeof(PFS_memory_stat), + array->m_instr_class_memory_array); + array->m_instr_class_memory_array= NULL; +} + +PFS_user_allocator user_allocator; +PFS_user_container global_user_container(& user_allocator); + diff --git a/storage/perfschema/pfs_buffer_container.h b/storage/perfschema/pfs_buffer_container.h new file mode 100644 index 00000000..d5745e76 --- /dev/null +++ b/storage/perfschema/pfs_buffer_container.h @@ -0,0 +1,1626 @@ +/* Copyright (c) 2014, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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_BUFFER_CONTAINER_H +#define PFS_BUFFER_CONTAINER_H + +#include "my_global.h" +#include "pfs.h" // PSI_COUNT_VOLATILITY +#include "pfs_lock.h" +#include "pfs_instr.h" +#include "pfs_setup_actor.h" +#include "pfs_setup_object.h" +#include "pfs_program.h" +#include "pfs_prepared_stmt.h" +#include "pfs_builtin_memory.h" + +#define USE_SCALABLE + +class PFS_opaque_container_page; +class PFS_opaque_container; + +struct PFS_builtin_memory_class; + +template <class T> +class PFS_buffer_const_iterator; + +template <class T> +class PFS_buffer_processor; + +template <class T, class U, class V> +class PFS_buffer_iterator; + +template <class T, int PFS_PAGE_SIZE, int PFS_PAGE_COUNT, class U, class V> +class PFS_buffer_scalable_iterator; + +template <class T> +class PFS_buffer_default_array; + +template <class T> +class PFS_buffer_default_allocator; + +template <class T, class U, class V> +class PFS_buffer_container; + +template <class T, int PFS_PAGE_SIZE, int PFS_PAGE_COUNT, class U, class V> +class PFS_buffer_scalable_container; + +template <class B, int COUNT> +class PFS_partitioned_buffer_scalable_iterator; + +template <class B, int COUNT> +class PFS_partitioned_buffer_scalable_container; + + +template <class T> +class PFS_buffer_default_array +{ +public: + typedef T value_type; + + value_type *allocate(pfs_dirty_state *dirty_state) + { + uint index; + uint monotonic; + uint monotonic_max; + value_type *pfs; + + if (m_full) + return NULL; + + monotonic= PFS_atomic::add_u32(& m_monotonic.m_u32, 1); + monotonic_max= monotonic + static_cast<uint>(m_max); + + while (monotonic < monotonic_max) + { + index= monotonic % m_max; + pfs= m_ptr + index; + + if (pfs->m_lock.free_to_dirty(dirty_state)) + { + return pfs; + } + monotonic= PFS_atomic::add_u32(& m_monotonic.m_u32, 1); + } + + m_full= true; + return NULL; + } + + void deallocate(value_type *pfs) + { + pfs->m_lock.allocated_to_free(); + m_full= false; + } + + T* get_first() + { + return m_ptr; + } + + T* get_last() + { + return m_ptr + m_max; + } + + bool m_full; + PFS_cacheline_uint32 m_monotonic; + T * m_ptr; + size_t m_max; + /** Container. */ + PFS_opaque_container *m_container; +}; + +template <class T> +class PFS_buffer_default_allocator +{ +public: + typedef PFS_buffer_default_array<T> array_type; + + PFS_buffer_default_allocator(PFS_builtin_memory_class *klass) + : m_builtin_class(klass) + {} + + int alloc_array(array_type *array) + { + array->m_ptr= NULL; + array->m_full= true; + array->m_monotonic.m_u32= 0; + + if (array->m_max > 0) + { + array->m_ptr= PFS_MALLOC_ARRAY(m_builtin_class, + array->m_max, sizeof(T), T, MYF(MY_ZEROFILL)); + if (array->m_ptr == NULL) + return 1; + array->m_full= false; + } + return 0; + } + + void free_array(array_type *array) + { + assert(array->m_max > 0); + + PFS_FREE_ARRAY(m_builtin_class, + array->m_max, sizeof(T), array->m_ptr); + array->m_ptr= NULL; + } + +private: + PFS_builtin_memory_class *m_builtin_class; +}; + +template <class T, + class U = PFS_buffer_default_array<T>, + class V = PFS_buffer_default_allocator<T> > +class PFS_buffer_container +{ +public: + friend class PFS_buffer_iterator<T, U, V>; + + typedef T value_type; + typedef U array_type; + typedef V allocator_type; + typedef PFS_buffer_const_iterator<T> const_iterator_type; + typedef PFS_buffer_iterator<T, U, V> iterator_type; + typedef PFS_buffer_processor<T> processor_type; + typedef void (*function_type)(value_type *); + + PFS_buffer_container(allocator_type *allocator) + { + m_array.m_full= true; + m_array.m_ptr= NULL; + m_array.m_max= 0; + m_array.m_monotonic.m_u32= 0; + m_lost= 0; + m_max= 0; + m_allocator= allocator; + } + + int init(ulong max_size) + { + if (max_size > 0) + { + m_array.m_max= max_size; + int rc= m_allocator->alloc_array(& m_array); + if (rc != 0) + { + m_allocator->free_array(& m_array); + return 1; + } + m_max= max_size; + m_array.m_full= false; + } + return 0; + } + + void cleanup() + { + m_allocator->free_array(& m_array); + } + + ulong get_row_count() const + { + return m_max; + } + + ulong get_row_size() const + { + return sizeof(value_type); + } + + ulong get_memory() const + { + return get_row_count() * get_row_size(); + } + + value_type *allocate(pfs_dirty_state *dirty_state) + { + value_type *pfs; + + pfs= m_array.allocate(dirty_state, m_max); + if (pfs == NULL) + { + m_lost++; + } + + return pfs; + } + + void deallocate(value_type *pfs) + { + m_array.deallocate(pfs); + } + + iterator_type iterate() + { + return PFS_buffer_iterator<T, U, V>(this, 0); + } + + iterator_type iterate(uint index) + { + assert(index <= m_max); + return PFS_buffer_iterator<T, U, V>(this, index); + } + + void apply(function_type fct) + { + value_type *pfs= m_array.get_first(); + value_type *pfs_last= m_array.get_last(); + + while (pfs < pfs_last) + { + if (pfs->m_lock.is_populated()) + { + fct(pfs); + } + pfs++; + } + } + + void apply_all(function_type fct) + { + value_type *pfs= m_array.get_first(); + value_type *pfs_last= m_array.get_last(); + + while (pfs < pfs_last) + { + fct(pfs); + pfs++; + } + } + + void apply(processor_type & proc) + { + value_type *pfs= m_array.get_first(); + value_type *pfs_last= m_array.get_last(); + + while (pfs < pfs_last) + { + if (pfs->m_lock.is_populated()) + { + proc(pfs); + } + pfs++; + } + } + + void apply_all(processor_type & proc) + { + value_type *pfs= m_array.get_first(); + value_type *pfs_last= m_array.get_last(); + + while (pfs < pfs_last) + { + proc(pfs); + pfs++; + } + } + + inline value_type* get(uint index) + { + assert(index < m_max); + + value_type *pfs= m_array.m_ptr + index; + if (pfs->m_lock.is_populated()) + { + return pfs; + } + + return NULL; + } + + value_type* get(uint index, bool *has_more) + { + if (index >= m_max) + { + *has_more= false; + return NULL; + } + + *has_more= true; + return get(index); + } + + value_type *sanitize(value_type *unsafe) + { + intptr offset; + value_type *pfs= m_array.get_first(); + value_type *pfs_last= m_array.get_last(); + + if ((pfs <= unsafe) && + (unsafe < pfs_last)) + { + offset= ((intptr) unsafe - (intptr) pfs) % sizeof(value_type); + if (offset == 0) + return unsafe; + } + + return NULL; + } + + ulong m_lost; + +private: + value_type* scan_next(uint & index, uint * found_index) + { + assert(index <= m_max); + + value_type *pfs_first= m_array.get_first(); + value_type *pfs= pfs_first + index; + value_type *pfs_last= m_array.get_last(); + + while (pfs < pfs_last) + { + if (pfs->m_lock.is_populated()) + { + uint found= pfs - pfs_first; + *found_index= found; + index= found + 1; + return pfs; + } + pfs++; + } + + index= m_max; + return NULL; + } + + ulong m_max; + array_type m_array; + allocator_type *m_allocator; +}; + +template <class T, + int PFS_PAGE_SIZE, + int PFS_PAGE_COUNT, + class U = PFS_buffer_default_array<T>, + class V = PFS_buffer_default_allocator<T> > +class PFS_buffer_scalable_container +{ +public: + friend class PFS_buffer_scalable_iterator<T, PFS_PAGE_SIZE, PFS_PAGE_COUNT, U, V>; + + /** + Type of elements in the buffer. + The following attributes are required: + - pfs_lock m_lock + - PFS_opaque_container_page *m_page + */ + typedef T value_type; + /** + Type of pages in the buffer. + The following attributes are required: + - PFS_opaque_container *m_container + */ + typedef U array_type; + typedef V allocator_type; + /** This container type */ + typedef PFS_buffer_scalable_container<T, PFS_PAGE_SIZE, PFS_PAGE_COUNT, U, V> container_type; + typedef PFS_buffer_const_iterator<T> const_iterator_type; + typedef PFS_buffer_scalable_iterator<T, PFS_PAGE_SIZE, PFS_PAGE_COUNT, U, V> iterator_type; + typedef PFS_buffer_processor<T> processor_type; + typedef void (*function_type)(value_type *); + + static const size_t MAX_SIZE= PFS_PAGE_SIZE*PFS_PAGE_COUNT; + + PFS_buffer_scalable_container(allocator_type *allocator) + { + m_allocator= allocator; + m_initialized= false; + m_lost= 0; + } + + int init(long max_size) + { + int i; + + m_initialized= true; + m_full= true; + m_max= PFS_PAGE_COUNT * PFS_PAGE_SIZE; + m_max_page_count= PFS_PAGE_COUNT; + m_last_page_size= PFS_PAGE_SIZE; + m_lost= 0; + m_monotonic.m_u32= 0; + m_max_page_index.m_u32= 0; + + for (i=0 ; i < PFS_PAGE_COUNT; i++) + { + m_pages[i]= NULL; + } + + if (max_size == 0) + { + /* No allocation. */ + m_max_page_count= 0; + } + else if (max_size > 0) + { + if (max_size % PFS_PAGE_SIZE == 0) + { + m_max_page_count= max_size / PFS_PAGE_SIZE; + } + else + { + m_max_page_count= max_size / PFS_PAGE_SIZE + 1; + m_last_page_size= max_size % PFS_PAGE_SIZE; + } + /* Bounded allocation. */ + m_full= false; + + if (m_max_page_count > PFS_PAGE_COUNT) + { + m_max_page_count= PFS_PAGE_COUNT; + m_last_page_size= PFS_PAGE_SIZE; + } + } + else + { + /* max_size = -1 means unbounded allocation */ + m_full= false; + } + + assert(m_max_page_count <= PFS_PAGE_COUNT); + assert(0 < m_last_page_size); + assert(m_last_page_size <= PFS_PAGE_SIZE); + + pthread_mutex_init(& m_critical_section, NULL); + return 0; + } + + void cleanup() + { + int i; + array_type *page; + + if (! m_initialized) + return; + + pthread_mutex_lock(& m_critical_section); + + for (i=0 ; i < PFS_PAGE_COUNT; i++) + { + page= m_pages[i]; + if (page != NULL) + { + m_allocator->free_array(page); + delete page; + m_pages[i]= NULL; + } + } + pthread_mutex_unlock(& m_critical_section); + + pthread_mutex_destroy(& m_critical_section); + + m_initialized= false; + } + + ulong get_row_count() + { + ulong page_count= PFS_atomic::load_u32(& m_max_page_index.m_u32); + + return page_count * PFS_PAGE_SIZE; + } + + ulong get_row_size() const + { + return sizeof(value_type); + } + + ulong get_memory() + { + return get_row_count() * get_row_size(); + } + + value_type *allocate(pfs_dirty_state *dirty_state) + { + if (m_full) + { + m_lost++; + return NULL; + } + + uint index; + uint monotonic; + uint monotonic_max; + uint current_page_count; + value_type *pfs; + array_type *array; + + void *addr; + void * volatile * typed_addr; + void *ptr; + + /* + 1: Try to find an available record within the existing pages + */ + current_page_count= PFS_atomic::load_u32(& m_max_page_index.m_u32); + + if (current_page_count != 0) + { + monotonic= PFS_atomic::load_u32(& m_monotonic.m_u32); + monotonic_max= monotonic + current_page_count; + + while (monotonic < monotonic_max) + { + /* + Scan in the [0 .. current_page_count - 1] range, + in parallel with m_monotonic (see below) + */ + index= monotonic % current_page_count; + + /* Atomic Load, array= m_pages[index] */ + addr= & m_pages[index]; + typed_addr= static_cast<void * volatile *>(addr); + ptr= my_atomic_loadptr(typed_addr); + array= static_cast<array_type *>(ptr); + + if (array != NULL) + { + pfs= array->allocate(dirty_state); + if (pfs != NULL) + { + /* Keep a pointer to the parent page, for deallocate(). */ + pfs->m_page= reinterpret_cast<PFS_opaque_container_page *> (array); + return pfs; + } + } + + /* + Parallel scans collaborate to increase + the common monotonic scan counter. + + Note that when all the existing page are full, + one thread will eventually add a new page, + and cause m_max_page_index to increase, + which fools all the modulo logic for scans already in progress, + because the monotonic counter is not folded to the same place + (sometime modulo N, sometime modulo N+1). + + This is actually ok: since all the pages are full anyway, + there is nothing to miss, so better increase the monotonic + counter faster and then move on to the detection of new pages, + in part 2: below. + */ + monotonic= PFS_atomic::add_u32(& m_monotonic.m_u32, 1); + }; + } + + /* + 2: Try to add a new page, beyond the m_max_page_index limit + */ + while (current_page_count < m_max_page_count) + { + /* Peek for pages added by collaborating threads */ + + /* (2-a) Atomic Load, array= m_pages[current_page_count] */ + addr= & m_pages[current_page_count]; + typed_addr= static_cast<void * volatile *>(addr); + ptr= my_atomic_loadptr(typed_addr); + array= static_cast<array_type *>(ptr); + + if (array == NULL) + { + // ================================================================== + // BEGIN CRITICAL SECTION -- buffer expand + // ================================================================== + + /* + On a fresh started server, buffers are typically empty. + When a sudden load spike is seen by the server, + multiple threads may want to expand the buffer at the same time. + + Using a compare and swap to allow multiple pages to be added, + possibly freeing duplicate pages on collisions, + does not work well because the amount of code involved + when creating a new page can be significant (PFS_thread), + causing MANY collisions between (2-b) and (2-d). + + A huge number of collisions (which can happen when thousands + of new connections hits the server after a restart) + leads to a huge memory consumption, and to OOM. + + To mitigate this, we use here a mutex, + to enforce that only ONE page is added at a time, + so that scaling the buffer happens in a predictable + and controlled manner. + */ + pthread_mutex_lock(& m_critical_section); + + /* + Peek again for pages added by collaborating threads, + this time as the only thread allowed to expand the buffer + */ + + /* (2-b) Atomic Load, array= m_pages[current_page_count] */ + + ptr= my_atomic_loadptr(typed_addr); + array= static_cast<array_type *>(ptr); + + if (array == NULL) + { + /* (2-c) Found no page, allocate a new one */ + array= new array_type(); + builtin_memory_scalable_buffer.count_alloc(sizeof (array_type)); + + array->m_max= get_page_logical_size(current_page_count); + int rc= m_allocator->alloc_array(array); + if (rc != 0) + { + m_allocator->free_array(array); + delete array; + builtin_memory_scalable_buffer.count_free(sizeof (array_type)); + m_lost++; + pthread_mutex_unlock(& m_critical_section); + return NULL; + } + + /* Keep a pointer to this container, for static_deallocate(). */ + array->m_container= reinterpret_cast<PFS_opaque_container *> (this); + + /* (2-d) Atomic STORE, m_pages[current_page_count] = array */ + ptr= array; + my_atomic_storeptr(typed_addr, ptr); + + /* Advertise the new page */ + PFS_atomic::add_u32(& m_max_page_index.m_u32, 1); + } + + pthread_mutex_unlock(& m_critical_section); + + // ================================================================== + // END CRITICAL SECTION -- buffer expand + // ================================================================== + } + + assert(array != NULL); + pfs= array->allocate(dirty_state); + if (pfs != NULL) + { + /* Keep a pointer to the parent page, for deallocate(). */ + pfs->m_page= reinterpret_cast<PFS_opaque_container_page *> (array); + return pfs; + } + + current_page_count++; + } + + m_lost++; + m_full= true; + return NULL; + } + + void deallocate(value_type *safe_pfs) + { + /* Find the containing page */ + PFS_opaque_container_page *opaque_page= safe_pfs->m_page; + array_type *page= reinterpret_cast<array_type *> (opaque_page); + + /* Mark the object free */ + safe_pfs->m_lock.allocated_to_free(); + + /* Flag the containing page as not full. */ + page->m_full= false; + + /* Flag the overall container as not full. */ + m_full= false; + } + + static void static_deallocate(value_type *safe_pfs) + { + /* Find the containing page */ + PFS_opaque_container_page *opaque_page= safe_pfs->m_page; + array_type *page= reinterpret_cast<array_type *> (opaque_page); + + /* Mark the object free */ + safe_pfs->m_lock.allocated_to_free(); + + /* Flag the containing page as not full. */ + page->m_full= false; + + /* Find the containing buffer */ + PFS_opaque_container *opaque_container= page->m_container; + PFS_buffer_scalable_container *container; + container= reinterpret_cast<container_type *> (opaque_container); + + /* Flag the overall container as not full. */ + container->m_full= false; + } + + iterator_type iterate() + { + return PFS_buffer_scalable_iterator<T, PFS_PAGE_SIZE, PFS_PAGE_COUNT, U, V>(this, 0); + } + + iterator_type iterate(uint index) + { + assert(index <= m_max); + return PFS_buffer_scalable_iterator<T, PFS_PAGE_SIZE, PFS_PAGE_COUNT, U, V>(this, index); + } + + void apply(function_type fct) + { + uint i; + array_type *page; + value_type *pfs; + value_type *pfs_last; + + for (i=0 ; i < PFS_PAGE_COUNT; i++) + { + page= m_pages[i]; + if (page != NULL) + { + pfs= page->get_first(); + pfs_last= page->get_last(); + + while (pfs < pfs_last) + { + if (pfs->m_lock.is_populated()) + { + fct(pfs); + } + pfs++; + } + } + } + } + + void apply_all(function_type fct) + { + uint i; + array_type *page; + value_type *pfs; + value_type *pfs_last; + + for (i=0 ; i < PFS_PAGE_COUNT; i++) + { + page= m_pages[i]; + if (page != NULL) + { + pfs= page->get_first(); + pfs_last= page->get_last(); + + while (pfs < pfs_last) + { + fct(pfs); + pfs++; + } + } + } + } + + void apply(processor_type & proc) + { + uint i; + array_type *page; + value_type *pfs; + value_type *pfs_last; + + for (i=0 ; i < PFS_PAGE_COUNT; i++) + { + page= m_pages[i]; + if (page != NULL) + { + pfs= page->get_first(); + pfs_last= page->get_last(); + + while (pfs < pfs_last) + { + if (pfs->m_lock.is_populated()) + { + proc(pfs); + } + pfs++; + } + } + } + } + + void apply_all(processor_type & proc) + { + uint i; + array_type *page; + value_type *pfs; + value_type *pfs_last; + + for (i=0 ; i < PFS_PAGE_COUNT; i++) + { + page= m_pages[i]; + if (page != NULL) + { + pfs= page->get_first(); + pfs_last= page->get_last(); + + while (pfs < pfs_last) + { + proc(pfs); + pfs++; + } + } + } + } + + value_type* get(uint index) + { + assert(index < m_max); + + uint index_1= index / PFS_PAGE_SIZE; + array_type *page= m_pages[index_1]; + if (page != NULL) + { + uint index_2= index % PFS_PAGE_SIZE; + + if (index_2 >= page->m_max) + { + return NULL; + } + + value_type *pfs= page->m_ptr + index_2; + + if (pfs->m_lock.is_populated()) + { + return pfs; + } + } + + return NULL; + } + + value_type* get(uint index, bool *has_more) + { + if (index >= m_max) + { + *has_more= false; + return NULL; + } + + uint index_1= index / PFS_PAGE_SIZE; + array_type *page= m_pages[index_1]; + + if (page == NULL) + { + *has_more= false; + return NULL; + } + + uint index_2= index % PFS_PAGE_SIZE; + + if (index_2 >= page->m_max) + { + *has_more= false; + return NULL; + } + + *has_more= true; + value_type *pfs= page->m_ptr + index_2; + + if (pfs->m_lock.is_populated()) + { + return pfs; + } + + return NULL; + } + + value_type *sanitize(value_type *unsafe) + { + intptr offset; + uint i; + array_type *page; + value_type *pfs; + value_type *pfs_last; + + for (i=0 ; i < PFS_PAGE_COUNT; i++) + { + page= m_pages[i]; + if (page != NULL) + { + pfs= page->get_first(); + pfs_last= page->get_last(); + + if ((pfs <= unsafe) && + (unsafe < pfs_last)) + { + offset= ((intptr) unsafe - (intptr) pfs) % sizeof(value_type); + if (offset == 0) + return unsafe; + } + } + } + + return NULL; + } + + ulong m_lost; + +private: + + uint get_page_logical_size(uint page_index) + { + if (page_index + 1 < m_max_page_count) + return PFS_PAGE_SIZE; + assert(page_index + 1 == m_max_page_count); + return m_last_page_size; + } + + value_type* scan_next(uint & index, uint * found_index) + { + assert(index <= m_max); + + uint index_1= index / PFS_PAGE_SIZE; + uint index_2= index % PFS_PAGE_SIZE; + array_type *page; + value_type *pfs_first; + value_type *pfs; + value_type *pfs_last; + + while (index_1 < PFS_PAGE_COUNT) + { + page= m_pages[index_1]; + + if (page == NULL) + { + index= static_cast<uint>(m_max); + return NULL; + } + + pfs_first= page->get_first(); + pfs= pfs_first + index_2; + pfs_last= page->get_last(); + + while (pfs < pfs_last) + { + if (pfs->m_lock.is_populated()) + { + uint found= index_1 * PFS_PAGE_SIZE + static_cast<uint>(pfs - pfs_first); + *found_index= found; + index= found + 1; + return pfs; + } + pfs++; + } + + index_1++; + index_2= 0; + } + + index= static_cast<uint>(m_max); + return NULL; + } + + bool m_initialized; + bool m_full; + size_t m_max; + PFS_cacheline_uint32 m_monotonic; + PFS_cacheline_uint32 m_max_page_index; + ulong m_max_page_count; + ulong m_last_page_size; + array_type * m_pages[PFS_PAGE_COUNT]; + allocator_type *m_allocator; + pthread_mutex_t m_critical_section; +}; + +template <class T, class U, class V> +class PFS_buffer_iterator +{ + friend class PFS_buffer_container<T, U, V>; + + typedef T value_type; + typedef PFS_buffer_container<T, U, V> container_type; + +public: + value_type* scan_next() + { + uint unused; + return m_container->scan_next(m_index, & unused); + } + + value_type* scan_next(uint * found_index) + { + return m_container->scan_next(m_index, found_index); + } + +private: + PFS_buffer_iterator(container_type *container, uint index) + : m_container(container), + m_index(index) + {} + + container_type *m_container; + uint m_index; +}; + +template <class T, int page_size, int page_count, class U, class V> +class PFS_buffer_scalable_iterator +{ + friend class PFS_buffer_scalable_container<T, page_size, page_count, U, V>; + + typedef T value_type; + typedef PFS_buffer_scalable_container<T, page_size, page_count, U, V> container_type; + +public: + value_type* scan_next() + { + uint unused; + return m_container->scan_next(m_index, & unused); + } + + value_type* scan_next(uint * found_index) + { + return m_container->scan_next(m_index, found_index); + } + +private: + PFS_buffer_scalable_iterator(container_type *container, uint index) + : m_container(container), + m_index(index) + {} + + container_type *m_container; + uint m_index; +}; + +template <class T> +class PFS_buffer_processor +{ +public: + virtual ~PFS_buffer_processor<T> () + {} + virtual void operator()(T *element) = 0; +}; + +template <class B, int PFS_PARTITION_COUNT> +class PFS_partitioned_buffer_scalable_container +{ +public: + friend class PFS_partitioned_buffer_scalable_iterator<B, PFS_PARTITION_COUNT>; + + typedef typename B::value_type value_type; + typedef typename B::allocator_type allocator_type; + typedef PFS_partitioned_buffer_scalable_iterator<B, PFS_PARTITION_COUNT> iterator_type; + typedef typename B::iterator_type sub_iterator_type; + typedef typename B::processor_type processor_type; + typedef typename B::function_type function_type; + + PFS_partitioned_buffer_scalable_container(allocator_type *allocator) + { + for (int i=0 ; i < PFS_PARTITION_COUNT; i++) + { + m_partitions[i]= new B(allocator); + } + } + + ~PFS_partitioned_buffer_scalable_container() + { + for (int i=0 ; i < PFS_PARTITION_COUNT; i++) + { + delete m_partitions[i]; + } + } + + int init(long max_size) + { + int rc= 0; + // FIXME: we have max_size * PFS_PARTITION_COUNT here + for (int i=0 ; i < PFS_PARTITION_COUNT; i++) + { + rc|= m_partitions[i]->init(max_size); + } + return rc; + } + + void cleanup() + { + for (int i=0 ; i < PFS_PARTITION_COUNT; i++) + { + m_partitions[i]->cleanup(); + } + } + + ulong get_row_count() const + { + ulong sum= 0; + + for (int i=0; i < PFS_PARTITION_COUNT; i++) + { + sum += m_partitions[i]->get_row_count(); + } + + return sum; + } + + ulong get_row_size() const + { + return sizeof(value_type); + } + + ulong get_memory() const + { + ulong sum= 0; + + for (int i=0; i < PFS_PARTITION_COUNT; i++) + { + sum += m_partitions[i]->get_memory(); + } + + return sum; + } + + long get_lost_counter() + { + long sum= 0; + + for (int i=0; i < PFS_PARTITION_COUNT; i++) + { + sum += m_partitions[i]->m_lost; + } + + return sum; + } + + value_type *allocate(pfs_dirty_state *dirty_state, uint partition) + { + assert(partition < PFS_PARTITION_COUNT); + + return m_partitions[partition]->allocate(dirty_state); + } + + void deallocate(value_type *safe_pfs) + { + /* + One issue here is that we do not know which partition + the record belongs to. + Each record points to the parent page, + and each page points to the parent buffer, + so using static_deallocate here, + which will find the correct partition by itself. + */ + B::static_deallocate(safe_pfs); + } + + iterator_type iterate() + { + return iterator_type(this, 0, 0); + } + + iterator_type iterate(uint user_index) + { + uint partition_index; + uint sub_index; + unpack_index(user_index, &partition_index, &sub_index); + return iterator_type(this, partition_index, sub_index); + } + + void apply(function_type fct) + { + for (int i=0; i < PFS_PARTITION_COUNT; i++) + { + m_partitions[i]->apply(fct); + } + } + + void apply_all(function_type fct) + { + for (int i=0; i < PFS_PARTITION_COUNT; i++) + { + m_partitions[i]->apply_all(fct); + } + } + + void apply(processor_type & proc) + { + for (int i=0; i < PFS_PARTITION_COUNT; i++) + { + m_partitions[i]->apply(proc); + } + } + + void apply_all(processor_type & proc) + { + for (int i=0; i < PFS_PARTITION_COUNT; i++) + { + m_partitions[i]->apply_all(proc); + } + } + + value_type* get(uint user_index) + { + uint partition_index; + uint sub_index; + unpack_index(user_index, &partition_index, &sub_index); + + if (partition_index >= PFS_PARTITION_COUNT) + { + return NULL; + } + + return m_partitions[partition_index]->get(sub_index); + } + + value_type* get(uint user_index, bool *has_more) + { + uint partition_index; + uint sub_index; + unpack_index(user_index, &partition_index, &sub_index); + + if (partition_index >= PFS_PARTITION_COUNT) + { + *has_more= false; + return NULL; + } + + *has_more= true; + return m_partitions[partition_index]->get(sub_index); + } + + value_type *sanitize(value_type *unsafe) + { + value_type *safe= NULL; + + for (int i=0; i < PFS_PARTITION_COUNT; i++) + { + safe= m_partitions[i]->sanitize(unsafe); + if (safe != NULL) + { + return safe; + } + } + + return safe; + } + +private: + static void pack_index(uint partition_index, uint sub_index, uint *user_index) + { + /* 2^8 = 256 partitions max */ + compile_time_assert(PFS_PARTITION_COUNT <= (1 << 8)); + /* 2^24 = 16777216 max per partitioned buffer. */ + compile_time_assert((B::MAX_SIZE) <= (1 << 24)); + + *user_index= (partition_index << 24) + sub_index; + } + + static void unpack_index(uint user_index, uint *partition_index, uint *sub_index) + { + *partition_index= user_index >> 24; + *sub_index= user_index & 0x00FFFFFF; + } + + value_type* scan_next(uint & partition_index, uint & sub_index, uint * found_partition, uint * found_sub_index) + { + value_type *record= NULL; + assert(partition_index < PFS_PARTITION_COUNT); + + while (partition_index < PFS_PARTITION_COUNT) + { + sub_iterator_type sub_iterator= m_partitions[partition_index]->iterate(sub_index); + record= sub_iterator.scan_next(found_sub_index); + if (record != NULL) + { + *found_partition= partition_index; + sub_index= *found_sub_index + 1; + return record; + } + + partition_index++; + sub_index= 0; + } + + *found_partition= PFS_PARTITION_COUNT; + *found_sub_index= 0; + sub_index= 0; + return NULL; + } + + B *m_partitions[PFS_PARTITION_COUNT]; +}; + +template <class B, int PFS_PARTITION_COUNT> +class PFS_partitioned_buffer_scalable_iterator +{ +public: + friend class PFS_partitioned_buffer_scalable_container<B, PFS_PARTITION_COUNT>; + + typedef typename B::value_type value_type; + typedef PFS_partitioned_buffer_scalable_container<B, PFS_PARTITION_COUNT> container_type; + + value_type* scan_next() + { + uint unused_partition; + uint unused_sub_index; + return m_container->scan_next(m_partition, m_sub_index, & unused_partition, & unused_sub_index); + } + + value_type* scan_next(uint *found_user_index) + { + uint found_partition; + uint found_sub_index; + value_type *record; + record= m_container->scan_next(m_partition, m_sub_index, &found_partition, &found_sub_index); + container_type::pack_index(found_partition, found_sub_index, found_user_index); + return record; + } + +private: + PFS_partitioned_buffer_scalable_iterator(container_type *container, uint partition, uint sub_index) + : m_container(container), + m_partition(partition), + m_sub_index(sub_index) + {} + + container_type *m_container; + uint m_partition; + uint m_sub_index; +}; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_mutex, 1024, 1024> PFS_mutex_basic_container; +typedef PFS_partitioned_buffer_scalable_container<PFS_mutex_basic_container, PSI_COUNT_VOLATILITY> PFS_mutex_container; +#else +typedef PFS_buffer_container<PFS_mutex> PFS_mutex_container; +#endif +typedef PFS_mutex_container::iterator_type PFS_mutex_iterator; +extern PFS_mutex_container global_mutex_container; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_rwlock, 1024, 1024> PFS_rwlock_container; +#else +typedef PFS_buffer_container<PFS_rwlock> PFS_rwlock_container; +#endif +typedef PFS_rwlock_container::iterator_type PFS_rwlock_iterator; +extern PFS_rwlock_container global_rwlock_container; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_cond, 256, 256> PFS_cond_container; +#else +typedef PFS_buffer_container<PFS_cond> PFS_cond_container; +#endif +typedef PFS_cond_container::iterator_type PFS_cond_iterator; +extern PFS_cond_container global_cond_container; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_file, 4 * 1024, 4 * 1024> PFS_file_container; +#else +typedef PFS_buffer_container<PFS_file> PFS_file_container; +#endif +typedef PFS_file_container::iterator_type PFS_file_iterator; +extern PFS_file_container global_file_container; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_socket, 256, 256> PFS_socket_container; +#else +typedef PFS_buffer_container<PFS_socket> PFS_socket_container; +#endif +typedef PFS_socket_container::iterator_type PFS_socket_iterator; +extern PFS_socket_container global_socket_container; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_metadata_lock, 1024, 1024> PFS_mdl_container; +#else +typedef PFS_buffer_container<PFS_metadata_lock> PFS_mdl_container; +#endif +typedef PFS_mdl_container::iterator_type PFS_mdl_iterator; +extern PFS_mdl_container global_mdl_container; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_setup_actor, 128, 1024> PFS_setup_actor_container; +#else +typedef PFS_buffer_container<PFS_setup_actor> PFS_setup_actor_container; +#endif +typedef PFS_setup_actor_container::iterator_type PFS_setup_actor_iterator; +extern PFS_setup_actor_container global_setup_actor_container; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_setup_object, 128, 1024> PFS_setup_object_container; +#else +typedef PFS_buffer_container<PFS_setup_object> PFS_setup_object_container; +#endif +typedef PFS_setup_object_container::iterator_type PFS_setup_object_iterator; +extern PFS_setup_object_container global_setup_object_container; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_table, 1024, 1024> PFS_table_container; +#else +typedef PFS_buffer_container<PFS_table> PFS_table_container; +#endif +typedef PFS_table_container::iterator_type PFS_table_iterator; +extern PFS_table_container global_table_container; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_table_share, 4 * 1024, 4 * 1024> PFS_table_share_container; +#else +typedef PFS_buffer_container<PFS_table_share> PFS_table_share_container; +#endif +typedef PFS_table_share_container::iterator_type PFS_table_share_iterator; +extern PFS_table_share_container global_table_share_container; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_table_share_index, 8 * 1024, 8 * 1024> PFS_table_share_index_container; +#else +typedef PFS_buffer_container<PFS_table_share_index> PFS_table_share_index_container; +#endif +typedef PFS_table_share_index_container::iterator_type PFS_table_share_index_iterator; +extern PFS_table_share_index_container global_table_share_index_container; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_table_share_lock, 4 * 1024, 4 * 1024> PFS_table_share_lock_container; +#else +typedef PFS_buffer_container<PFS_table_share_lock> PFS_table_share_lock_container; +#endif +typedef PFS_table_share_lock_container::iterator_type PFS_table_share_lock_iterator; +extern PFS_table_share_lock_container global_table_share_lock_container; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_program, 1024, 1024> PFS_program_container; +#else +typedef PFS_buffer_container<PFS_program> PFS_program_container; +#endif +typedef PFS_program_container::iterator_type PFS_program_iterator; +extern PFS_program_container global_program_container; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_prepared_stmt, 1024, 1024> PFS_prepared_stmt_container; +#else +typedef PFS_buffer_container<PFS_prepared_stmt> PFS_prepared_stmt_container; +#endif +typedef PFS_prepared_stmt_container::iterator_type PFS_prepared_stmt_iterator; +extern PFS_prepared_stmt_container global_prepared_stmt_container; + +class PFS_account_array : public PFS_buffer_default_array<PFS_account> +{ +public: + PFS_single_stat *m_instr_class_waits_array; + PFS_stage_stat *m_instr_class_stages_array; + PFS_statement_stat *m_instr_class_statements_array; + PFS_transaction_stat *m_instr_class_transactions_array; + PFS_memory_stat *m_instr_class_memory_array; +}; + +class PFS_account_allocator +{ +public: + int alloc_array(PFS_account_array *array); + void free_array(PFS_account_array *array); +}; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_account, + 128, + 128, + PFS_account_array, + PFS_account_allocator> PFS_account_container; +#else +typedef PFS_buffer_container<PFS_account, + PFS_account_array, + PFS_account_allocator> PFS_account_container; +#endif +typedef PFS_account_container::iterator_type PFS_account_iterator; +extern PFS_account_container global_account_container; + +class PFS_host_array : public PFS_buffer_default_array<PFS_host> +{ +public: + PFS_single_stat *m_instr_class_waits_array; + PFS_stage_stat *m_instr_class_stages_array; + PFS_statement_stat *m_instr_class_statements_array; + PFS_transaction_stat *m_instr_class_transactions_array; + PFS_memory_stat *m_instr_class_memory_array; +}; + +class PFS_host_allocator +{ +public: + int alloc_array(PFS_host_array *array); + void free_array(PFS_host_array *array); +}; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_host, + 128, + 128, + PFS_host_array, + PFS_host_allocator> PFS_host_container; +#else +typedef PFS_buffer_container<PFS_host, + PFS_host_array, + PFS_host_allocator> PFS_host_container; +#endif +typedef PFS_host_container::iterator_type PFS_host_iterator; +extern PFS_host_container global_host_container; + +class PFS_thread_array : public PFS_buffer_default_array<PFS_thread> +{ +public: + PFS_single_stat *m_instr_class_waits_array; + PFS_stage_stat *m_instr_class_stages_array; + PFS_statement_stat *m_instr_class_statements_array; + PFS_transaction_stat *m_instr_class_transactions_array; + PFS_memory_stat *m_instr_class_memory_array; + + PFS_events_waits *m_waits_history_array; + PFS_events_stages *m_stages_history_array; + PFS_events_statements *m_statements_history_array; + PFS_events_statements *m_statements_stack_array; + PFS_events_transactions *m_transactions_history_array; + char *m_session_connect_attrs_array; + + char *m_current_stmts_text_array; + char *m_history_stmts_text_array; + unsigned char *m_current_stmts_digest_token_array; + unsigned char *m_history_stmts_digest_token_array; +}; + +class PFS_thread_allocator +{ +public: + int alloc_array(PFS_thread_array *array); + void free_array(PFS_thread_array *array); +}; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_thread, + 256, + 256, + PFS_thread_array, + PFS_thread_allocator> PFS_thread_container; +#else +typedef PFS_buffer_container<PFS_thread, + PFS_thread_array, + PFS_thread_allocator> PFS_thread_container; +#endif +typedef PFS_thread_container::iterator_type PFS_thread_iterator; +extern PFS_thread_container global_thread_container; + +class PFS_user_array : public PFS_buffer_default_array<PFS_user> +{ +public: + PFS_single_stat *m_instr_class_waits_array; + PFS_stage_stat *m_instr_class_stages_array; + PFS_statement_stat *m_instr_class_statements_array; + PFS_transaction_stat *m_instr_class_transactions_array; + PFS_memory_stat *m_instr_class_memory_array; +}; + +class PFS_user_allocator +{ +public: + int alloc_array(PFS_user_array *array); + void free_array(PFS_user_array *array); +}; + +#ifdef USE_SCALABLE +typedef PFS_buffer_scalable_container<PFS_user, + 128, + 128, + PFS_user_array, + PFS_user_allocator> PFS_user_container; +#else +typedef PFS_buffer_container<PFS_user, + PFS_user_array, + PFS_user_allocator> PFS_user_container; +#endif +typedef PFS_user_container::iterator_type PFS_user_iterator; +extern PFS_user_container global_user_container; + +#endif + diff --git a/storage/perfschema/pfs_builtin_memory.cc b/storage/perfschema/pfs_builtin_memory.cc new file mode 100644 index 00000000..c8b9e62f --- /dev/null +++ b/storage/perfschema/pfs_builtin_memory.cc @@ -0,0 +1,382 @@ +/* Copyright (c) 2014, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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 */ + +#include "my_global.h" +#include "m_string.h" +#include "pfs_global.h" +#include "pfs_builtin_memory.h" + +PFS_builtin_memory_class builtin_memory_mutex; +PFS_builtin_memory_class builtin_memory_rwlock; +PFS_builtin_memory_class builtin_memory_cond; +PFS_builtin_memory_class builtin_memory_file; +PFS_builtin_memory_class builtin_memory_socket; +PFS_builtin_memory_class builtin_memory_mdl; +PFS_builtin_memory_class builtin_memory_file_handle; + +PFS_builtin_memory_class builtin_memory_account; +PFS_builtin_memory_class builtin_memory_account_waits; +PFS_builtin_memory_class builtin_memory_account_stages; +PFS_builtin_memory_class builtin_memory_account_statements; +PFS_builtin_memory_class builtin_memory_account_transactions; +PFS_builtin_memory_class builtin_memory_account_memory; + +PFS_builtin_memory_class builtin_memory_global_stages; +PFS_builtin_memory_class builtin_memory_global_statements; +PFS_builtin_memory_class builtin_memory_global_memory; + +PFS_builtin_memory_class builtin_memory_host; +PFS_builtin_memory_class builtin_memory_host_waits; +PFS_builtin_memory_class builtin_memory_host_stages; +PFS_builtin_memory_class builtin_memory_host_statements; +PFS_builtin_memory_class builtin_memory_host_transactions; +PFS_builtin_memory_class builtin_memory_host_memory; + +PFS_builtin_memory_class builtin_memory_thread; +PFS_builtin_memory_class builtin_memory_thread_waits; +PFS_builtin_memory_class builtin_memory_thread_stages; +PFS_builtin_memory_class builtin_memory_thread_statements; +PFS_builtin_memory_class builtin_memory_thread_transactions; +PFS_builtin_memory_class builtin_memory_thread_memory; + +PFS_builtin_memory_class builtin_memory_thread_waits_history; +PFS_builtin_memory_class builtin_memory_thread_stages_history; +PFS_builtin_memory_class builtin_memory_thread_statements_history; +PFS_builtin_memory_class builtin_memory_thread_statements_history_tokens; +PFS_builtin_memory_class builtin_memory_thread_statements_history_sqltext; +PFS_builtin_memory_class builtin_memory_thread_statements_stack; +PFS_builtin_memory_class builtin_memory_thread_statements_stack_tokens; +PFS_builtin_memory_class builtin_memory_thread_statements_stack_sqltext; +PFS_builtin_memory_class builtin_memory_thread_transaction_history; +PFS_builtin_memory_class builtin_memory_thread_session_connect_attrs; + +PFS_builtin_memory_class builtin_memory_user; +PFS_builtin_memory_class builtin_memory_user_waits; +PFS_builtin_memory_class builtin_memory_user_stages; +PFS_builtin_memory_class builtin_memory_user_statements; +PFS_builtin_memory_class builtin_memory_user_transactions; +PFS_builtin_memory_class builtin_memory_user_memory; + +PFS_builtin_memory_class builtin_memory_mutex_class; +PFS_builtin_memory_class builtin_memory_rwlock_class; +PFS_builtin_memory_class builtin_memory_cond_class; +PFS_builtin_memory_class builtin_memory_thread_class; +PFS_builtin_memory_class builtin_memory_file_class; +PFS_builtin_memory_class builtin_memory_socket_class; +PFS_builtin_memory_class builtin_memory_stage_class; +PFS_builtin_memory_class builtin_memory_statement_class; +PFS_builtin_memory_class builtin_memory_memory_class; + +PFS_builtin_memory_class builtin_memory_setup_actor; +PFS_builtin_memory_class builtin_memory_setup_object; + +PFS_builtin_memory_class builtin_memory_digest; +PFS_builtin_memory_class builtin_memory_digest_tokens; + +PFS_builtin_memory_class builtin_memory_stages_history_long; +PFS_builtin_memory_class builtin_memory_statements_history_long; +PFS_builtin_memory_class builtin_memory_statements_history_long_tokens; +PFS_builtin_memory_class builtin_memory_statements_history_long_sqltext; +PFS_builtin_memory_class builtin_memory_transactions_history_long; +PFS_builtin_memory_class builtin_memory_waits_history_long; + +PFS_builtin_memory_class builtin_memory_table; +PFS_builtin_memory_class builtin_memory_table_share; +PFS_builtin_memory_class builtin_memory_table_share_index; +PFS_builtin_memory_class builtin_memory_table_share_lock; + +PFS_builtin_memory_class builtin_memory_program; +PFS_builtin_memory_class builtin_memory_prepared_stmt; + +PFS_builtin_memory_class builtin_memory_scalable_buffer; + +static void init_builtin_memory_class(PFS_builtin_memory_class *klass, const char* name) +{ + klass->m_class.m_type= PFS_CLASS_MEMORY; + klass->m_class.m_enabled= true; /* Immutable */ + klass->m_class.m_timed= false; /* Immutable */ + klass->m_class.m_flags= PSI_FLAG_GLOBAL; + klass->m_class.m_event_name_index= 0; + my_snprintf(klass->m_class.m_name, sizeof(klass->m_class.m_name), "%.*s", + PFS_MAX_INFO_NAME_LENGTH - 1, name); + klass->m_class.m_name_length= static_cast<uint>(strlen(name)); + DBUG_ASSERT(klass->m_class.m_name_length < sizeof(klass->m_class.m_name)); + klass->m_class.m_timer= NULL; + + klass->m_stat.reset(); +} + +void init_all_builtin_memory_class() +{ + init_builtin_memory_class( & builtin_memory_mutex, + "memory/performance_schema/mutex_instances"); + init_builtin_memory_class( & builtin_memory_rwlock, + "memory/performance_schema/rwlock_instances"); + init_builtin_memory_class( & builtin_memory_cond, + "memory/performance_schema/cond_instances"); + init_builtin_memory_class( & builtin_memory_file, + "memory/performance_schema/file_instances"); + init_builtin_memory_class( & builtin_memory_socket, + "memory/performance_schema/socket_instances"); + init_builtin_memory_class( & builtin_memory_mdl, + "memory/performance_schema/metadata_locks"); + init_builtin_memory_class( & builtin_memory_file_handle, + "memory/performance_schema/file_handle"); + + init_builtin_memory_class( & builtin_memory_account, + "memory/performance_schema/accounts"); + init_builtin_memory_class( & builtin_memory_account_waits, + "memory/performance_schema/events_waits_summary_by_account_by_event_name"); + init_builtin_memory_class( & builtin_memory_account_stages, + "memory/performance_schema/events_stages_summary_by_account_by_event_name"); + init_builtin_memory_class( & builtin_memory_account_statements, + "memory/performance_schema/events_statements_summary_by_account_by_event_name"); + init_builtin_memory_class( & builtin_memory_account_transactions, + "memory/performance_schema/events_transactions_summary_by_account_by_event_name"); + init_builtin_memory_class( & builtin_memory_account_memory, + "memory/performance_schema/memory_summary_by_account_by_event_name"); + + init_builtin_memory_class( & builtin_memory_global_stages, + "memory/performance_schema/events_stages_summary_global_by_event_name"); + init_builtin_memory_class( & builtin_memory_global_statements, + "memory/performance_schema/events_statements_summary_global_by_event_name"); + init_builtin_memory_class( & builtin_memory_global_memory, + "memory/performance_schema/memory_summary_global_by_event_name"); + + init_builtin_memory_class( & builtin_memory_host, + "memory/performance_schema/hosts"); + init_builtin_memory_class( & builtin_memory_host_waits, + "memory/performance_schema/events_waits_summary_by_host_by_event_name"); + init_builtin_memory_class( & builtin_memory_host_stages, + "memory/performance_schema/events_stages_summary_by_host_by_event_name"); + init_builtin_memory_class( & builtin_memory_host_statements, + "memory/performance_schema/events_statements_summary_by_host_by_event_name"); + init_builtin_memory_class( & builtin_memory_host_transactions, + "memory/performance_schema/events_transactions_summary_by_host_by_event_name"); + init_builtin_memory_class( & builtin_memory_host_memory, + "memory/performance_schema/memory_summary_by_host_by_event_name"); + + init_builtin_memory_class( & builtin_memory_thread, + "memory/performance_schema/threads"); + init_builtin_memory_class( & builtin_memory_thread_waits, + "memory/performance_schema/events_waits_summary_by_thread_by_event_name"); + init_builtin_memory_class( & builtin_memory_thread_stages, + "memory/performance_schema/events_stages_summary_by_thread_by_event_name"); + init_builtin_memory_class( & builtin_memory_thread_statements, + "memory/performance_schema/events_statements_summary_by_thread_by_event_name"); + init_builtin_memory_class( & builtin_memory_thread_transactions, + "memory/performance_schema/events_transactions_summary_by_thread_by_event_name"); + init_builtin_memory_class( & builtin_memory_thread_memory, + "memory/performance_schema/memory_summary_by_thread_by_event_name"); + + init_builtin_memory_class( & builtin_memory_thread_waits_history, + "memory/performance_schema/events_waits_history"); + init_builtin_memory_class( & builtin_memory_thread_stages_history, + "memory/performance_schema/events_stages_history"); + init_builtin_memory_class( & builtin_memory_thread_statements_history, + "memory/performance_schema/events_statements_history"); + init_builtin_memory_class( & builtin_memory_thread_statements_history_tokens, + "memory/performance_schema/events_statements_history.tokens"); + init_builtin_memory_class( & builtin_memory_thread_statements_history_sqltext, + "memory/performance_schema/events_statements_history.sqltext"); + init_builtin_memory_class( & builtin_memory_thread_statements_stack, + "memory/performance_schema/events_statements_current"); + init_builtin_memory_class( & builtin_memory_thread_statements_stack_tokens, + "memory/performance_schema/events_statements_current.tokens"); + init_builtin_memory_class( & builtin_memory_thread_statements_stack_sqltext, + "memory/performance_schema/events_statements_current.sqltext"); + init_builtin_memory_class( & builtin_memory_thread_transaction_history, + "memory/performance_schema/events_transactions_history"); + init_builtin_memory_class( & builtin_memory_thread_session_connect_attrs, + "memory/performance_schema/session_connect_attrs"); + + init_builtin_memory_class( & builtin_memory_user, + "memory/performance_schema/users"); + init_builtin_memory_class( & builtin_memory_user_waits, + "memory/performance_schema/events_waits_summary_by_user_by_event_name"); + init_builtin_memory_class( & builtin_memory_user_stages, + "memory/performance_schema/events_stages_summary_by_user_by_event_name"); + init_builtin_memory_class( & builtin_memory_user_statements, + "memory/performance_schema/events_statements_summary_by_user_by_event_name"); + init_builtin_memory_class( & builtin_memory_user_transactions, + "memory/performance_schema/events_transactions_summary_by_user_by_event_name"); + init_builtin_memory_class( & builtin_memory_user_memory, + "memory/performance_schema/memory_summary_by_user_by_event_name"); + + init_builtin_memory_class( & builtin_memory_mutex_class, + "memory/performance_schema/mutex_class"); + init_builtin_memory_class( & builtin_memory_rwlock_class, + "memory/performance_schema/rwlock_class"); + init_builtin_memory_class( & builtin_memory_cond_class, + "memory/performance_schema/cond_class"); + init_builtin_memory_class( & builtin_memory_thread_class, + "memory/performance_schema/thread_class"); + init_builtin_memory_class( & builtin_memory_file_class, + "memory/performance_schema/file_class"); + init_builtin_memory_class( & builtin_memory_socket_class, + "memory/performance_schema/socket_class"); + init_builtin_memory_class( & builtin_memory_stage_class, + "memory/performance_schema/stage_class"); + init_builtin_memory_class( & builtin_memory_statement_class, + "memory/performance_schema/statement_class"); + init_builtin_memory_class( & builtin_memory_memory_class, + "memory/performance_schema/memory_class"); + + init_builtin_memory_class( & builtin_memory_setup_actor, + "memory/performance_schema/setup_actors"); + init_builtin_memory_class( & builtin_memory_setup_object, + "memory/performance_schema/setup_objects"); + + init_builtin_memory_class( & builtin_memory_digest, + "memory/performance_schema/events_statements_summary_by_digest"); + init_builtin_memory_class( & builtin_memory_digest_tokens, + "memory/performance_schema/events_statements_summary_by_digest.tokens"); + + init_builtin_memory_class( & builtin_memory_stages_history_long, + "memory/performance_schema/events_stages_history_long"); + init_builtin_memory_class( & builtin_memory_statements_history_long, + "memory/performance_schema/events_statements_history_long"); + init_builtin_memory_class( & builtin_memory_statements_history_long_tokens, + "memory/performance_schema/events_statements_history_long.tokens"); + init_builtin_memory_class( & builtin_memory_statements_history_long_sqltext, + "memory/performance_schema/events_statements_history_long.sqltext"); + init_builtin_memory_class( & builtin_memory_transactions_history_long, + "memory/performance_schema/events_transactions_history_long"); + init_builtin_memory_class( & builtin_memory_waits_history_long, + "memory/performance_schema/events_waits_history_long"); + + init_builtin_memory_class( & builtin_memory_table, + "memory/performance_schema/table_handles"); + init_builtin_memory_class( & builtin_memory_table_share, + "memory/performance_schema/table_shares"); + init_builtin_memory_class( & builtin_memory_table_share_index, + "memory/performance_schema/table_io_waits_summary_by_index_usage"); + init_builtin_memory_class( & builtin_memory_table_share_lock, + "memory/performance_schema/table_lock_waits_summary_by_table"); + + init_builtin_memory_class( & builtin_memory_program, + "memory/performance_schema/events_statements_summary_by_program"); + init_builtin_memory_class( & builtin_memory_prepared_stmt, + "memory/performance_schema/prepared_statements_instances"); + + init_builtin_memory_class( & builtin_memory_scalable_buffer, + "memory/performance_schema/scalable_buffer"); +} + +static PFS_builtin_memory_class* all_builtin_memory[]= +{ + & builtin_memory_mutex, + & builtin_memory_rwlock, + & builtin_memory_cond, + & builtin_memory_file, + & builtin_memory_socket, + & builtin_memory_mdl, + & builtin_memory_file_handle, + + & builtin_memory_account, + & builtin_memory_account_waits, + & builtin_memory_account_stages, + & builtin_memory_account_statements, + & builtin_memory_account_transactions, + & builtin_memory_account_memory, + + & builtin_memory_global_stages, + & builtin_memory_global_statements, + & builtin_memory_global_memory, + + & builtin_memory_host, + & builtin_memory_host_waits, + & builtin_memory_host_stages, + & builtin_memory_host_statements, + & builtin_memory_host_transactions, + & builtin_memory_host_memory, + + & builtin_memory_thread, + & builtin_memory_thread_waits, + & builtin_memory_thread_stages, + & builtin_memory_thread_statements, + & builtin_memory_thread_transactions, + & builtin_memory_thread_memory, + + & builtin_memory_thread_waits_history, + & builtin_memory_thread_stages_history, + & builtin_memory_thread_statements_history, + & builtin_memory_thread_statements_history_tokens, + & builtin_memory_thread_statements_history_sqltext, + & builtin_memory_thread_statements_stack, + & builtin_memory_thread_statements_stack_tokens, + & builtin_memory_thread_statements_stack_sqltext, + & builtin_memory_thread_transaction_history, + & builtin_memory_thread_session_connect_attrs, + + & builtin_memory_user, + & builtin_memory_user_waits, + & builtin_memory_user_stages, + & builtin_memory_user_statements, + & builtin_memory_user_transactions, + & builtin_memory_user_memory, + + & builtin_memory_mutex_class, + & builtin_memory_rwlock_class, + & builtin_memory_cond_class, + & builtin_memory_thread_class, + & builtin_memory_file_class, + & builtin_memory_socket_class, + & builtin_memory_stage_class, + & builtin_memory_statement_class, + & builtin_memory_memory_class, + + & builtin_memory_setup_actor, + & builtin_memory_setup_object, + + & builtin_memory_digest, + & builtin_memory_digest_tokens, + + & builtin_memory_stages_history_long, + & builtin_memory_statements_history_long, + & builtin_memory_statements_history_long_tokens, + & builtin_memory_statements_history_long_sqltext, + & builtin_memory_transactions_history_long, + & builtin_memory_waits_history_long, + + & builtin_memory_table, + & builtin_memory_table_share, + & builtin_memory_table_share_index, + & builtin_memory_table_share_lock, + + & builtin_memory_program, + & builtin_memory_prepared_stmt, + + & builtin_memory_scalable_buffer, + + NULL +}; + + +PFS_builtin_memory_class *find_builtin_memory_class(PFS_builtin_memory_key key) +{ + if (key == 0) + return NULL; + + return all_builtin_memory[key - 1]; +} + diff --git a/storage/perfschema/pfs_builtin_memory.h b/storage/perfschema/pfs_builtin_memory.h new file mode 100644 index 00000000..956b31c5 --- /dev/null +++ b/storage/perfschema/pfs_builtin_memory.h @@ -0,0 +1,143 @@ +/* Copyright (c) 2014, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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_BUILTIN_MEMORY_H +#define PFS_BUILTIN_MEMORY_H + +#include "my_global.h" +#include "pfs_global.h" +#include "pfs_instr_class.h" + +/** + @file storage/perfschema/pfs_builtin_memory.h + Performance schema instruments meta data (declarations). +*/ + +typedef uint PFS_builtin_memory_key; + +struct PFS_builtin_memory_class +{ + PFS_memory_class m_class; + PFS_memory_stat m_stat; + + inline void count_alloc(size_t size) + { + m_stat.count_builtin_alloc(size); + } + + inline void count_free(size_t size) + { + m_stat.count_builtin_free(size); + } +}; + +void init_all_builtin_memory_class(); + +PFS_builtin_memory_class *find_builtin_memory_class(PFS_builtin_memory_key); + +extern PFS_builtin_memory_class builtin_memory_mutex; +extern PFS_builtin_memory_class builtin_memory_rwlock; +extern PFS_builtin_memory_class builtin_memory_cond; +extern PFS_builtin_memory_class builtin_memory_file; +extern PFS_builtin_memory_class builtin_memory_socket; +extern PFS_builtin_memory_class builtin_memory_mdl; +extern PFS_builtin_memory_class builtin_memory_file_handle; + +extern PFS_builtin_memory_class builtin_memory_account; +extern PFS_builtin_memory_class builtin_memory_account_waits; +extern PFS_builtin_memory_class builtin_memory_account_stages; +extern PFS_builtin_memory_class builtin_memory_account_statements; +extern PFS_builtin_memory_class builtin_memory_account_transactions; +extern PFS_builtin_memory_class builtin_memory_account_memory; + +extern PFS_builtin_memory_class builtin_memory_global_stages; +extern PFS_builtin_memory_class builtin_memory_global_statements; +extern PFS_builtin_memory_class builtin_memory_global_memory; + +extern PFS_builtin_memory_class builtin_memory_host; +extern PFS_builtin_memory_class builtin_memory_host_waits; +extern PFS_builtin_memory_class builtin_memory_host_stages; +extern PFS_builtin_memory_class builtin_memory_host_statements; +extern PFS_builtin_memory_class builtin_memory_host_transactions; +extern PFS_builtin_memory_class builtin_memory_host_memory; + +extern PFS_builtin_memory_class builtin_memory_thread; +extern PFS_builtin_memory_class builtin_memory_thread_waits; +extern PFS_builtin_memory_class builtin_memory_thread_stages; +extern PFS_builtin_memory_class builtin_memory_thread_statements; +extern PFS_builtin_memory_class builtin_memory_thread_transactions; +extern PFS_builtin_memory_class builtin_memory_thread_memory; + +extern PFS_builtin_memory_class builtin_memory_thread_waits_history; +extern PFS_builtin_memory_class builtin_memory_thread_stages_history; +extern PFS_builtin_memory_class builtin_memory_thread_statements_history; +extern PFS_builtin_memory_class builtin_memory_thread_statements_history_tokens; +extern PFS_builtin_memory_class builtin_memory_thread_statements_history_sqltext; +extern PFS_builtin_memory_class builtin_memory_thread_statements_stack; +extern PFS_builtin_memory_class builtin_memory_thread_statements_stack_tokens; +extern PFS_builtin_memory_class builtin_memory_thread_statements_stack_sqltext; +extern PFS_builtin_memory_class builtin_memory_thread_transaction_history; +extern PFS_builtin_memory_class builtin_memory_thread_session_connect_attrs; + +extern PFS_builtin_memory_class builtin_memory_user; +extern PFS_builtin_memory_class builtin_memory_user_waits; +extern PFS_builtin_memory_class builtin_memory_user_stages; +extern PFS_builtin_memory_class builtin_memory_user_statements; +extern PFS_builtin_memory_class builtin_memory_user_transactions; +extern PFS_builtin_memory_class builtin_memory_user_memory; + +extern PFS_builtin_memory_class builtin_memory_mutex_class; +extern PFS_builtin_memory_class builtin_memory_rwlock_class; +extern PFS_builtin_memory_class builtin_memory_cond_class; +extern PFS_builtin_memory_class builtin_memory_thread_class; +extern PFS_builtin_memory_class builtin_memory_file_class; +extern PFS_builtin_memory_class builtin_memory_socket_class; +extern PFS_builtin_memory_class builtin_memory_stage_class; +extern PFS_builtin_memory_class builtin_memory_statement_class; +extern PFS_builtin_memory_class builtin_memory_memory_class; + +extern PFS_builtin_memory_class builtin_memory_setup_actor; +extern PFS_builtin_memory_class builtin_memory_setup_object; + +extern PFS_builtin_memory_class builtin_memory_digest; +extern PFS_builtin_memory_class builtin_memory_digest_tokens; + +extern PFS_builtin_memory_class builtin_memory_stages_history_long; +extern PFS_builtin_memory_class builtin_memory_statements_history_long; +extern PFS_builtin_memory_class builtin_memory_statements_history_long_tokens; +extern PFS_builtin_memory_class builtin_memory_statements_history_long_sqltext; +extern PFS_builtin_memory_class builtin_memory_transactions_history_long; +extern PFS_builtin_memory_class builtin_memory_waits_history_long; + +extern PFS_builtin_memory_class builtin_memory_table; +extern PFS_builtin_memory_class builtin_memory_table_share; +extern PFS_builtin_memory_class builtin_memory_table_share_index; +extern PFS_builtin_memory_class builtin_memory_table_share_lock; + +extern PFS_builtin_memory_class builtin_memory_program; +extern PFS_builtin_memory_class builtin_memory_prepared_stmt; + +extern PFS_builtin_memory_class builtin_memory_scalable_buffer; + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_column_types.h b/storage/perfschema/pfs_column_types.h new file mode 100644 index 00000000..862916fa --- /dev/null +++ b/storage/perfschema/pfs_column_types.h @@ -0,0 +1,328 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_COLUMN_TYPES_H +#define PFS_COLUMN_TYPES_H + +/** + @file storage/perfschema/pfs_column_types.h + Data types for columns used in the performance schema tables (declarations) +*/ + +/** Size of the OBJECT_SCHEMA columns. */ +#define COL_OBJECT_SCHEMA_SIZE 64 + +/** + Size of the extended OBJECT_NAME columns. + 'Extended' columns are used when the object name also represents + the name of a non SQL object, such as a file name. + Size in bytes of: + - performance_schema.events_waits_current (OBJECT_NAME) + - performance_schema.events_waits_history (OBJECT_NAME) + - performance_schema.events_waits_history_long (OBJECT_NAME) +*/ +#define COL_OBJECT_NAME_EXTENDED_SIZE 512 + +/** Size of the OBJECT_NAME columns. */ +#define COL_OBJECT_NAME_SIZE 64 + +/** Size of the INDEX_NAME columns. */ +#define COL_INDEX_NAME_SIZE 64 + +/** + Size of INFO columns. + Size in bytes of: + - performance_schema.events_statement_current (INFO) + - performance_schema.events_statement_history (INFO) + - performance_schema.events_statement_history_long (INFO) +*/ +#define COL_INFO_SIZE 1024 + +/** Size of the SOURCE columns. */ +#define COL_SOURCE_SIZE 64 + +/** Size of the DIGEST columns. */ +#define COL_DIGEST_SIZE 64 + +/** + Enum values for the TIMER_NAME columns. + This enum is found in the following tables: + - performance_schema.setup_timer (TIMER_NAME) + - performance_schema.performance_timer (TIMER_NAME) +*/ +enum enum_timer_name +{ + TIMER_NAME_CYCLE= 1, + TIMER_NAME_NANOSEC= 2, + TIMER_NAME_MICROSEC= 3, + TIMER_NAME_MILLISEC= 4, + TIMER_NAME_TICK= 5 +}; + +/** Integer, first value of @sa enum_timer_name. */ +#define FIRST_TIMER_NAME (static_cast<int> (TIMER_NAME_CYCLE)) +/** Integer, last value of @sa enum_timer_name. */ +#define LAST_TIMER_NAME (static_cast<int> (TIMER_NAME_TICK)) +/** Integer, number of values of @sa enum_timer_name. */ +#define COUNT_TIMER_NAME (LAST_TIMER_NAME - FIRST_TIMER_NAME + 1) + +/** + Enum values for the various YES/NO columns. + This enum is found in the following tables: + - performance_schema.setup_instruments (ENABLED) + - performance_schema.setup_instruments (TIMED) + - performance_schema.setup_consumers (ENABLED) +*/ +enum enum_yes_no +{ + ENUM_YES= 1, + ENUM_NO= 2 +}; + +/** + Enum values for the various OPERATION columns. + This enum is found in the following tables: + - performance_schema.events_waits_current (OPERATION) + - performance_schema.events_waits_history (OPERATION) + - performance_schema.events_waits_history_long (OPERATION) +*/ +enum enum_operation_type +{ + /* Mutex operations */ + OPERATION_TYPE_LOCK= 1, + OPERATION_TYPE_TRYLOCK= 2, + + /* Rwlock operations (RW-lock) */ + OPERATION_TYPE_READLOCK= 3, + OPERATION_TYPE_WRITELOCK= 4, + OPERATION_TYPE_TRYREADLOCK= 5, + OPERATION_TYPE_TRYWRITELOCK= 6, + + /* Rwlock operations (SX-lock) */ + OPERATION_TYPE_SHAREDLOCK= 7, + OPERATION_TYPE_SHAREDEXCLUSIVELOCK= 8, + OPERATION_TYPE_EXCLUSIVELOCK= 9, + OPERATION_TYPE_TRYSHAREDLOCK= 10, + OPERATION_TYPE_TRYSHAREDEXCLUSIVELOCK= 11, + OPERATION_TYPE_TRYEXCLUSIVELOCK= 12, + + /* Cond operations */ + OPERATION_TYPE_WAIT= 13, + OPERATION_TYPE_TIMEDWAIT= 14, + + /* File operations */ + OPERATION_TYPE_FILECREATE= 15, + OPERATION_TYPE_FILECREATETMP= 16, + OPERATION_TYPE_FILEOPEN= 17, + OPERATION_TYPE_FILESTREAMOPEN= 18, + OPERATION_TYPE_FILECLOSE= 19, + OPERATION_TYPE_FILESTREAMCLOSE= 20, + OPERATION_TYPE_FILEREAD= 21, + OPERATION_TYPE_FILEWRITE= 22, + OPERATION_TYPE_FILESEEK= 23, + OPERATION_TYPE_FILETELL= 24, + OPERATION_TYPE_FILEFLUSH= 25, + OPERATION_TYPE_FILESTAT= 26, + OPERATION_TYPE_FILEFSTAT= 27, + OPERATION_TYPE_FILECHSIZE= 28, + OPERATION_TYPE_FILEDELETE= 29, + OPERATION_TYPE_FILERENAME= 30, + OPERATION_TYPE_FILESYNC= 31, + + /* Table io operations */ + OPERATION_TYPE_TABLE_FETCH= 32, + OPERATION_TYPE_TABLE_WRITE_ROW= 33, + OPERATION_TYPE_TABLE_UPDATE_ROW= 34, + OPERATION_TYPE_TABLE_DELETE_ROW= 35, + + /* Table lock operations */ + OPERATION_TYPE_TL_READ_NORMAL= 36, + OPERATION_TYPE_TL_READ_WITH_SHARED_LOCKS= 37, + OPERATION_TYPE_TL_READ_HIGH_PRIORITY= 38, + OPERATION_TYPE_TL_READ_NO_INSERTS= 39, + OPERATION_TYPE_TL_WRITE_ALLOW_WRITE= 40, + OPERATION_TYPE_TL_WRITE_CONCURRENT_INSERT= 41, + OPERATION_TYPE_TL_WRITE_DELAYED= 42, + OPERATION_TYPE_TL_WRITE_LOW_PRIORITY= 43, + OPERATION_TYPE_TL_WRITE_NORMAL= 44, + OPERATION_TYPE_TL_READ_EXTERNAL= 45, + OPERATION_TYPE_TL_WRITE_EXTERNAL= 46, + + /* Socket operations */ + OPERATION_TYPE_SOCKETCREATE = 47, + OPERATION_TYPE_SOCKETCONNECT = 48, + OPERATION_TYPE_SOCKETBIND = 49, + OPERATION_TYPE_SOCKETCLOSE = 50, + OPERATION_TYPE_SOCKETSEND = 51, + OPERATION_TYPE_SOCKETRECV = 52, + OPERATION_TYPE_SOCKETSENDTO = 53, + OPERATION_TYPE_SOCKETRECVFROM = 54, + OPERATION_TYPE_SOCKETSENDMSG = 55, + OPERATION_TYPE_SOCKETRECVMSG = 56, + OPERATION_TYPE_SOCKETSEEK = 57, + OPERATION_TYPE_SOCKETOPT = 58, + OPERATION_TYPE_SOCKETSTAT = 59, + OPERATION_TYPE_SOCKETSHUTDOWN = 60, + OPERATION_TYPE_SOCKETSELECT = 61, + + /* Idle operation */ + OPERATION_TYPE_IDLE= 62, + + /* Metadata lock operation */ + OPERATION_TYPE_METADATA= 63 +}; +/** Integer, first value of @sa enum_operation_type. */ +#define FIRST_OPERATION_TYPE (static_cast<int> (OPERATION_TYPE_LOCK)) +/** Integer, last value of @sa enum_operation_type. */ +#define LAST_OPERATION_TYPE (static_cast<int> (OPERATION_TYPE_METADATA)) +/** Integer, number of values of @sa enum_operation_type. */ +#define COUNT_OPERATION_TYPE (LAST_OPERATION_TYPE - FIRST_OPERATION_TYPE + 1) + +/** + Enum values for the various OBJECT_TYPE columns. +*/ +enum enum_object_type +{ + NO_OBJECT_TYPE= 0, + + /* Advertised in SQL ENUM (see table_setup_object.cc) */ + + OBJECT_TYPE_EVENT= 1, + OBJECT_TYPE_FUNCTION= 2, + OBJECT_TYPE_PROCEDURE= 3, + OBJECT_TYPE_TABLE= 4, + OBJECT_TYPE_TRIGGER= 5, + + /* Not advertised in SQL ENUM, only displayed as VARCHAR */ + + OBJECT_TYPE_TEMPORARY_TABLE= 6, + OBJECT_TYPE_BACKUP= 7, + OBJECT_TYPE_SCHEMA= 8, + OBJECT_TYPE_PACKAGE= 9, + OBJECT_TYPE_PACKAGE_BODY= 10, + OBJECT_TYPE_USER_LEVEL_LOCK= 11, +}; +/** Integer, first value of @sa enum_object_type. */ +#define FIRST_OBJECT_TYPE (static_cast<int> (OBJECT_TYPE_EVENT)) +/** Integer, last value of @sa enum_object_type. */ +#define LAST_OBJECT_TYPE (static_cast<int> (OBJECT_TYPE_USER_LEVEL_LOCK)) +/** Integer, number of values of @sa enum_object_type. */ +#define COUNT_OBJECT_TYPE (LAST_OBJECT_TYPE - FIRST_OBJECT_TYPE + 1) + +/** + Enum values for the NESTING_EVENT_TYPE columns. + This enum is found in the following tables: + - performance_schema.events_waits_current (NESTING_EVENT_TYPE) + - performance_schema.events_stages_current (NESTING_EVENT_TYPE) + - performance_schema.events_statements_current (NESTING_EVENT_TYPE) +*/ +enum enum_event_type +{ + EVENT_TYPE_TRANSACTION= 1, + EVENT_TYPE_STATEMENT= 2, + EVENT_TYPE_STAGE= 3, + EVENT_TYPE_WAIT= 4 +}; + +/** Integer, first value of @sa enum_event_type. */ +#define FIRST_EVENT_TYPE (static_cast<int> (EVENT_TYPE_TRANSACTION)) +/** Integer, last value of @sa enum_event_type. */ +#define LAST_EVENT_TYPE (static_cast<int> (EVENT_TYPE_WAIT)) +/** Integer, number of values of @sa enum_event_type. */ +#define COUNT_EVENT_TYPE (LAST_EVENT_TYPE - FIRST_EVENT_TYPE + 1) + +/** + Enum values for transaction state columns. +*/ +enum enum_transaction_state +{ + TRANS_STATE_ACTIVE= 1, + TRANS_STATE_COMMITTED= 2, + TRANS_STATE_ROLLED_BACK= 3 +}; + +/** Integer, first value of @sa enum_transaction_state. */ +#define FIRST_TRANS_STATE (static_cast<int> (TRANS_STATE_ACTIVE)) +/** Integer, last value of @sa enum_transaction_state. */ +#define LAST_TRANS_STATE (static_cast<int> (TRANS_STATE_ROLLED_BACK)) +/** Integer, number of values of @sa enum_transaction_state. */ +#define COUNT_TRANS_STATE (LAST_TRANS_STATE - FIRST_TRANS_STATE + 1) + +/** + Enum values for XA transaction state columns. Enums 0-3 match those used by + the server. See XID_STATE::enum xa_states in xa.h. +*/ +enum enum_xa_transaction_state +{ + TRANS_STATE_XA_NOTR=-1, + TRANS_STATE_XA_ACTIVE=0, + TRANS_STATE_XA_IDLE, + TRANS_STATE_XA_PREPARED, + TRANS_STATE_XA_ROLLBACK_ONLY, + TRANS_STATE_XA_COMMITTED +}; + +/** Integer, first value of @sa enum_xa_transaction_state. */ +#define FIRST_TRANS_STATE_XA (static_cast<int> (TRANS_STATE_XA_NOTR)) +/** Integer, last value of @sa enum_xa_transaction_state. */ +#define LAST_TRANS_STATE_XA (static_cast<int> (TRANS_STATE_XA_COMMITTED)) +/** Integer, number of values of @sa enum_xa_transaction_state. */ +#define COUNT_TRANS_STATE_XA (LAST_TRANS_STATE_XA - FIRST_TRANS_STATE_XA + 1) + +/** + Enum values for transaction isolation level columns. + See enum_tx_isolation in handler.h. +*/ +enum enum_isolation_level +{ + TRANS_LEVEL_READ_UNCOMMITTED, + TRANS_LEVEL_READ_COMMITTED, + TRANS_LEVEL_REPEATABLE_READ, + TRANS_LEVEL_SERIALIZABLE +}; + +/** Integer, first value of @sa enum_isolation_level. */ +#define FIRST_TRANS_LEVEL (static_cast<int> (TRANS_LEVEL_READ_UNCOMMITTED)) +/** Integer, last value of @sa enum_isolation_level. */ +#define LAST_TRANS_LEVEL (static_cast<int> (TRANS_LEVEL_SERIALIZABLE)) +/** Integer, number of values of @sa enum_isolation_level. */ +#define COUNT_TRANS_LEVEL (LAST_TRANS_LEVEL - FIRST_TRANS_LEVEL + 1) + +/** + Enum values for transaction acces mode columns. +*/ +enum enum_transaction_mode +{ + TRANS_MODE_READ_ONLY= 1, + TRANS_MODE_READ_WRITE= 2 +}; + +/** Integer, first value of @sa enum_transaction_mode. */ +#define FIRST_TRANS_MODE (static_cast<int> (TRANS_MODE_READ_WRITE)) +/** Integer, last value of @sa enum_transaction_mode. */ +#define LAST_TRANS_MODE (static_cast<int> (TRANS_MODE_READ_ONLY)) +/** Integer, number of values of @sa enum_transaction_mode. */ +#define COUNT_TRANS_MODE (LAST_TRANS_MODE - FIRST_TRANS_MODE + 1) + + +#endif + diff --git a/storage/perfschema/pfs_column_values.cc b/storage/perfschema/pfs_column_values.cc new file mode 100644 index 00000000..88da190b --- /dev/null +++ b/storage/perfschema/pfs_column_values.cc @@ -0,0 +1,67 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_column_values.cc + Literal values for columns used in the performance + schema tables (implementation). +*/ + +#include "my_global.h" +#include "pfs_column_values.h" + +LEX_CSTRING PERFORMANCE_SCHEMA_str= +{ STRING_WITH_LEN("performance_schema") }; + +LEX_CSTRING mutex_instrument_prefix= +{ STRING_WITH_LEN("wait/synch/mutex/") }; + +LEX_CSTRING rwlock_instrument_prefix= +{ STRING_WITH_LEN("wait/synch/rwlock/") }; + +LEX_CSTRING sxlock_instrument_prefix= +{ C_STRING_WITH_LEN("wait/synch/sxlock/") }; + +LEX_CSTRING cond_instrument_prefix= +{ C_STRING_WITH_LEN("wait/synch/cond/") }; + +LEX_CSTRING thread_instrument_prefix= +{ STRING_WITH_LEN("thread/") }; + +LEX_CSTRING file_instrument_prefix= +{ STRING_WITH_LEN("wait/io/file/") }; + +LEX_CSTRING stage_instrument_prefix= +{ STRING_WITH_LEN("stage/") }; + +LEX_CSTRING statement_instrument_prefix= +{ STRING_WITH_LEN("statement/") }; + +LEX_CSTRING transaction_instrument_prefix= +{ C_STRING_WITH_LEN("transaction") }; + +LEX_CSTRING socket_instrument_prefix= +{ C_STRING_WITH_LEN("wait/io/socket/") }; + +LEX_CSTRING memory_instrument_prefix= +{ C_STRING_WITH_LEN("memory/") }; + diff --git a/storage/perfschema/pfs_column_values.h b/storage/perfschema/pfs_column_values.h new file mode 100644 index 00000000..75fd7b1d --- /dev/null +++ b/storage/perfschema/pfs_column_values.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_COLUMN_VALUES_H +#define PFS_COLUMN_VALUES_H + +#include "m_string.h" /* LEX_CSTRING */ + +/** + @file storage/perfschema/pfs_column_values.h + Literal values for columns used in the + performance schema tables (declarations). +*/ + +/** String, "PERFORMANCE_SCHEMA". */ +extern LEX_CSTRING PERFORMANCE_SCHEMA_str; + +/** String prefix for all mutex instruments. */ +extern LEX_CSTRING mutex_instrument_prefix; +/** String prefix for all rwlock instruments. */ +extern LEX_CSTRING rwlock_instrument_prefix; +/** String prefix for all sxlock instruments. */ +extern LEX_CSTRING sxlock_instrument_prefix; +/** String prefix for all cond instruments. */ +extern LEX_CSTRING cond_instrument_prefix; +/** String prefix for all thread instruments. */ +extern LEX_CSTRING thread_instrument_prefix; +/** String prefix for all file instruments. */ +extern LEX_CSTRING file_instrument_prefix; +/** String prefix for all stage instruments. */ +extern LEX_CSTRING stage_instrument_prefix; +/** String prefix for all statement instruments. */ +extern LEX_CSTRING statement_instrument_prefix; +/** String prefix for all transaction instruments. */ +extern LEX_CSTRING transaction_instrument_prefix; +/** String prefix for all socket instruments. */ +extern LEX_CSTRING socket_instrument_prefix; +/** String prefix for all memory instruments. */ +extern LEX_CSTRING memory_instrument_prefix; + +#endif + diff --git a/storage/perfschema/pfs_con_slice.cc b/storage/perfschema/pfs_con_slice.cc new file mode 100644 index 00000000..4c99a32f --- /dev/null +++ b/storage/perfschema/pfs_con_slice.cc @@ -0,0 +1,82 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_con_slice.h" +#include "pfs_stat.h" +#include "pfs_global.h" +#include "pfs_instr_class.h" + +/** + @file storage/perfschema/pfs_con_slice.cc + Performance schema connection slice (implementation). +*/ + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +void PFS_connection_slice::reset_waits_stats() +{ + PFS_single_stat *stat= m_instr_class_waits_stats; + PFS_single_stat *stat_last= stat + wait_class_max; + for ( ; stat < stat_last; stat++) + stat->reset(); +} + +void PFS_connection_slice::reset_stages_stats() +{ + PFS_stage_stat *stat= m_instr_class_stages_stats; + PFS_stage_stat *stat_last= stat + stage_class_max; + for ( ; stat < stat_last; stat++) + stat->reset(); +} + +void PFS_connection_slice::reset_statements_stats() +{ + PFS_statement_stat *stat= m_instr_class_statements_stats; + PFS_statement_stat *stat_last= stat + statement_class_max; + for ( ; stat < stat_last; stat++) + stat->reset(); +} + +void PFS_connection_slice::reset_transactions_stats() +{ + PFS_transaction_stat *stat= + &m_instr_class_transactions_stats[GLOBAL_TRANSACTION_INDEX]; + if (stat) + stat->reset(); +} + +void PFS_connection_slice::rebase_memory_stats() +{ + PFS_memory_stat *stat= m_instr_class_memory_stats; + PFS_memory_stat *stat_last= stat + memory_class_max; + for ( ; stat < stat_last; stat++) + stat->reset(); +} + +/** @} */ + diff --git a/storage/perfschema/pfs_con_slice.h b/storage/perfschema/pfs_con_slice.h new file mode 100644 index 00000000..f0a89c8f --- /dev/null +++ b/storage/perfschema/pfs_con_slice.h @@ -0,0 +1,261 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_CON_SLICE_H +#define PFS_CON_SLICE_H + +/** + @file storage/perfschema/pfs_con_slice.h + Performance schema connection slice (declarations). +*/ + +#include "sql_class.h" +#include "pfs_lock.h" +#include "lf.h" +#include "pfs_status.h" + +struct PFS_single_stat; +struct PFS_stage_stat; +struct PFS_statement_stat; +struct PFS_transaction_stat; +struct PFS_memory_stat; +class PFS_opaque_container_page; + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +/** + A connection slice, an arbitrary grouping of several connections. + This structure holds statistics for grouping of connections. +*/ +struct PFS_connection_slice +{ + /** Reset all statistics. */ + inline void reset_stats() + { + m_has_waits_stats= false; + m_has_stages_stats= false; + m_has_statements_stats= false; + m_has_transactions_stats= false; + m_has_memory_stats= false; + reset_status_stats(); + } + + /** Reset all wait statistics. */ + void reset_waits_stats(); + /** Reset all stages statistics. */ + void reset_stages_stats(); + /** Reset all statements statistics. */ + void reset_statements_stats(); + /** Reset all transactions statistics. */ + void reset_transactions_stats(); + /** Reset all memory statistics. */ + void rebase_memory_stats(); + /** Reset all status variable statistics. */ + void reset_status_stats() + { + m_status_stats.reset(); + } + + void set_instr_class_waits_stats(PFS_single_stat *array) + { + m_has_waits_stats= false; + m_instr_class_waits_stats= array; + } + + const PFS_single_stat* read_instr_class_waits_stats() const + { + if (! m_has_waits_stats) + return NULL; + return m_instr_class_waits_stats; + } + + PFS_single_stat* write_instr_class_waits_stats() + { + if (! m_has_waits_stats) + { + reset_waits_stats(); + m_has_waits_stats= true; + } + return m_instr_class_waits_stats; + } + + void set_instr_class_stages_stats(PFS_stage_stat *array) + { + m_has_stages_stats= false; + m_instr_class_stages_stats= array; + } + + const PFS_stage_stat* read_instr_class_stages_stats() const + { + if (! m_has_stages_stats) + return NULL; + return m_instr_class_stages_stats; + } + + PFS_stage_stat* write_instr_class_stages_stats() + { + if (! m_has_stages_stats) + { + reset_stages_stats(); + m_has_stages_stats= true; + } + return m_instr_class_stages_stats; + } + + void set_instr_class_statements_stats(PFS_statement_stat *array) + { + m_has_statements_stats= false; + m_instr_class_statements_stats= array; + } + + const PFS_statement_stat* read_instr_class_statements_stats() const + { + if (! m_has_statements_stats) + return NULL; + return m_instr_class_statements_stats; + } + + PFS_statement_stat* write_instr_class_statements_stats() + { + if (! m_has_statements_stats) + { + reset_statements_stats(); + m_has_statements_stats= true; + } + return m_instr_class_statements_stats; + } + + void set_instr_class_transactions_stats(PFS_transaction_stat *array) + { + m_has_transactions_stats= false; + m_instr_class_transactions_stats= array; + } + + const PFS_transaction_stat* read_instr_class_transactions_stats() const + { + if (! m_has_transactions_stats) + return NULL; + return m_instr_class_transactions_stats; + } + + PFS_transaction_stat* write_instr_class_transactions_stats() + { + if (! m_has_transactions_stats) + { + reset_transactions_stats(); + m_has_transactions_stats= true; + } + return m_instr_class_transactions_stats; + } + + void set_instr_class_memory_stats(PFS_memory_stat *array) + { + m_has_memory_stats= false; + m_instr_class_memory_stats= array; + } + + const PFS_memory_stat* read_instr_class_memory_stats() const + { + if (! m_has_memory_stats) + return NULL; + return m_instr_class_memory_stats; + } + + PFS_memory_stat* write_instr_class_memory_stats() + { + if (! m_has_memory_stats) + { + rebase_memory_stats(); + m_has_memory_stats= true; + } + return m_instr_class_memory_stats; + } + +private: + bool m_has_waits_stats; + bool m_has_stages_stats; + bool m_has_statements_stats; + bool m_has_transactions_stats; + bool m_has_memory_stats; + + /** + Per connection slice waits aggregated statistics. + This member holds the data for the table + PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_*_BY_EVENT_NAME. + Immutable, safe to use without internal lock. + */ + PFS_single_stat *m_instr_class_waits_stats; + + /** + Per connection slice stages aggregated statistics. + This member holds the data for the table + PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_BY_*_BY_EVENT_NAME. + Immutable, safe to use without internal lock. + */ + PFS_stage_stat *m_instr_class_stages_stats; + + /** + Per connection slice statements aggregated statistics. + This member holds the data for the table + PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_*_BY_EVENT_NAME. + Immutable, safe to use without internal lock. + */ + PFS_statement_stat *m_instr_class_statements_stats; + + /** + Per connection slice transactions aggregated statistics. + This member holds the data for the table + PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_BY_*_BY_EVENT_NAME. + Immutable, safe to use without internal lock. + */ + PFS_transaction_stat *m_instr_class_transactions_stats; + + /** + Per connection slice memory aggregated statistics. + This member holds the data for the table + PERFORMANCE_SCHEMA.MEMORY_SUMMARY_BY_*_BY_EVENT_NAME. + Immutable, safe to use without internal lock. + */ + PFS_memory_stat *m_instr_class_memory_stats; + +public: + + void aggregate_status_stats(const STATUS_VAR *status_vars) + { + m_status_stats.aggregate_from(status_vars); + } + + /** + Aggregated status variables. + */ + PFS_status_stats m_status_stats; + + /** Container page. */ + PFS_opaque_container_page *m_page; +}; + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_config.h.cmake b/storage/perfschema/pfs_config.h.cmake new file mode 100644 index 00000000..2b61b7e1 --- /dev/null +++ b/storage/perfschema/pfs_config.h.cmake @@ -0,0 +1,6 @@ +#cmakedefine HAVE_PTHREAD_THREADID_NP 1 +#cmakedefine HAVE_SYS_GETTID 1 +#cmakedefine HAVE_GETTID +#cmakedefine HAVE_GETTHRID 1 +#cmakedefine HAVE_PTHREAD_GETTHREADID_NP 1 +#cmakedefine HAVE_INTEGER_PTHREAD_SELF 1 diff --git a/storage/perfschema/pfs_defaults.cc b/storage/perfschema/pfs_defaults.cc new file mode 100644 index 00000000..23599c9e --- /dev/null +++ b/storage/perfschema/pfs_defaults.cc @@ -0,0 +1,106 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_defaults.cc + Default setup (implementation). +*/ + +#include <my_global.h> +#include "pfs.h" +#include "pfs_defaults.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_setup_actor.h" +#include "pfs_setup_object.h" + +static PSI_thread_key thread_key; +static PSI_thread_info thread_info= { &thread_key, "setup", PSI_FLAG_GLOBAL }; + +const char* pfs_category= "performance_schema"; + +void install_default_setup(PSI_bootstrap *boot) +{ + PSI *psi= (PSI*) boot->get_interface(PSI_CURRENT_VERSION); + if (psi == NULL) + return; + + psi->register_thread(pfs_category, &thread_info, 1); + PSI_thread *psi_thread= psi->new_thread(thread_key, NULL, 0); + + if (psi_thread != NULL) + { + /* LF_HASH needs a thread, for PINS */ + psi->set_thread(psi_thread); + + String percent("%", 1, &my_charset_utf8mb3_bin); + /* Enable all users on all hosts by default */ + insert_setup_actor(&percent, &percent, &percent, true, true); + + String mysql_db("mysql", 5, &my_charset_utf8mb3_bin); + String PS_db("performance_schema", 18, &my_charset_utf8mb3_bin); + String IS_db("information_schema", 18, &my_charset_utf8mb3_bin); + + /* Disable sp by default in mysql. */ + insert_setup_object(OBJECT_TYPE_EVENT, &mysql_db, &percent, false, false); + /* Disable sp in performance/information schema. */ + insert_setup_object(OBJECT_TYPE_EVENT, &PS_db, &percent, false, false); + insert_setup_object(OBJECT_TYPE_EVENT, &IS_db, &percent, false, false); + /* Enable every other sp. */ + insert_setup_object(OBJECT_TYPE_EVENT, &percent, &percent, true, true); + + /* Disable sp by default in mysql. */ + insert_setup_object(OBJECT_TYPE_FUNCTION, &mysql_db, &percent, false, false); + /* Disable sp in performance/information schema. */ + insert_setup_object(OBJECT_TYPE_FUNCTION, &PS_db, &percent, false, false); + insert_setup_object(OBJECT_TYPE_FUNCTION, &IS_db, &percent, false, false); + /* Enable every other sp. */ + insert_setup_object(OBJECT_TYPE_FUNCTION, &percent, &percent, true, true); + + /* Disable sp by default in mysql. */ + insert_setup_object(OBJECT_TYPE_PROCEDURE, &mysql_db, &percent, false, false); + /* Disable sp in performance/information schema. */ + insert_setup_object(OBJECT_TYPE_PROCEDURE, &PS_db, &percent, false, false); + insert_setup_object(OBJECT_TYPE_PROCEDURE, &IS_db, &percent, false, false); + /* Enable every other sp. */ + insert_setup_object(OBJECT_TYPE_PROCEDURE, &percent, &percent, true, true); + + /* Disable system tables by default */ + insert_setup_object(OBJECT_TYPE_TABLE, &mysql_db, &percent, false, false); + /* Disable performance/information schema tables. */ + insert_setup_object(OBJECT_TYPE_TABLE, &PS_db, &percent, false, false); + insert_setup_object(OBJECT_TYPE_TABLE, &IS_db, &percent, false, false); + /* Enable every other tables */ + insert_setup_object(OBJECT_TYPE_TABLE, &percent, &percent, true, true); + + /* Disable sp by default in mysql. */ + insert_setup_object(OBJECT_TYPE_TRIGGER, &mysql_db, &percent, false, false); + /* Disable sp in performance/information schema. */ + insert_setup_object(OBJECT_TYPE_TRIGGER, &PS_db, &percent, false, false); + insert_setup_object(OBJECT_TYPE_TRIGGER, &IS_db, &percent, false, false); + /* Enable every other sp. */ + insert_setup_object(OBJECT_TYPE_TRIGGER, &percent, &percent, true, true); + } + + psi->delete_current_thread(); +} + diff --git a/storage/perfschema/pfs_defaults.h b/storage/perfschema/pfs_defaults.h new file mode 100644 index 00000000..23e8755f --- /dev/null +++ b/storage/perfschema/pfs_defaults.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_DEFAULTS_H +#define PFS_DEFAULTS_H + +/** + @file storage/perfschema/pfs_defaults.h + Default setup (declarations). +*/ + +/** + Configure the performance schema setup tables with default content. + The tables populated are: + - SETUP_ACTORS + - SETUP_OBJECTS +*/ +void install_default_setup(PSI_bootstrap *boot); + +#endif diff --git a/storage/perfschema/pfs_digest.cc b/storage/perfschema/pfs_digest.cc new file mode 100644 index 00000000..fd5fdc5f --- /dev/null +++ b/storage/perfschema/pfs_digest.cc @@ -0,0 +1,413 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + Copyright (c) 2022, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_digest.h + Statement Digest data structures (implementation). +*/ + +/* + This code needs extra visibility in the lexer structures +*/ + +#define MYSQL_LEX 1 + +#include "my_global.h" +#include "my_sys.h" +#include "pfs_instr.h" +#include "pfs_digest.h" +#include "pfs_global.h" +#include "pfs_builtin_memory.h" +#include "table_helper.h" +#include "sql_lex.h" +#include "sql_signal.h" +#include "sql_get_diagnostics.h" +#include "sql_string.h" +#include <string.h> + +size_t digest_max= 0; +ulong digest_lost= 0; + +/** EVENTS_STATEMENTS_HISTORY_LONG circular buffer. */ +PFS_statements_digest_stat *statements_digest_stat_array= NULL; +static unsigned char *statements_digest_token_array= NULL; +/** Consumer flag for table EVENTS_STATEMENTS_SUMMARY_BY_DIGEST. */ +bool flag_statements_digest= true; +/** + Current index in Stat array where new record is to be inserted. + index 0 is reserved for "all else" case when entire array is full. +*/ +PFS_ALIGNED static PFS_cacheline_uint32 digest_monotonic_index; + +bool digest_full= false; + +LF_HASH digest_hash; +static bool digest_hash_inited= false; + +/** + Initialize table EVENTS_STATEMENTS_SUMMARY_BY_DIGEST. + @param param performance schema sizing +*/ +int init_digest(const PFS_global_param *param) +{ + /* + Allocate memory for statements_digest_stat_array based on + performance_schema_digests_size values + */ + digest_max= param->m_digest_sizing; + digest_lost= 0; + PFS_atomic::store_u32(& digest_monotonic_index.m_u32, 1); + digest_full= false; + + if (digest_max == 0) + return 0; + + statements_digest_stat_array= + PFS_MALLOC_ARRAY(& builtin_memory_digest, + digest_max, + sizeof(PFS_statements_digest_stat), PFS_statements_digest_stat, + MYF(MY_ZEROFILL)); + + if (unlikely(statements_digest_stat_array == NULL)) + { + cleanup_digest(); + return 1; + } + + if (pfs_max_digest_length > 0) + { + /* Size of each digest array. */ + size_t digest_memory_size= pfs_max_digest_length * sizeof(unsigned char); + + statements_digest_token_array= + PFS_MALLOC_ARRAY(& builtin_memory_digest_tokens, + digest_max, + digest_memory_size, + unsigned char, + MYF(MY_ZEROFILL)); + + if (unlikely(statements_digest_token_array == NULL)) + { + cleanup_digest(); + return 1; + } + } + + for (size_t index= 0; index < digest_max; index++) + { + statements_digest_stat_array[index].reset_data(statements_digest_token_array + + index * pfs_max_digest_length, pfs_max_digest_length); + } + + /* Set record[0] as allocated. */ + statements_digest_stat_array[0].m_lock.set_allocated(); + + /* Set record[0] as allocated. */ + statements_digest_stat_array[0].m_lock.set_allocated(); + + return 0; +} + +/** Cleanup table EVENTS_STATEMENTS_SUMMARY_BY_DIGEST. */ +void cleanup_digest(void) +{ + PFS_FREE_ARRAY(& builtin_memory_digest, + digest_max, + sizeof(PFS_statements_digest_stat), + statements_digest_stat_array); + + PFS_FREE_ARRAY(& builtin_memory_digest_tokens, + digest_max, + (pfs_max_digest_length * sizeof(unsigned char)), + statements_digest_token_array); + + statements_digest_stat_array= NULL; + statements_digest_token_array= NULL; +} + +C_MODE_START +static uchar *digest_hash_get_key(const uchar *entry, size_t *length, + my_bool) +{ + const PFS_statements_digest_stat * const *typed_entry; + const PFS_statements_digest_stat *digest; + const void *result; + typed_entry= reinterpret_cast<const PFS_statements_digest_stat*const*>(entry); + assert(typed_entry != NULL); + digest= *typed_entry; + assert(digest != NULL); + *length= sizeof (PFS_digest_key); + result= & digest->m_digest_key; + return const_cast<uchar*> (reinterpret_cast<const uchar*> (result)); +} +C_MODE_END + + +/** + Initialize the digest hash. + @return 0 on success +*/ +int init_digest_hash(const PFS_global_param *param) +{ + if ((! digest_hash_inited) && (param->m_digest_sizing != 0)) + { + lf_hash_init(&digest_hash, sizeof(PFS_statements_digest_stat*), + LF_HASH_UNIQUE, 0, 0, digest_hash_get_key, + &my_charset_bin); + digest_hash_inited= true; + } + return 0; +} + +void cleanup_digest_hash(void) +{ + if (digest_hash_inited) + { + lf_hash_destroy(&digest_hash); + digest_hash_inited= false; + } +} + +static LF_PINS* get_digest_hash_pins(PFS_thread *thread) +{ + if (unlikely(thread->m_digest_hash_pins == NULL)) + { + if (!digest_hash_inited) + return NULL; + thread->m_digest_hash_pins= lf_hash_get_pins(&digest_hash); + } + return thread->m_digest_hash_pins; +} + +PFS_statement_stat* +find_or_create_digest(PFS_thread *thread, + const sql_digest_storage *digest_storage, + const char *schema_name, + uint schema_name_length) +{ + assert(digest_storage != NULL); + + if (statements_digest_stat_array == NULL) + return NULL; + + if (digest_storage->m_byte_count <= 0) + return NULL; + + LF_PINS *pins= get_digest_hash_pins(thread); + if (unlikely(pins == NULL)) + return NULL; + + /* + Note: the LF_HASH key is a block of memory, + make sure to clean unused bytes, + so that memcmp() can compare keys. + */ + PFS_digest_key hash_key; + memset(& hash_key, 0, sizeof(hash_key)); + /* Compute MD5 Hash of the tokens received. */ + compute_digest_md5(digest_storage, hash_key.m_md5); + memcpy((void*)& digest_storage->m_md5, &hash_key.m_md5, MD5_HASH_SIZE); + /* Add the current schema to the key */ + hash_key.m_schema_name_length= schema_name_length; + if (schema_name_length > 0) + memcpy(hash_key.m_schema_name, schema_name, schema_name_length); + + int res; + uint retry_count= 0; + const uint retry_max= 3; + size_t safe_index; + size_t attempts= 0; + PFS_statements_digest_stat **entry; + PFS_statements_digest_stat *pfs= NULL; + pfs_dirty_state dirty_state; + + ulonglong now= my_hrtime().val; + +search: + + /* Lookup LF_HASH using this new key. */ + entry= reinterpret_cast<PFS_statements_digest_stat**> + (lf_hash_search(&digest_hash, pins, + &hash_key, sizeof(PFS_digest_key))); + + if (entry && (entry != MY_ERRPTR)) + { + /* If digest already exists, update stats and return. */ + pfs= *entry; + pfs->m_last_seen= now; + lf_hash_search_unpin(pins); + return & pfs->m_stat; + } + + lf_hash_search_unpin(pins); + + if (digest_full) + { + /* digest_stat array is full. Add stat at index 0 and return. */ + pfs= &statements_digest_stat_array[0]; + digest_lost++; + + if (pfs->m_first_seen == 0) + pfs->m_first_seen= now; + pfs->m_last_seen= now; + return & pfs->m_stat; + } + + while (++attempts <= digest_max) + { + safe_index= PFS_atomic::add_u32(& digest_monotonic_index.m_u32, 1) % digest_max; + if (safe_index == 0) + { + /* Record [0] is reserved. */ + continue; + } + + /* Add a new record in digest stat array. */ + assert(safe_index < digest_max); + pfs= &statements_digest_stat_array[safe_index]; + + if (pfs->m_lock.is_free()) + { + if (pfs->m_lock.free_to_dirty(& dirty_state)) + { + /* Copy digest hash/LF Hash search key. */ + memcpy(& pfs->m_digest_key, &hash_key, sizeof(PFS_digest_key)); + + /* + Copy digest storage to statement_digest_stat_array so that it could be + used later to generate digest text. + */ + pfs->m_digest_storage.copy(digest_storage); + + pfs->m_first_seen= now; + pfs->m_last_seen= now; + + res= lf_hash_insert(&digest_hash, pins, &pfs); + if (likely(res == 0)) + { + pfs->m_lock.dirty_to_allocated(& dirty_state); + return & pfs->m_stat; + } + + pfs->m_lock.dirty_to_free(& dirty_state); + + if (res > 0) + { + /* Duplicate insert by another thread */ + if (++retry_count > retry_max) + { + /* Avoid infinite loops */ + digest_lost++; + return NULL; + } + goto search; + } + + /* OOM in lf_hash_insert */ + digest_lost++; + return NULL; + } + } + } + + /* The digest array is now full. */ + digest_full= true; + pfs= &statements_digest_stat_array[0]; + + if (pfs->m_first_seen == 0) + pfs->m_first_seen= now; + pfs->m_last_seen= now; + return & pfs->m_stat; +} + +void purge_digest(PFS_thread* thread, PFS_digest_key *hash_key) +{ + LF_PINS *pins= get_digest_hash_pins(thread); + if (unlikely(pins == NULL)) + return; + + PFS_statements_digest_stat **entry; + + /* Lookup LF_HASH using this new key. */ + entry= reinterpret_cast<PFS_statements_digest_stat**> + (lf_hash_search(&digest_hash, pins, + hash_key, sizeof(PFS_digest_key))); + + if (entry && (entry != MY_ERRPTR)) + { + lf_hash_delete(&digest_hash, pins, + hash_key, sizeof(PFS_digest_key)); + } + lf_hash_search_unpin(pins); + return; +} + +void PFS_statements_digest_stat::reset_data(unsigned char *token_array, size_t length) +{ + pfs_dirty_state dirty_state; + m_lock.set_dirty(& dirty_state); + m_digest_storage.reset(token_array, length); + m_stat.reset(); + m_first_seen= 0; + m_last_seen= 0; + m_lock.dirty_to_free(& dirty_state); +} + +void PFS_statements_digest_stat::reset_index(PFS_thread *thread) +{ + /* Only remove entries that exists in the HASH index. */ + if (m_digest_storage.m_byte_count > 0) + { + purge_digest(thread, & m_digest_key); + } +} + +void reset_esms_by_digest() +{ + uint index; + + if (statements_digest_stat_array == NULL) + return; + + PFS_thread *thread= PFS_thread::get_current_thread(); + if (unlikely(thread == NULL)) + return; + + /* Reset statements_digest_stat_array. */ + for (index= 0; index < digest_max; index++) + { + statements_digest_stat_array[index].reset_index(thread); + statements_digest_stat_array[index].reset_data(statements_digest_token_array + index * pfs_max_digest_length, pfs_max_digest_length); + } + + /* Mark record[0] as allocated again. */ + statements_digest_stat_array[0].m_lock.set_allocated(); + + /* + Reset index which indicates where the next calculated digest information + to be inserted in statements_digest_stat_array. + */ + PFS_atomic::store_u32(& digest_monotonic_index.m_u32, 1); + digest_full= false; +} + diff --git a/storage/perfschema/pfs_digest.h b/storage/perfschema/pfs_digest.h new file mode 100644 index 00000000..bec2c28e --- /dev/null +++ b/storage/perfschema/pfs_digest.h @@ -0,0 +1,94 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_DIGEST_H +#define PFS_DIGEST_H + +/** + @file storage/perfschema/pfs_digest.h + Statement Digest data structures (declarations). +*/ + +#include "pfs_column_types.h" +#include "lf.h" +#include "pfs_stat.h" +#include "sql_digest.h" + +extern bool flag_statements_digest; +extern size_t digest_max; +extern ulong digest_lost; +struct PFS_thread; + +/** + Structure to store a MD5 hash value (digest) for a statement. +*/ +struct PFS_digest_key +{ + unsigned char m_md5[MD5_HASH_SIZE]; + char m_schema_name[NAME_LEN]; + uint m_schema_name_length; +}; + +/** A statement digest stat record. */ +struct PFS_ALIGNED PFS_statements_digest_stat +{ + /** Internal lock. */ + pfs_lock m_lock; + + /** Digest Schema + MD5 Hash. */ + PFS_digest_key m_digest_key; + + /** Digest Storage. */ + sql_digest_storage m_digest_storage; + + /** Statement stat. */ + PFS_statement_stat m_stat; + + /** First and last seen timestamps.*/ + ulonglong m_first_seen; + ulonglong m_last_seen; + + /** Reset data for this record. */ + void reset_data(unsigned char* token_array, size_t length); + /** Reset data and remove index for this record. */ + void reset_index(PFS_thread *thread); +}; + +int init_digest(const PFS_global_param *param); +void cleanup_digest(); + +int init_digest_hash(const PFS_global_param *param); +void cleanup_digest_hash(void); +PFS_statement_stat* find_or_create_digest(PFS_thread *thread, + const sql_digest_storage *digest_storage, + const char *schema_name, + uint schema_name_length); + +void reset_esms_by_digest(); + +/* Exposing the data directly, for iterators. */ +extern PFS_statements_digest_stat *statements_digest_stat_array; + +extern LF_HASH digest_hash; + +#endif + diff --git a/storage/perfschema/pfs_engine_table.cc b/storage/perfschema/pfs_engine_table.cc new file mode 100644 index 00000000..76095b53 --- /dev/null +++ b/storage/perfschema/pfs_engine_table.cc @@ -0,0 +1,1976 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_engine_table.cc + Performance schema tables (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "hostname.h" /* For Host_entry */ +#include "pfs_engine_table.h" +#include "pfs_buffer_container.h" + +#include "table_events_waits.h" +#include "table_setup_actors.h" +#include "table_setup_consumers.h" +#include "table_setup_instruments.h" +#include "table_setup_objects.h" +#include "table_setup_timers.h" +#include "table_performance_timers.h" +#include "table_events_waits_summary.h" +#include "table_ews_by_thread_by_event_name.h" +#include "table_ews_global_by_event_name.h" +#include "table_host_cache.h" +#include "table_os_global_by_type.h" +#include "table_sync_instances.h" +#include "table_file_instances.h" +#include "table_file_summary_by_instance.h" +#include "table_file_summary_by_event_name.h" +#include "table_threads.h" +//#include "table_processlist.h" + +#include "table_ews_by_host_by_event_name.h" +#include "table_ews_by_user_by_event_name.h" +#include "table_ews_by_account_by_event_name.h" +#include "table_tiws_by_index_usage.h" +#include "table_tiws_by_table.h" +#include "table_tlws_by_table.h" + +#include "table_events_stages.h" +#include "table_esgs_by_thread_by_event_name.h" +#include "table_esgs_by_host_by_event_name.h" +#include "table_esgs_by_user_by_event_name.h" +#include "table_esgs_by_account_by_event_name.h" +#include "table_esgs_global_by_event_name.h" + +#include "table_events_statements.h" +#include "table_esms_by_thread_by_event_name.h" +#include "table_esms_by_host_by_event_name.h" +#include "table_esms_by_user_by_event_name.h" +#include "table_esms_by_account_by_event_name.h" +#include "table_esms_global_by_event_name.h" +#include "table_esms_by_digest.h" +#include "table_esms_by_program.h" + +#include "table_events_transactions.h" +#include "table_ets_by_thread_by_event_name.h" +#include "table_ets_by_host_by_event_name.h" +#include "table_ets_by_user_by_event_name.h" +#include "table_ets_by_account_by_event_name.h" +#include "table_ets_global_by_event_name.h" + +#include "table_users.h" +#include "table_accounts.h" +#include "table_hosts.h" + +#include "table_socket_instances.h" +#include "table_socket_summary_by_instance.h" +#include "table_socket_summary_by_event_name.h" +#include "table_session_connect_attrs.h" +#include "table_session_account_connect_attrs.h" +#include "table_mems_global_by_event_name.h" +#include "table_mems_by_account_by_event_name.h" +#include "table_mems_by_host_by_event_name.h" +#include "table_mems_by_thread_by_event_name.h" +#include "table_mems_by_user_by_event_name.h" + +/* For replication related perfschema tables. */ +#include "table_replication_connection_configuration.h" +#include "table_replication_group_members.h" +#include "table_replication_connection_status.h" +#include "table_replication_applier_configuration.h" +#include "table_replication_applier_status.h" +#include "table_replication_applier_status_by_coordinator.h" +#include "table_replication_applier_status_by_worker.h" +#include "table_replication_group_member_stats.h" + +#include "table_prepared_stmt_instances.h" + +#include "table_md_locks.h" +#include "table_table_handles.h" + +#include "table_uvar_by_thread.h" + +#include "table_status_by_account.h" +#include "table_status_by_host.h" +#include "table_status_by_thread.h" +#include "table_status_by_user.h" +#include "table_global_status.h" +#include "table_session_status.h" + +#include "table_variables_by_thread.h" +#include "table_global_variables.h" +#include "table_session_variables.h" + +/* For show status */ +#include "pfs_column_values.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_setup_actor.h" +#include "pfs_setup_object.h" +#include "pfs_global.h" +#include "pfs_digest.h" + +#include "sql_base.h" // close_thread_tables +#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT +#include "log.h" + +/** + @addtogroup Performance_schema_engine + @{ +*/ + +bool PFS_table_context::initialize(void) +{ + if (m_restore) + { + /* Restore context from TLS. */ + PFS_table_context *context= static_cast<PFS_table_context *>(my_get_thread_local(m_thr_key)); + assert(context != NULL); + + if(context) + { + m_last_version= context->m_current_version; + m_map= context->m_map; + assert(m_map_size == context->m_map_size); + m_map_size= context->m_map_size; + } + } + else + { + /* Check that TLS is not in use. */ + PFS_table_context *context= static_cast<PFS_table_context *>(my_get_thread_local(m_thr_key)); + //assert(context == NULL); + + context= this; + + /* Initialize a new context, store in TLS. */ + m_last_version= m_current_version; + m_map= NULL; + + /* Allocate a bitmap to record which threads are materialized. */ + if (m_map_size > 0) + { + THD *thd= current_thd; + ulong words= m_map_size / m_word_size + (m_map_size % m_word_size > 0); + m_map= (ulong *)thd->calloc(words * m_word_size); + } + + /* Write to TLS. */ + my_set_thread_local(m_thr_key, static_cast<void *>(context)); + } + + m_initialized= (m_map_size > 0) ? (m_map != NULL) : true; + + return m_initialized; +} + +/* Constructor for global or single thread tables, map size = 0. */ +PFS_table_context::PFS_table_context(ulonglong current_version, bool restore, thread_local_key_t key) : + m_thr_key(key), m_current_version(current_version), m_last_version(0), + m_map(NULL), m_map_size(0), + m_restore(restore), m_initialized(false), m_last_item(0) +{ + initialize(); +} + +/* Constructor for by-thread or aggregate tables, map size = max thread/user/host/account. */ +PFS_table_context::PFS_table_context(ulonglong current_version, ulong map_size, bool restore, thread_local_key_t key) : + m_thr_key(key), m_current_version(current_version), m_last_version(0), + m_map(NULL), m_map_size(map_size), + m_restore(restore), m_initialized(false), m_last_item(0) +{ + initialize(); +} + +PFS_table_context::~PFS_table_context(void) +{ + /* Clear TLS after final use. */ // TODO: How is that determined? +// if (m_restore) +// { +// my_set_thread_local(m_thr_key, NULL); +// } +} + +void PFS_table_context::set_item(ulong n) +{ + if (n == m_last_item) + return; + ulong word= n / m_word_size; + ulong bit= n % m_word_size; + m_map[word] |= (1UL << bit); + m_last_item= n; +} + +bool PFS_table_context::is_item_set(ulong n) +{ + ulong word= n / m_word_size; + ulong bit= n % m_word_size; + return (m_map[word] & (1UL << bit)); +} + + +static PFS_engine_table_share *all_shares[]= +{ + &table_cond_instances::m_share, + &table_events_waits_current::m_share, + &table_events_waits_history::m_share, + &table_events_waits_history_long::m_share, + &table_ews_by_host_by_event_name::m_share, + &table_events_waits_summary_by_instance::m_share, + &table_ews_by_thread_by_event_name::m_share, + &table_ews_by_user_by_event_name::m_share, + &table_ews_by_account_by_event_name::m_share, + &table_ews_global_by_event_name::m_share, + &table_file_instances::m_share, + &table_file_summary_by_event_name::m_share, + &table_file_summary_by_instance::m_share, + &table_host_cache::m_share, + &table_mutex_instances::m_share, + &table_os_global_by_type::m_share, + &table_performance_timers::m_share, + &table_rwlock_instances::m_share, + &table_setup_actors::m_share, + &table_setup_consumers::m_share, + &table_setup_instruments::m_share, + &table_setup_objects::m_share, + &table_setup_timers::m_share, + &table_tiws_by_index_usage::m_share, + &table_tiws_by_table::m_share, + &table_tlws_by_table::m_share, + &table_threads::m_share, + + &table_events_stages_current::m_share, + &table_events_stages_history::m_share, + &table_events_stages_history_long::m_share, + &table_esgs_by_thread_by_event_name::m_share, + &table_esgs_by_account_by_event_name::m_share, + &table_esgs_by_user_by_event_name::m_share, + &table_esgs_by_host_by_event_name::m_share, + &table_esgs_global_by_event_name::m_share, + + &table_events_statements_current::m_share, + &table_events_statements_history::m_share, + &table_events_statements_history_long::m_share, + &table_esms_by_thread_by_event_name::m_share, + &table_esms_by_account_by_event_name::m_share, + &table_esms_by_user_by_event_name::m_share, + &table_esms_by_host_by_event_name::m_share, + &table_esms_global_by_event_name::m_share, + &table_esms_by_digest::m_share, + &table_esms_by_program::m_share, + + &table_events_transactions_current::m_share, + &table_events_transactions_history::m_share, + &table_events_transactions_history_long::m_share, + &table_ets_by_thread_by_event_name::m_share, + &table_ets_by_account_by_event_name::m_share, + &table_ets_by_user_by_event_name::m_share, + &table_ets_by_host_by_event_name::m_share, + &table_ets_global_by_event_name::m_share, + + &table_users::m_share, + &table_accounts::m_share, + &table_hosts::m_share, + + &table_socket_instances::m_share, + &table_socket_summary_by_instance::m_share, + &table_socket_summary_by_event_name::m_share, + + &table_session_connect_attrs::m_share, + &table_session_account_connect_attrs::m_share, + + &table_mems_global_by_event_name::m_share, + &table_mems_by_account_by_event_name::m_share, + &table_mems_by_host_by_event_name::m_share, + &table_mems_by_thread_by_event_name::m_share, + &table_mems_by_user_by_event_name::m_share, + &table_table_handles::m_share, + &table_metadata_locks::m_share, + +#ifdef HAVE_REPLICATION + &table_replication_connection_configuration::m_share, + //&table_replication_group_members::m_share, + //&table_replication_connection_status::m_share, + &table_replication_applier_configuration::m_share, + &table_replication_applier_status::m_share, + &table_replication_applier_status_by_coordinator::m_share, + &table_replication_applier_status_by_worker::m_share, + //&table_replication_group_member_stats::m_share, +#endif + + &table_prepared_stmt_instances::m_share, + + &table_uvar_by_thread::m_share, + &table_status_by_account::m_share, + &table_status_by_host::m_share, + &table_status_by_thread::m_share, + &table_status_by_user::m_share, + &table_global_status::m_share, + &table_session_status::m_share, + + //&table_variables_by_thread::m_share, + //&table_global_variables::m_share, + //&table_session_variables::m_share, + + //&table_processlist::m_share, + + NULL +}; + +/** Error reporting for schema integrity checks. */ +class PFS_silent_check_intact : public Table_check_intact +{ +protected: + virtual void report_error(uint code, const char *fmt, ...) {} + +public: + PFS_silent_check_intact() + {} + + ~PFS_silent_check_intact() + {} +}; + + + +/** Initialize all the table share locks. */ +void PFS_engine_table_share::init_all_locks(void) +{ + PFS_engine_table_share **current; + + for (current= &all_shares[0]; (*current) != NULL; current++) + thr_lock_init((*current)->m_thr_lock_ptr); +} + +/** Delete all the table share locks. */ +void PFS_engine_table_share::delete_all_locks(void) +{ + PFS_engine_table_share **current; + + for (current= &all_shares[0]; (*current) != NULL; current++) + thr_lock_delete((*current)->m_thr_lock_ptr); +} + +ha_rows PFS_engine_table_share::get_row_count(void) const +{ + return m_get_row_count(); +} + +int PFS_engine_table_share::write_row(TABLE *table, const unsigned char *buf, + Field **fields) const +{ + if (m_write_row == NULL) + { + return HA_ERR_WRONG_COMMAND; + } + + /* We internally read from Fields to support the write interface */ + MY_BITMAP *org_bitmap= dbug_tmp_use_all_columns(table, &table->read_set); + int result= m_write_row(table, buf, fields); + dbug_tmp_restore_column_map(&table->read_set, org_bitmap); + + return result; +} + +static int compare_table_names(const char *name1, const char *name2) +{ + /* + The performance schema is implemented as a storage engine, in memory. + The current storage engine interface exposed by the server, + and in particular handlerton::discover, uses 'FRM' files to describe a + table structure, which are later stored on disk, by the server, + in ha_create_table_from_engine(). + Because the table metadata is stored on disk, the table naming rules + used by the performance schema then have to comply with the constraints + imposed by the disk storage, and in particular with lower_case_table_names. + Once the server is changed to be able to discover a table in a storage engine + and then open the table without storing a FRM file on disk, this constraint + on the performance schema will be lifted, and the naming logic can be relaxed + to be simply my_strcasecmp(system_charset_info, name1, name2). + */ + if (lower_case_table_names) + return strcasecmp(name1, name2); + return strcmp(name1, name2); +} + +/** + Find a table share by name. + @param name The table name + @return table share +*/ +const PFS_engine_table_share* +PFS_engine_table::find_engine_table_share(const char *name) +{ + DBUG_ENTER("PFS_engine_table::find_table_share"); + + PFS_engine_table_share **current; + + for (current= &all_shares[0]; (*current) != NULL; current++) + { + if (compare_table_names(name, (*current)->m_name.str) == 0) + DBUG_RETURN(*current); + } + + DBUG_RETURN(NULL); +} + +/** + Read a table row. + @param table Table handle + @param buf Row buffer + @param fields Table fields + @return 0 on success +*/ +int PFS_engine_table::read_row(TABLE *table, + unsigned char *buf, + Field **fields) +{ + Field *f; + Field **fields_reset; + + /* We must read all columns in case a table is opened for update */ + bool read_all= !bitmap_is_clear_all(table->write_set); + + /* We internally write to Fields to support the read interface */ + MY_BITMAP *org_bitmap= dbug_tmp_use_all_columns(table, &table->write_set); + + /* + Some callers of the storage engine interface do not honor the + f->is_null() flag, and will attempt to read the data itself. + A known offender is mysql_checksum_table(). + For robustness, reset every field. + */ + for (fields_reset= fields; (f= *fields_reset) ; fields_reset++) + f->reset(); + + int result= read_row_values(table, buf, fields, read_all); + dbug_tmp_restore_column_map(&table->write_set, org_bitmap); + + return result; +} + +/** + Update a table row. + @param table Table handle + @param old_buf old row buffer + @param new_buf new row buffer + @param fields Table fields + @return 0 on success +*/ +int PFS_engine_table::update_row(TABLE *table, + const unsigned char *old_buf, + const unsigned char *new_buf, + Field **fields) +{ + /* We internally read from Fields to support the write interface */ + MY_BITMAP *org_bitmap= dbug_tmp_use_all_columns(table, &table->read_set); + int result= update_row_values(table, old_buf, new_buf, fields); + dbug_tmp_restore_column_map(&table->read_set, org_bitmap); + + return result; +} + +int PFS_engine_table::delete_row(TABLE *table, + const unsigned char *buf, + Field **fields) +{ + /* We internally read from Fields to support the delete interface */ + MY_BITMAP *org_bitmap= dbug_tmp_use_all_columns(table, &table->read_set); + int result= delete_row_values(table, buf, fields); + dbug_tmp_restore_column_map(&table->read_set, org_bitmap); + + return result; +} + +int PFS_engine_table::delete_row_values(TABLE *, + const unsigned char *, + Field **) +{ + return HA_ERR_WRONG_COMMAND; +} + +/** + Get the position of the current row. + @param [out] ref position +*/ +void PFS_engine_table::get_position(void *ref) +{ + memcpy(ref, m_pos_ptr, m_share_ptr->m_ref_length); +} + +/** + Set the table cursor at a given position. + @param [in] ref position +*/ +void PFS_engine_table::set_position(const void *ref) +{ + memcpy(m_pos_ptr, ref, m_share_ptr->m_ref_length); +} + +/** + Get the timer normalizer and class type for the current row. + @param [in] instr_class class +*/ +void PFS_engine_table::get_normalizer(PFS_instr_class *instr_class) +{ + if (instr_class->m_type != m_class_type) + { + m_normalizer= time_normalizer::get(*instr_class->m_timer); + m_class_type= instr_class->m_type; + } +} + +void PFS_engine_table::set_field_long(Field *f, long value) +{ + assert(f->real_type() == MYSQL_TYPE_LONG); + Field_long *f2= (Field_long*) f; + f2->store(value, false); +} + +void PFS_engine_table::set_field_ulong(Field *f, ulong value) +{ + assert(f->real_type() == MYSQL_TYPE_LONG); + Field_long *f2= (Field_long*) f; + f2->store(value, true); +} + +void PFS_engine_table::set_field_longlong(Field *f, longlong value) +{ + assert(f->real_type() == MYSQL_TYPE_LONGLONG); + Field_longlong *f2= (Field_longlong*) f; + f2->store(value, false); +} + +void PFS_engine_table::set_field_ulonglong(Field *f, ulonglong value) +{ + assert(f->real_type() == MYSQL_TYPE_LONGLONG); + Field_longlong *f2= (Field_longlong*) f; + f2->store(value, true); +} + +void PFS_engine_table::set_field_char_utf8(Field *f, const char* str, + uint len) +{ + assert(f->real_type() == MYSQL_TYPE_STRING); + Field_string *f2= (Field_string*) f; + f2->store(str, len, &my_charset_utf8mb3_bin); +} + +void PFS_engine_table::set_field_varchar(Field *f, + const CHARSET_INFO *cs, + const char* str, + uint len) +{ + assert(f->real_type() == MYSQL_TYPE_VARCHAR); + Field_varstring *f2= (Field_varstring*) f; + f2->store(str, len, cs); +} + +void PFS_engine_table::set_field_varchar_utf8(Field *f, const char* str, + uint len) +{ + assert(f->real_type() == MYSQL_TYPE_VARCHAR); + Field_varstring *f2= (Field_varstring*) f; + f2->store(str, len, &my_charset_utf8mb3_bin); +} + +void PFS_engine_table::set_field_longtext_utf8(Field *f, const char* str, + uint len) +{ + assert(f->real_type() == MYSQL_TYPE_BLOB); + Field_blob *f2= (Field_blob*) f; + f2->store(str, len, &my_charset_utf8mb3_bin); +} + +void PFS_engine_table::set_field_blob(Field *f, const char* val, + uint len) +{ + assert(f->real_type() == MYSQL_TYPE_BLOB); + Field_blob *f2= (Field_blob*) f; + f2->store(val, len, &my_charset_utf8mb3_bin); +} + +void PFS_engine_table::set_field_enum(Field *f, ulonglong value) +{ + assert(f->real_type() == MYSQL_TYPE_ENUM); + Field_enum *f2= (Field_enum*) f; + f2->store_type(value); +} + +void PFS_engine_table::set_field_timestamp(Field *f, ulonglong value) +{ + DBUG_ASSERT(f->type_handler()->is_timestamp_type()); + Field_timestamp *f2= (Field_timestamp*) f; + f2->store_TIME((long)(value / 1000000), (value % 1000000)); +} + +void PFS_engine_table::set_field_double(Field *f, double value) +{ + assert(f->real_type() == MYSQL_TYPE_DOUBLE); + Field_double *f2= (Field_double*) f; + f2->store(value); +} + +ulonglong PFS_engine_table::get_field_enum(Field *f) +{ + assert(f->real_type() == MYSQL_TYPE_ENUM); + Field_enum *f2= (Field_enum*) f; + return f2->val_int(); +} + +String* +PFS_engine_table::get_field_char_utf8(Field *f, String *val) +{ + assert(f->real_type() == MYSQL_TYPE_STRING); + Field_string *f2= (Field_string*) f; + val= f2->val_str(NULL, val); + return val; +} + +String* +PFS_engine_table::get_field_varchar_utf8(Field *f, String *val) +{ + assert(f->real_type() == MYSQL_TYPE_VARCHAR); + Field_varstring *f2= (Field_varstring*) f; + val= f2->val_str(NULL, val); + return val; +} + +int PFS_engine_table::update_row_values(TABLE *, + const unsigned char *, + const unsigned char *, + Field **) +{ + return HA_ERR_WRONG_COMMAND; +} + +/** Implementation of internal ACL checks, for the performance schema. */ +class PFS_internal_schema_access : public ACL_internal_schema_access +{ +public: + PFS_internal_schema_access() = default; + + ~PFS_internal_schema_access() = default; + + ACL_internal_access_result check(privilege_t want_access, + privilege_t *save_priv) const; + + const ACL_internal_table_access *lookup(const char *name) const; +}; + +ACL_internal_access_result +PFS_internal_schema_access::check(privilege_t want_access, + privilege_t *save_priv) const +{ + const privilege_t always_forbidden= /* CREATE_ACL | */ REFERENCES_ACL + | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | EXECUTE_ACL + | CREATE_VIEW_ACL | SHOW_VIEW_ACL | CREATE_PROC_ACL | ALTER_PROC_ACL + | EVENT_ACL | TRIGGER_ACL ; + + if (unlikely((want_access & always_forbidden) != NO_ACL)) + return ACL_INTERNAL_ACCESS_DENIED; + + /* + Proceed with regular grant tables, + to give administrative control to the DBA. + */ + return ACL_INTERNAL_ACCESS_CHECK_GRANT; +} + +const ACL_internal_table_access * +PFS_internal_schema_access::lookup(const char *name) const +{ + const PFS_engine_table_share* share; + share= PFS_engine_table::find_engine_table_share(name); + if (share) + return share->m_acl; + /* + Do not return NULL, it would mean we are not interested + in privilege checks for unknown tables. + Instead, return an object that denies every actions, + to prevent users for creating their own tables in the + performance_schema database schema. + */ + return &pfs_unknown_acl; +} + +PFS_internal_schema_access pfs_internal_access; + +void initialize_performance_schema_acl(bool bootstrap) +{ + /* + ACL is always enforced, even if the performance schema + is not enabled (the tables are still visible). + */ + if (! bootstrap) + { + ACL_internal_schema_registry::register_schema(&PERFORMANCE_SCHEMA_str, + &pfs_internal_access); + } +} + +static bool allow_drop_table_privilege() { + /* + The same DROP_ACL privilege is used for different statements, + in particular: + - TRUNCATE TABLE + - DROP TABLE + - ALTER TABLE + Here, we want to prevent DROP / ALTER while allowing TRUNCATE. + Note that we must also allow GRANT to transfer the truncate privilege. + */ + THD *thd= current_thd; + if (thd == NULL) { + return false; + } + + assert(thd->lex != NULL); + if ((thd->lex->sql_command != SQLCOM_TRUNCATE) && + (thd->lex->sql_command != SQLCOM_GRANT) && + (thd->lex->sql_command != SQLCOM_REVOKE)) { + return false; + } + + return true; +} + + +PFS_readonly_acl pfs_readonly_acl; + +ACL_internal_access_result +PFS_readonly_acl::check(privilege_t want_access, privilege_t *save_priv) const +{ + const privilege_t always_forbidden= INSERT_ACL | UPDATE_ACL | DELETE_ACL + | /* CREATE_ACL | */ REFERENCES_ACL | INDEX_ACL | ALTER_ACL + | CREATE_VIEW_ACL | SHOW_VIEW_ACL | TRIGGER_ACL | LOCK_TABLES_ACL; + + if (unlikely((want_access & always_forbidden) != NO_ACL)) + return ACL_INTERNAL_ACCESS_DENIED; + + return ACL_INTERNAL_ACCESS_CHECK_GRANT; +} + + +PFS_readonly_world_acl pfs_readonly_world_acl; + +ACL_internal_access_result +PFS_readonly_world_acl::check(privilege_t want_access, privilege_t *save_priv) const +{ + ACL_internal_access_result res= PFS_readonly_acl::check(want_access, save_priv); + if (res == ACL_INTERNAL_ACCESS_CHECK_GRANT) + { + if (want_access == SELECT_ACL) + res= ACL_INTERNAL_ACCESS_GRANTED; + } + return res; +} + +PFS_readonly_processlist_acl pfs_readonly_processlist_acl; + +ACL_internal_access_result PFS_readonly_processlist_acl::check( + privilege_t want_access, privilege_t *save_priv) const { + ACL_internal_access_result res = + PFS_readonly_acl::check(want_access, save_priv); + + if ((res == ACL_INTERNAL_ACCESS_CHECK_GRANT) && (want_access == SELECT_ACL)) { + THD *thd = current_thd; + if (thd != NULL) { + if (thd->lex->sql_command == SQLCOM_SHOW_PROCESSLIST || + thd->lex->sql_command == SQLCOM_SELECT) { + /* + For compatibility with the historical + SHOW PROCESSLIST command, + SHOW PROCESSLIST does not require a + SELECT privilege on table performance_schema.processlist, + when rewriting the query using table processlist. + */ + return ACL_INTERNAL_ACCESS_GRANTED; + } + } + } + + return res; +} + + +PFS_truncatable_acl pfs_truncatable_acl; + +ACL_internal_access_result +PFS_truncatable_acl::check(privilege_t want_access, privilege_t *save_priv) const +{ + const privilege_t always_forbidden= INSERT_ACL | UPDATE_ACL | DELETE_ACL + | /* CREATE_ACL | */ REFERENCES_ACL | INDEX_ACL | ALTER_ACL + | CREATE_VIEW_ACL | SHOW_VIEW_ACL | TRIGGER_ACL | LOCK_TABLES_ACL; + + if (unlikely((want_access & always_forbidden) != NO_ACL)) + return ACL_INTERNAL_ACCESS_DENIED; + + return ACL_INTERNAL_ACCESS_CHECK_GRANT; +} + + +PFS_truncatable_world_acl pfs_truncatable_world_acl; + +ACL_internal_access_result +PFS_truncatable_world_acl::check(privilege_t want_access, privilege_t *save_priv) const +{ + ACL_internal_access_result res= PFS_truncatable_acl::check(want_access, save_priv); + if (res == ACL_INTERNAL_ACCESS_CHECK_GRANT) + { + if (want_access == DROP_ACL) + { + if (allow_drop_table_privilege()) + res= ACL_INTERNAL_ACCESS_GRANTED; + } + else if (want_access == SELECT_ACL) + res= ACL_INTERNAL_ACCESS_GRANTED; + } + return res; +} + + +PFS_updatable_acl pfs_updatable_acl; + +ACL_internal_access_result +PFS_updatable_acl::check(privilege_t want_access, privilege_t *save_priv) const +{ + const privilege_t always_forbidden= INSERT_ACL | DELETE_ACL + | /* CREATE_ACL | */ REFERENCES_ACL | INDEX_ACL | ALTER_ACL + | CREATE_VIEW_ACL | SHOW_VIEW_ACL | TRIGGER_ACL; + + if (unlikely((want_access & always_forbidden) != NO_ACL)) + return ACL_INTERNAL_ACCESS_DENIED; + + return ACL_INTERNAL_ACCESS_CHECK_GRANT; +} + +PFS_editable_acl pfs_editable_acl; + +ACL_internal_access_result +PFS_editable_acl::check(privilege_t want_access, privilege_t *save_priv) const +{ + const privilege_t always_forbidden= /* CREATE_ACL | */ REFERENCES_ACL + | INDEX_ACL | ALTER_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | TRIGGER_ACL; + + if (unlikely((want_access & always_forbidden) != NO_ACL)) + return ACL_INTERNAL_ACCESS_DENIED; + + return ACL_INTERNAL_ACCESS_CHECK_GRANT; +} + +PFS_unknown_acl pfs_unknown_acl; + +ACL_internal_access_result +PFS_unknown_acl::check(privilege_t want_access, privilege_t *save_priv) const +{ + const privilege_t always_forbidden= CREATE_ACL + | REFERENCES_ACL | INDEX_ACL | ALTER_ACL + | CREATE_VIEW_ACL | TRIGGER_ACL; + + if (unlikely((want_access & always_forbidden) != NO_ACL)) + return ACL_INTERNAL_ACCESS_DENIED; + + /* + There is no point in hiding (by enforcing ACCESS_DENIED for SELECT_ACL + on performance_schema.*) tables that do not exist anyway. + When SELECT_ACL is granted on performance_schema.* or *.*, + SELECT * from performance_schema.wrong_table + will fail with a more understandable ER_NO_SUCH_TABLE error, + instead of ER_TABLEACCESS_DENIED_ERROR. + The same goes for other DML (INSERT_ACL | UPDATE_ACL | DELETE_ACL), + for ease of use: error messages will be less surprising. + */ + return ACL_INTERNAL_ACCESS_CHECK_GRANT; +} + +/** + SHOW ENGINE PERFORMANCE_SCHEMA STATUS. + @param hton Storage engine handler + @param thd Current thread + @param print Print function + @param stat status to show +*/ +bool pfs_show_status(handlerton *hton, THD *thd, + stat_print_fn *print, enum ha_stat_type stat) +{ + char buf[1024]; + uint buflen; + const char *name; + int i; + size_t size; + + DBUG_ENTER("pfs_show_status"); + + /* + Note about naming conventions: + - Internal buffers exposed as a table in the performance schema are named + after the table, as in 'events_waits_current' + - Internal buffers not exposed by a table are named with parenthesis, + as in '(pfs_mutex_class)'. + */ + if (stat != HA_ENGINE_STATUS) + DBUG_RETURN(false); + + size_t total_memory= 0; + + for (i=0; /* empty */; i++) + { + switch (i){ + case 0: + name= "events_waits_current.size"; + size= sizeof(PFS_events_waits); + break; + case 1: + name= "events_waits_current.count"; + size= WAIT_STACK_SIZE * global_thread_container.get_row_count(); + break; + case 2: + name= "events_waits_history.size"; + size= sizeof(PFS_events_waits); + break; + case 3: + name= "events_waits_history.count"; + size= events_waits_history_per_thread * global_thread_container.get_row_count(); + break; + case 4: + name= "events_waits_history.memory"; + size= events_waits_history_per_thread * global_thread_container.get_row_count() + * sizeof(PFS_events_waits); + total_memory+= size; + break; + case 5: + name= "events_waits_history_long.size"; + size= sizeof(PFS_events_waits); + break; + case 6: + name= "events_waits_history_long.count"; + size= events_waits_history_long_size; + break; + case 7: + name= "events_waits_history_long.memory"; + size= events_waits_history_long_size * sizeof(PFS_events_waits); + total_memory+= size; + break; + case 8: + name= "(pfs_mutex_class).size"; + size= sizeof(PFS_mutex_class); + break; + case 9: + name= "(pfs_mutex_class).count"; + size= mutex_class_max; + break; + case 10: + name= "(pfs_mutex_class).memory"; + size= mutex_class_max * sizeof(PFS_mutex_class); + total_memory+= size; + break; + case 11: + name= "(pfs_rwlock_class).size"; + size= sizeof(PFS_rwlock_class); + break; + case 12: + name= "(pfs_rwlock_class).count"; + size= rwlock_class_max; + break; + case 13: + name= "(pfs_rwlock_class).memory"; + size= rwlock_class_max * sizeof(PFS_rwlock_class); + total_memory+= size; + break; + case 14: + name= "(pfs_cond_class).size"; + size= sizeof(PFS_cond_class); + break; + case 15: + name= "(pfs_cond_class).count"; + size= cond_class_max; + break; + case 16: + name= "(pfs_cond_class).memory"; + size= cond_class_max * sizeof(PFS_cond_class); + total_memory+= size; + break; + case 17: + name= "(pfs_thread_class).size"; + size= sizeof(PFS_thread_class); + break; + case 18: + name= "(pfs_thread_class).count"; + size= thread_class_max; + break; + case 19: + name= "(pfs_thread_class).memory"; + size= thread_class_max * sizeof(PFS_thread_class); + total_memory+= size; + break; + case 20: + name= "(pfs_file_class).size"; + size= sizeof(PFS_file_class); + break; + case 21: + name= "(pfs_file_class).count"; + size= file_class_max; + break; + case 22: + name= "(pfs_file_class).memory"; + size= file_class_max * sizeof(PFS_file_class); + total_memory+= size; + break; + case 23: + name= "mutex_instances.size"; + size= global_mutex_container.get_row_size(); + break; + case 24: + name= "mutex_instances.count"; + size= global_mutex_container.get_row_count(); + break; + case 25: + name= "mutex_instances.memory"; + size= global_mutex_container.get_memory(); + total_memory+= size; + break; + case 26: + name= "rwlock_instances.size"; + size= global_rwlock_container.get_row_size(); + break; + case 27: + name= "rwlock_instances.count"; + size= global_rwlock_container.get_row_count(); + break; + case 28: + name= "rwlock_instances.memory"; + size= global_rwlock_container.get_memory(); + total_memory+= size; + break; + case 29: + name= "cond_instances.size"; + size= global_cond_container.get_row_size(); + break; + case 30: + name= "cond_instances.count"; + size= global_cond_container.get_row_count(); + break; + case 31: + name= "cond_instances.memory"; + size= global_cond_container.get_memory(); + total_memory+= size; + break; + case 32: + name= "threads.size"; + size= global_thread_container.get_row_size(); + break; + case 33: + name= "threads.count"; + size= global_thread_container.get_row_count(); + break; + case 34: + name= "threads.memory"; + size= global_thread_container.get_memory(); + total_memory+= size; + break; + case 35: + name= "file_instances.size"; + size= global_file_container.get_row_size(); + break; + case 36: + name= "file_instances.count"; + size= global_file_container.get_row_count(); + break; + case 37: + name= "file_instances.memory"; + size= global_file_container.get_memory(); + total_memory+= size; + break; + case 38: + name= "(pfs_file_handle).size"; + size= sizeof(PFS_file*); + break; + case 39: + name= "(pfs_file_handle).count"; + size= file_handle_max; + break; + case 40: + name= "(pfs_file_handle).memory"; + size= file_handle_max * sizeof(PFS_file*); + total_memory+= size; + break; + case 41: + name= "events_waits_summary_by_thread_by_event_name.size"; + size= sizeof(PFS_single_stat); + break; + case 42: + name= "events_waits_summary_by_thread_by_event_name.count"; + size= global_thread_container.get_row_count() * wait_class_max; + break; + case 43: + name= "events_waits_summary_by_thread_by_event_name.memory"; + size= global_thread_container.get_row_count() * wait_class_max * sizeof(PFS_single_stat); + total_memory+= size; + break; + case 44: + name= "(pfs_table_share).size"; + size= global_table_share_container.get_row_size(); + break; + case 45: + name= "(pfs_table_share).count"; + size= global_table_share_container.get_row_count(); + break; + case 46: + name= "(pfs_table_share).memory"; + size= global_table_share_container.get_memory(); + total_memory+= size; + break; + case 47: + name= "(pfs_table).size"; + size= global_table_container.get_row_size(); + break; + case 48: + name= "(pfs_table).count"; + size= global_table_container.get_row_count(); + break; + case 49: + name= "(pfs_table).memory"; + size= global_table_container.get_memory(); + total_memory+= size; + break; + case 50: + name= "setup_actors.size"; + size= global_setup_actor_container.get_row_size(); + break; + case 51: + name= "setup_actors.count"; + size= global_setup_actor_container.get_row_count(); + break; + case 52: + name= "setup_actors.memory"; + size= global_setup_actor_container.get_memory(); + total_memory+= size; + break; + case 53: + name= "setup_objects.size"; + size= global_setup_object_container.get_row_size(); + break; + case 54: + name= "setup_objects.count"; + size= global_setup_object_container.get_row_count(); + break; + case 55: + name= "setup_objects.memory"; + size= global_setup_object_container.get_memory(); + total_memory+= size; + break; + case 56: + name= "(pfs_account).size"; + size= global_account_container.get_row_size(); + break; + case 57: + name= "(pfs_account).count"; + size= global_account_container.get_row_count(); + break; + case 58: + name= "(pfs_account).memory"; + size= global_account_container.get_memory(); + total_memory+= size; + break; + case 59: + name= "events_waits_summary_by_account_by_event_name.size"; + size= sizeof(PFS_single_stat); + break; + case 60: + name= "events_waits_summary_by_account_by_event_name.count"; + size= global_account_container.get_row_count() * wait_class_max; + break; + case 61: + name= "events_waits_summary_by_account_by_event_name.memory"; + size= global_account_container.get_row_count() * wait_class_max * sizeof(PFS_single_stat); + total_memory+= size; + break; + case 62: + name= "events_waits_summary_by_user_by_event_name.size"; + size= sizeof(PFS_single_stat); + break; + case 63: + name= "events_waits_summary_by_user_by_event_name.count"; + size= global_user_container.get_row_count() * wait_class_max; + break; + case 64: + name= "events_waits_summary_by_user_by_event_name.memory"; + size= global_user_container.get_row_count() * wait_class_max * sizeof(PFS_single_stat); + total_memory+= size; + break; + case 65: + name= "events_waits_summary_by_host_by_event_name.size"; + size= sizeof(PFS_single_stat); + break; + case 66: + name= "events_waits_summary_by_host_by_event_name.count"; + size= global_host_container.get_row_count() * wait_class_max; + break; + case 67: + name= "events_waits_summary_by_host_by_event_name.memory"; + size= global_host_container.get_row_count() * wait_class_max * sizeof(PFS_single_stat); + total_memory+= size; + break; + case 68: + name= "(pfs_user).size"; + size= global_user_container.get_row_size(); + break; + case 69: + name= "(pfs_user).count"; + size= global_user_container.get_row_count(); + break; + case 70: + name= "(pfs_user).memory"; + size= global_user_container.get_memory(); + total_memory+= size; + break; + case 71: + name= "(pfs_host).size"; + size= global_host_container.get_row_size(); + break; + case 72: + name= "(pfs_host).count"; + size= global_host_container.get_row_count(); + break; + case 73: + name= "(pfs_host).memory"; + size= global_host_container.get_memory(); + total_memory+= size; + break; + case 74: + name= "(pfs_stage_class).size"; + size= sizeof(PFS_stage_class); + break; + case 75: + name= "(pfs_stage_class).count"; + size= stage_class_max; + break; + case 76: + name= "(pfs_stage_class).memory"; + size= stage_class_max * sizeof(PFS_stage_class); + total_memory+= size; + break; + case 77: + name= "events_stages_history.size"; + size= sizeof(PFS_events_stages); + break; + case 78: + name= "events_stages_history.count"; + size= events_stages_history_per_thread * global_thread_container.get_row_count(); + break; + case 79: + name= "events_stages_history.memory"; + size= events_stages_history_per_thread * global_thread_container.get_row_count() + * sizeof(PFS_events_stages); + total_memory+= size; + break; + case 80: + name= "events_stages_history_long.size"; + size= sizeof(PFS_events_stages); + break; + case 81: + name= "events_stages_history_long.count"; + size= events_stages_history_long_size; + break; + case 82: + name= "events_stages_history_long.memory"; + size= events_stages_history_long_size * sizeof(PFS_events_stages); + total_memory+= size; + break; + case 83: + name= "events_stages_summary_by_thread_by_event_name.size"; + size= sizeof(PFS_stage_stat); + break; + case 84: + name= "events_stages_summary_by_thread_by_event_name.count"; + size= global_thread_container.get_row_count() * stage_class_max; + break; + case 85: + name= "events_stages_summary_by_thread_by_event_name.memory"; + size= global_thread_container.get_row_count() * stage_class_max * sizeof(PFS_stage_stat); + total_memory+= size; + break; + case 86: + name= "events_stages_summary_global_by_event_name.size"; + size= sizeof(PFS_stage_stat); + break; + case 87: + name= "events_stages_summary_global_by_event_name.count"; + size= stage_class_max; + break; + case 88: + name= "events_stages_summary_global_by_event_name.memory"; + size= stage_class_max * sizeof(PFS_stage_stat); + total_memory+= size; + break; + case 89: + name= "events_stages_summary_by_account_by_event_name.size"; + size= sizeof(PFS_stage_stat); + break; + case 90: + name= "events_stages_summary_by_account_by_event_name.count"; + size= global_account_container.get_row_count() * stage_class_max; + break; + case 91: + name= "events_stages_summary_by_account_by_event_name.memory"; + size= global_account_container.get_row_count() * stage_class_max * sizeof(PFS_stage_stat); + total_memory+= size; + break; + case 92: + name= "events_stages_summary_by_user_by_event_name.size"; + size= sizeof(PFS_stage_stat); + break; + case 93: + name= "events_stages_summary_by_user_by_event_name.count"; + size= global_user_container.get_row_count() * stage_class_max; + break; + case 94: + name= "events_stages_summary_by_user_by_event_name.memory"; + size= global_user_container.get_row_count() * stage_class_max * sizeof(PFS_stage_stat); + total_memory+= size; + break; + case 95: + name= "events_stages_summary_by_host_by_event_name.size"; + size= sizeof(PFS_stage_stat); + break; + case 96: + name= "events_stages_summary_by_host_by_event_name.count"; + size= global_host_container.get_row_count() * stage_class_max; + break; + case 97: + name= "events_stages_summary_by_host_by_event_name.memory"; + size= global_host_container.get_row_count() * stage_class_max * sizeof(PFS_stage_stat); + total_memory+= size; + break; + case 98: + name= "(pfs_statement_class).size"; + size= sizeof(PFS_statement_class); + break; + case 99: + name= "(pfs_statement_class).count"; + size= statement_class_max; + break; + case 100: + name= "(pfs_statement_class).memory"; + size= statement_class_max * sizeof(PFS_statement_class); + total_memory+= size; + break; + case 101: + name= "events_statements_history.size"; + size= sizeof(PFS_events_statements); + break; + case 102: + name= "events_statements_history.count"; + size= events_statements_history_per_thread * global_thread_container.get_row_count(); + break; + case 103: + name= "events_statements_history.memory"; + size= events_statements_history_per_thread * global_thread_container.get_row_count() + * sizeof(PFS_events_statements); + total_memory+= size; + break; + case 104: + name= "events_statements_history_long.size"; + size= sizeof(PFS_events_statements); + break; + case 105: + name= "events_statements_history_long.count"; + size= events_statements_history_long_size; + break; + case 106: + name= "events_statements_history_long.memory"; + size= events_statements_history_long_size * (sizeof(PFS_events_statements)); + total_memory+= size; + break; + case 107: + name= "events_statements_summary_by_thread_by_event_name.size"; + size= sizeof(PFS_statement_stat); + break; + case 108: + name= "events_statements_summary_by_thread_by_event_name.count"; + size= global_thread_container.get_row_count() * statement_class_max; + break; + case 109: + name= "events_statements_summary_by_thread_by_event_name.memory"; + size= global_thread_container.get_row_count() * statement_class_max * sizeof(PFS_statement_stat); + total_memory+= size; + break; + case 110: + name= "events_statements_summary_global_by_event_name.size"; + size= sizeof(PFS_statement_stat); + break; + case 111: + name= "events_statements_summary_global_by_event_name.count"; + size= statement_class_max; + break; + case 112: + name= "events_statements_summary_global_by_event_name.memory"; + size= statement_class_max * sizeof(PFS_statement_stat); + total_memory+= size; + break; + case 113: + name= "events_statements_summary_by_account_by_event_name.size"; + size= sizeof(PFS_statement_stat); + break; + case 114: + name= "events_statements_summary_by_account_by_event_name.count"; + size= global_account_container.get_row_count() * statement_class_max; + break; + case 115: + name= "events_statements_summary_by_account_by_event_name.memory"; + size= global_account_container.get_row_count() * statement_class_max * sizeof(PFS_statement_stat); + total_memory+= size; + break; + case 116: + name= "events_statements_summary_by_user_by_event_name.size"; + size= sizeof(PFS_statement_stat); + break; + case 117: + name= "events_statements_summary_by_user_by_event_name.count"; + size= global_user_container.get_row_count() * statement_class_max; + break; + case 118: + name= "events_statements_summary_by_user_by_event_name.memory"; + size= global_user_container.get_row_count() * statement_class_max * sizeof(PFS_statement_stat); + total_memory+= size; + break; + case 119: + name= "events_statements_summary_by_host_by_event_name.size"; + size= sizeof(PFS_statement_stat); + break; + case 120: + name= "events_statements_summary_by_host_by_event_name.count"; + size= global_host_container.get_row_count() * statement_class_max; + break; + case 121: + name= "events_statements_summary_by_host_by_event_name.memory"; + size= global_host_container.get_row_count() * statement_class_max * sizeof(PFS_statement_stat); + total_memory+= size; + break; + case 122: + name= "events_statements_current.size"; + size= sizeof(PFS_events_statements); + break; + case 123: + name= "events_statements_current.count"; + size= global_thread_container.get_row_count() * statement_stack_max; + break; + case 124: + name= "events_statements_current.memory"; + size= global_thread_container.get_row_count() * statement_stack_max * sizeof(PFS_events_statements); + total_memory+= size; + break; + case 125: + name= "(pfs_socket_class).size"; + size= sizeof(PFS_socket_class); + break; + case 126: + name= "(pfs_socket_class).count"; + size= socket_class_max; + break; + case 127: + name= "(pfs_socket_class).memory"; + size= socket_class_max * sizeof(PFS_socket_class); + total_memory+= size; + break; + case 128: + name= "socket_instances.size"; + size= global_socket_container.get_row_size(); + break; + case 129: + name= "socket_instances.count"; + size= global_socket_container.get_row_count(); + break; + case 130: + name= "socket_instances.memory"; + size= global_socket_container.get_memory(); + total_memory+= size; + break; + case 131: + name= "events_statements_summary_by_digest.size"; + size= sizeof(PFS_statements_digest_stat); + break; + case 132: + name= "events_statements_summary_by_digest.count"; + size= digest_max; + break; + case 133: + name= "events_statements_summary_by_digest.memory"; + size= digest_max * (sizeof(PFS_statements_digest_stat)); + total_memory+= size; + break; + case 134: + name= "events_statements_summary_by_program.size"; + size= global_program_container.get_row_size(); + break; + case 135: + name= "events_statements_summary_by_program.count"; + size= global_program_container.get_row_count(); + break; + case 136: + name= "events_statements_summary_by_program.memory"; + size= global_program_container.get_memory(); + total_memory+= size; + break; + case 137: + name= "session_connect_attrs.size"; + size= global_thread_container.get_row_count(); + break; + case 138: + name= "session_connect_attrs.count"; + size= session_connect_attrs_size_per_thread; + break; + case 139: + name= "session_connect_attrs.memory"; + size= global_thread_container.get_row_count() * session_connect_attrs_size_per_thread; + total_memory+= size; + break; + case 140: + name= "prepared_statements_instances.size"; + size= global_prepared_stmt_container.get_row_size(); + break; + case 141: + name= "prepared_statements_instances.count"; + size= global_prepared_stmt_container.get_row_count(); + break; + case 142: + name= "prepared_statements_instances.memory"; + size= global_prepared_stmt_container.get_memory(); + total_memory+= size; + break; + + case 143: + name= "(account_hash).count"; + size= account_hash.count; + break; + case 144: + name= "(account_hash).size"; + size= account_hash.size; + break; + case 145: + name= "(digest_hash).count"; + size= digest_hash.count; + break; + case 146: + name= "(digest_hash).size"; + size= digest_hash.size; + break; + case 147: + name= "(filename_hash).count"; + size= pfs_filename_hash.count; + break; + case 148: + name= "(filename_hash).size"; + size= pfs_filename_hash.size; + break; + case 149: + name= "(host_hash).count"; + size= host_hash.count; + break; + case 150: + name= "(host_hash).size"; + size= host_hash.size; + break; + case 151: + name= "(setup_actor_hash).count"; + size= setup_actor_hash.count; + break; + case 152: + name= "(setup_actor_hash).size"; + size= setup_actor_hash.size; + break; + case 153: + name= "(setup_object_hash).count"; + size= setup_object_hash.count; + break; + case 154: + name= "(setup_object_hash).size"; + size= setup_object_hash.size; + break; + case 155: + name= "(table_share_hash).count"; + size= table_share_hash.count; + break; + case 156: + name= "(table_share_hash).size"; + size= table_share_hash.size; + break; + case 157: + name= "(user_hash).count"; + size= user_hash.count; + break; + case 158: + name= "(user_hash).size"; + size= user_hash.size; + break; + case 159: + name= "(program_hash).count"; + size= program_hash.count; + break; + case 160: + name= "(program_hash).size"; + size= program_hash.size; + break; + case 161: + /* + This is not a performance_schema buffer, + the data is maintained in the server, + in hostname_cache. + Print the size only, there are: + - no host_cache.count + - no host_cache.memory + */ + name= "host_cache.size"; + size= sizeof(Host_entry); + break; + + case 162: + name= "(pfs_memory_class).row_size"; + size= sizeof(PFS_memory_class); + break; + case 163: + name= "(pfs_memory_class).row_count"; + size= memory_class_max; + break; + case 164: + name= "(pfs_memory_class).memory"; + size= memory_class_max * sizeof(PFS_memory_class); + total_memory+= size; + break; + + case 165: + name= "memory_summary_by_thread_by_event_name.row_size"; + size= sizeof(PFS_memory_stat); + break; + case 166: + name= "memory_summary_by_thread_by_event_name.row_count"; + size= global_thread_container.get_row_count() * memory_class_max; + break; + case 167: + name= "memory_summary_by_thread_by_event_name.memory"; + size= global_thread_container.get_row_count() * memory_class_max * sizeof(PFS_memory_stat); + total_memory+= size; + break; + case 168: + name= "memory_summary_global_by_event_name.row_size"; + size= sizeof(PFS_memory_stat); + break; + case 169: + name= "memory_summary_global_by_event_name.row_count"; + size= memory_class_max; + break; + case 170: + name= "memory_summary_global_by_event_name.memory"; + size= memory_class_max * sizeof(PFS_memory_stat); + total_memory+= size; + break; + case 171: + name= "memory_summary_by_account_by_event_name.row_size"; + size= sizeof(PFS_memory_stat); + break; + case 172: + name= "memory_summary_by_account_by_event_name.row_count"; + size= global_account_container.get_row_count() * memory_class_max; + break; + case 173: + name= "memory_summary_by_account_by_event_name.memory"; + size= global_account_container.get_row_count() * memory_class_max * sizeof(PFS_memory_stat); + total_memory+= size; + break; + case 174: + name= "memory_summary_by_user_by_event_name.row_size"; + size= sizeof(PFS_memory_stat); + break; + case 175: + name= "memory_summary_by_user_by_event_name.row_count"; + size= global_user_container.get_row_count() * memory_class_max; + break; + case 176: + name= "memory_summary_by_user_by_event_name.memory"; + size= global_user_container.get_row_count() * memory_class_max * sizeof(PFS_memory_stat); + total_memory+= size; + break; + case 177: + name= "memory_summary_by_host_by_event_name.row_size"; + size= sizeof(PFS_memory_stat); + break; + case 178: + name= "memory_summary_by_host_by_event_name.row_count"; + size= global_host_container.get_row_count() * memory_class_max; + break; + case 179: + name= "memory_summary_by_host_by_event_name.memory"; + size= global_host_container.get_row_count() * memory_class_max * sizeof(PFS_memory_stat); + total_memory+= size; + break; + case 180: + name= "metadata_locks.row_size"; + size= global_mdl_container.get_row_size(); + break; + case 181: + name= "metadata_locks.row_count"; + size= global_mdl_container.get_row_count(); + break; + case 182: + name= "metadata_locks.memory"; + size= global_mdl_container.get_memory(); + total_memory+= size; + break; + case 183: + name= "events_transactions_history.size"; + size= sizeof(PFS_events_transactions); + break; + case 184: + name= "events_transactions_history.count"; + size= events_transactions_history_per_thread * global_thread_container.get_row_count(); + break; + case 185: + name= "events_transactions_history.memory"; + size= events_transactions_history_per_thread * global_thread_container.get_row_count() + * sizeof(PFS_events_transactions); + total_memory+= size; + break; + case 186: + name= "events_transactions_history_long.size"; + size= sizeof(PFS_events_transactions); + break; + case 187: + name= "events_transactions_history_long.count"; + size= events_transactions_history_long_size; + break; + case 188: + name= "events_transactions_history_long.memory"; + size= events_transactions_history_long_size * sizeof(PFS_events_transactions); + total_memory+= size; + break; + case 189: + name= "events_transactions_summary_by_thread_by_event_name.size"; + size= sizeof(PFS_transaction_stat); + break; + case 190: + name= "events_transactions_summary_by_thread_by_event_name.count"; + size= global_thread_container.get_row_count() * transaction_class_max; + break; + case 191: + name= "events_transactions_summary_by_thread_by_event_name.memory"; + size= global_thread_container.get_row_count() * transaction_class_max * sizeof(PFS_transaction_stat); + total_memory+= size; + break; + case 192: + name= "events_transactions_summary_by_account_by_event_name.size"; + size= sizeof(PFS_transaction_stat); + break; + case 193: + name= "events_transactions_summary_by_account_by_event_name.count"; + size= global_account_container.get_row_count() * transaction_class_max; + break; + case 194: + name= "events_transactions_summary_by_account_by_event_name.memory"; + size= global_account_container.get_row_count() * transaction_class_max * sizeof(PFS_transaction_stat); + total_memory+= size; + break; + case 195: + name= "events_transactions_summary_by_user_by_event_name.size"; + size= sizeof(PFS_transaction_stat); + break; + case 196: + name= "events_transactions_summary_by_user_by_event_name.count"; + size= global_user_container.get_row_count() * transaction_class_max; + break; + case 197: + name= "events_transactions_summary_by_user_by_event_name.memory"; + size= global_user_container.get_row_count() * transaction_class_max * sizeof(PFS_transaction_stat); + total_memory+= size; + break; + case 198: + name= "events_transactions_summary_by_host_by_event_name.size"; + size= sizeof(PFS_transaction_stat); + break; + case 199: + name= "events_transactions_summary_by_host_by_event_name.count"; + size= global_host_container.get_row_count() * transaction_class_max; + break; + case 200: + name= "events_transactions_summary_by_host_by_event_name.memory"; + size= global_host_container.get_row_count() * transaction_class_max * sizeof(PFS_transaction_stat); + total_memory+= size; + break; + case 201: + name= "table_lock_waits_summary_by_table.size"; + size= global_table_share_lock_container.get_row_size(); + break; + case 202: + name= "table_lock_waits_summary_by_table.count"; + size= global_table_share_lock_container.get_row_count(); + break; + case 203: + name= "table_lock_waits_summary_by_table.memory"; + size= global_table_share_lock_container.get_memory(); + total_memory+= size; + break; + case 204: + name= "table_io_waits_summary_by_index_usage.size"; + size= global_table_share_index_container.get_row_size(); + break; + case 205: + name= "table_io_waits_summary_by_index_usage.count"; + size= global_table_share_index_container.get_row_count(); + break; + case 206: + name= "table_io_waits_summary_by_index_usage.memory"; + size= global_table_share_index_container.get_memory(); + total_memory+= size; + break; + case 207: + name= "(history_long_statements_digest_token_array).count"; + size= events_statements_history_long_size; + break; + case 208: + name= "(history_long_statements_digest_token_array).size"; + size= pfs_max_digest_length; + break; + case 209: + name= "(history_long_statements_digest_token_array).memory"; + size= events_statements_history_long_size * pfs_max_digest_length; + total_memory+= size; + break; + case 210: + name= "(history_statements_digest_token_array).count"; + size= global_thread_container.get_row_count() * events_statements_history_per_thread; + break; + case 211: + name= "(history_statements_digest_token_array).size"; + size= pfs_max_digest_length; + break; + case 212: + name= "(history_statements_digest_token_array).memory"; + size= global_thread_container.get_row_count() * events_statements_history_per_thread * pfs_max_digest_length; + total_memory+= size; + break; + case 213: + name= "(current_statements_digest_token_array).count"; + size= global_thread_container.get_row_count() * statement_stack_max; + break; + case 214: + name= "(current_statements_digest_token_array).size"; + size= pfs_max_digest_length; + break; + case 215: + name= "(current_statements_digest_token_array).memory"; + size= global_thread_container.get_row_count() * statement_stack_max * pfs_max_digest_length; + total_memory+= size; + break; + case 216: + name= "(history_long_statements_text_array).count"; + size= events_statements_history_long_size; + break; + case 217: + name= "(history_long_statements_text_array).size"; + size= pfs_max_sqltext; + break; + case 218: + name= "(history_long_statements_text_array).memory"; + size= events_statements_history_long_size * pfs_max_sqltext; + total_memory+= size; + break; + case 219: + name= "(history_statements_text_array).count"; + size= global_thread_container.get_row_count() * events_statements_history_per_thread; + break; + case 220: + name= "(history_statements_text_array).size"; + size= pfs_max_sqltext; + break; + case 221: + name= "(history_statements_text_array).memory"; + size= global_thread_container.get_row_count() * events_statements_history_per_thread * pfs_max_sqltext; + total_memory+= size; + break; + case 222: + name= "(current_statements_text_array).count"; + size= global_thread_container.get_row_count() * statement_stack_max; + break; + case 223: + name= "(current_statements_text_array).size"; + size= pfs_max_sqltext; + break; + case 224: + name= "(current_statements_text_array).memory"; + size= global_thread_container.get_row_count() * statement_stack_max * pfs_max_sqltext; + total_memory+= size; + break; + case 225: + name= "(statements_digest_token_array).count"; + size= digest_max; + break; + case 226: + name= "(statements_digest_token_array).size"; + size= pfs_max_digest_length; + break; + case 227: + name= "(statements_digest_token_array).memory"; + size= digest_max * pfs_max_digest_length; + total_memory+= size; + break; + /* + This case must be last, + for aggregation in total_memory. + */ + case 228: + name= "performance_schema.memory"; + size= total_memory; + break; + default: + goto end; + break; + } + + buflen= (uint)(longlong10_to_str(size, buf, 10) - buf); + if (print(thd, + PERFORMANCE_SCHEMA_str.str, PERFORMANCE_SCHEMA_str.length, + name, strlen(name), + buf, buflen)) + DBUG_RETURN(true); + } + +end: + DBUG_RETURN(false); +} + +int pfs_discover_table_names(handlerton *hton __attribute__((unused)), + LEX_CSTRING *db, + MY_DIR *dir __attribute__((unused)), + handlerton::discovered_list *result) +{ + if (compare_table_names(db->str, PERFORMANCE_SCHEMA_str.str)) + return 0; + for (size_t i= 0; i < array_elements(all_shares) - 1; i++) + result->add_table(all_shares[i]->m_name.str, + all_shares[i]->m_name.length); + return 0; +} + +/** @} */ + diff --git a/storage/perfschema/pfs_engine_table.h b/storage/perfschema/pfs_engine_table.h new file mode 100644 index 00000000..02ef9b07 --- /dev/null +++ b/storage/perfschema/pfs_engine_table.h @@ -0,0 +1,627 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_ENGINE_TABLE_H +#define PFS_ENGINE_TABLE_H + +#include "table.h" +#include "sql_acl.h" +/** + @file storage/perfschema/pfs_engine_table.h + Performance schema tables (declarations). +*/ + +#include "pfs_instr_class.h" +extern pthread_key_t THR_PFS_VG; // global_variables +extern pthread_key_t THR_PFS_SV; // session_variables +extern pthread_key_t THR_PFS_VBT; // variables_by_thread +extern pthread_key_t THR_PFS_SG; // global_status +extern pthread_key_t THR_PFS_SS; // session_status +extern pthread_key_t THR_PFS_SBT; // status_by_thread +extern pthread_key_t THR_PFS_SBU; // status_by_user +extern pthread_key_t THR_PFS_SBH; // status_by_host +extern pthread_key_t THR_PFS_SBA; // status_by_account + +class Field; +struct PFS_engine_table_share; +struct time_normalizer; + +/** + @addtogroup Performance_schema_engine + @{ +*/ + +/** + Store and retrieve table state information during a query. +*/ +class PFS_table_context +{ +public: + PFS_table_context(ulonglong current_version, bool restore, pthread_key_t key); + PFS_table_context(ulonglong current_version, ulong map_size, bool restore, pthread_key_t key); +~PFS_table_context(void); + + bool initialize(void); + bool is_initialized(void) { return m_initialized; } + ulonglong current_version(void) { return m_current_version; } + ulonglong last_version(void) { return m_last_version; } + bool versions_match(void) { return m_last_version == m_current_version; } + void set_item(ulong n); + bool is_item_set(ulong n); + pthread_key_t m_thr_key; + +private: + ulonglong m_current_version; + ulonglong m_last_version; + ulong *m_map; + ulong m_map_size; + static constexpr ulong m_word_size= 8 * sizeof(ulong); + bool m_restore; + bool m_initialized; + ulong m_last_item; +}; + +/** + An abstract PERFORMANCE_SCHEMA table. + Every table implemented in the performance schema schema and storage engine + derives from this class. +*/ +class PFS_engine_table +{ +public: + static const PFS_engine_table_share* + find_engine_table_share(const char *name); + + int read_row(TABLE *table, unsigned char *buf, Field **fields); + + int update_row(TABLE *table, const unsigned char *old_buf, + const unsigned char *new_buf, Field **fields); + + /** + Delete a row from this table. + @param table Table handle + @param buf the row buffer to delete + @param fields Table fields + @return 0 on success + */ + int delete_row(TABLE *table, const unsigned char *buf, Field **fields); + + /** Initialize table scan. */ + virtual int rnd_init(bool scan){return 0;}; + + /** Fetch the next row in this cursor. */ + virtual int rnd_next(void)= 0; + /** + Fetch a row by position. + @param pos position to fetch + */ + virtual int rnd_pos(const void *pos)= 0; + + void get_position(void *ref); + void set_position(const void *ref); + /** Reset the cursor position to the beginning of the table. */ + virtual void reset_position(void)= 0; + + /** Get the normalizer and class type for the current row. */ + void get_normalizer(PFS_instr_class *instr_class); + + /** Destructor. */ + virtual ~PFS_engine_table() = default; + + /** + Helper, assign a value to a long field. + @param f the field to set + @param value the value to assign + */ + static void set_field_long(Field *f, long value); + /** + Helper, assign a value to a ulong field. + @param f the field to set + @param value the value to assign + */ + static void set_field_ulong(Field *f, ulong value); + /** + Helper, assign a value to a longlong field. + @param f the field to set + @param value the value to assign + */ + static void set_field_longlong(Field *f, longlong value); + /** + Helper, assign a value to a ulonglong field. + @param f the field to set + @param value the value to assign + */ + static void set_field_ulonglong(Field *f, ulonglong value); + /** + Helper, assign a value to a char utf8 field. + @param f the field to set + @param str the string to assign + @param len the length of the string to assign + */ + static void set_field_char_utf8(Field *f, const char *str, uint len); + /** + Helper, assign a value to a varchar utf8 field. + @param f the field to set + @param cs the string character set + @param str the string to assign + @param len the length of the string to assign + */ + static void set_field_varchar(Field *f, const CHARSET_INFO *cs, const char *str, uint len); + /** + Helper, assign a value to a varchar utf8 field. + @param f the field to set + @param str the string to assign + @param len the length of the string to assign + */ + static void set_field_varchar_utf8(Field *f, const char *str, uint len); + /** + Helper, assign a value to a longtext utf8 field. + @param f the field to set + @param str the string to assign + @param len the length of the string to assign + */ + static void set_field_longtext_utf8(Field *f, const char *str, uint len); + /** + Helper, assign a value to a blob field. + @param f the field to set + @param val the value to assign + @param len the length of the string to assign + */ + static void set_field_blob(Field *f, const char *val, uint len); + /** + Helper, assign a value to an enum field. + @param f the field to set + @param value the value to assign + */ + static void set_field_enum(Field *f, ulonglong value); + /** + Helper, assign a value to a timestamp field. + @param f the field to set + @param value the value to assign + */ + static void set_field_timestamp(Field *f, ulonglong value); + /** + Helper, assign a value to a double field. + @param f the field to set + @param value the value to assign + */ + static void set_field_double(Field *f, double value); + /** + Helper, read a value from an enum field. + @param f the field to read + @return the field value + */ + static ulonglong get_field_enum(Field *f); + /** + Helper, read a value from a char utf8 field. + @param f the field to read + @param[out] val the field value + @return the field value + */ + static String *get_field_char_utf8(Field *f, String *val); + /** + Helper, read a value from a varchar utf8 field. + @param f the field to read + @param[out] val the field value + @return the field value + */ + static String *get_field_varchar_utf8(Field *f, String *val); + +protected: + /** + Read the current row values. + @param table Table handle + @param buf row buffer + @param fields Table fields + @param read_all true if all columns are read. + */ + virtual int read_row_values(TABLE *table, unsigned char *buf, + Field **fields, bool read_all)= 0; + + /** + Update the current row values. + @param table Table handle + @param old_buf old row buffer + @param new_buf new row buffer + @param fields Table fields + */ + virtual int update_row_values(TABLE *table, const unsigned char *old_buf, + const unsigned char *new_buf, Field **fields); + + /** + Delete a row. + @param table Table handle + @param buf Row buffer + @param fields Table fields + */ + virtual int delete_row_values(TABLE *table, const unsigned char *buf, + Field **fields); + /** + Constructor. + @param share table share + @param pos address of the m_pos position member + */ + PFS_engine_table(const PFS_engine_table_share *share, void *pos) + : m_share_ptr(share), m_pos_ptr(pos), + m_normalizer(NULL), m_class_type(PFS_CLASS_NONE) + {} + + /** Table share. */ + const PFS_engine_table_share *m_share_ptr; + /** Opaque pointer to the m_pos position of this cursor. */ + void *m_pos_ptr; + /** Current normalizer */ + time_normalizer *m_normalizer; + /** Current class type */ + enum PFS_class_type m_class_type; +}; + +/** Callback to open a table. */ +typedef PFS_engine_table* (*pfs_open_table_t)(void); +/** Callback to write a row. */ +typedef int (*pfs_write_row_t)(TABLE *table, + const unsigned char *buf, Field **fields); +/** Callback to delete all rows. */ +typedef int (*pfs_delete_all_rows_t)(void); +/** Callback to get a row count. */ +typedef ha_rows (*pfs_get_row_count_t)(void); + +struct PFS_engine_table_share_state { + /** Schema integrity flag. */ + bool m_checked; +}; + +/** + A PERFORMANCE_SCHEMA table share. + This data is shared by all the table handles opened on the same table. +*/ +struct PFS_engine_table_share +{ + static void init_all_locks(void); + static void delete_all_locks(void); + /** Get the row count. */ + ha_rows get_row_count(void) const; + /** Write a row. */ + int write_row(TABLE *table, const unsigned char *buf, Field **fields) const; + + /** Table name. */ + LEX_STRING m_name; + /** Table ACL. */ + const ACL_internal_table_access *m_acl; + /** Open table function. */ + pfs_open_table_t m_open_table; + /** Write row function. */ + pfs_write_row_t m_write_row; + /** Delete all rows function. */ + pfs_delete_all_rows_t m_delete_all_rows; + /** Get rows count function. */ + pfs_get_row_count_t m_get_row_count; + /** Length of the m_pos position structure. */ + uint m_ref_length; + /** The lock, stored on behalf of the SQL layer. */ + THR_LOCK *m_thr_lock_ptr; + /** Table definition. */ + LEX_STRING sql; + /** Table is available even if the Performance Schema is disabled. */ + bool m_perpetual; + /** Table is optional. */ + bool m_optional; + /** Dynamic state. */ + PFS_engine_table_share_state *m_state; +}; + +/** + Privileges for read only tables. + The only operation allowed is SELECT. +*/ +class PFS_readonly_acl : public ACL_internal_table_access +{ +public: + PFS_readonly_acl() = default; + + ~PFS_readonly_acl() = default; + + virtual ACL_internal_access_result check(privilege_t want_access, + privilege_t *save_priv) const; +}; + +/** Singleton instance of PFS_readonly_acl. */ +extern PFS_readonly_acl pfs_readonly_acl; + +/** + Privileges for truncatable tables. + Operations allowed are SELECT and TRUNCATE. +*/ +class PFS_truncatable_acl : public ACL_internal_table_access +{ +public: + PFS_truncatable_acl() = default; + + ~PFS_truncatable_acl() = default; + + virtual ACL_internal_access_result check(privilege_t want_access, + privilege_t *save_priv) const; +}; + +/** Singleton instance of PFS_truncatable_acl. */ +extern PFS_truncatable_acl pfs_truncatable_acl; + +/** + Privileges for updatable tables. + Operations allowed are SELECT and UPDATE. +*/ +class PFS_updatable_acl : public ACL_internal_table_access +{ +public: + PFS_updatable_acl() = default; + + ~PFS_updatable_acl() = default; + + ACL_internal_access_result check(privilege_t want_access, + privilege_t *save_priv) const; +}; + +/** Singleton instance of PFS_updatable_acl. */ +extern PFS_updatable_acl pfs_updatable_acl; + +/** + Privileges for editable tables. + Operations allowed are SELECT, INSERT, UPDATE, DELETE and TRUNCATE. +*/ +class PFS_editable_acl : public ACL_internal_table_access +{ +public: + PFS_editable_acl() = default; + + ~PFS_editable_acl() = default; + + ACL_internal_access_result check(privilege_t want_access, + privilege_t *save_priv) const; +}; + +/** Singleton instance of PFS_editable_acl. */ +extern PFS_editable_acl pfs_editable_acl; + +/** + Privileges for unknown tables. +*/ +class PFS_unknown_acl : public ACL_internal_table_access +{ +public: + PFS_unknown_acl() = default; + + ~PFS_unknown_acl() = default; + + ACL_internal_access_result check(privilege_t want_access, + privilege_t *save_priv) const; +}; + +/** Singleton instance of PFS_unknown_acl. */ +extern PFS_unknown_acl pfs_unknown_acl; + + +/** + Privileges for world readable tables. +*/ +class PFS_readonly_world_acl : public PFS_readonly_acl +{ +public: + PFS_readonly_world_acl() + {} + + ~PFS_readonly_world_acl() + {} + virtual ACL_internal_access_result check(privilege_t want_access, privilege_t *save_priv) const; +}; + + +/** Singleton instance of PFS_readonly_world_acl */ +extern PFS_readonly_world_acl pfs_readonly_world_acl; + + +/** +Privileges for world readable truncatable tables. +*/ +class PFS_truncatable_world_acl : public PFS_truncatable_acl +{ +public: + PFS_truncatable_world_acl() + {} + + ~PFS_truncatable_world_acl() + {} + virtual ACL_internal_access_result check(privilege_t want_access, privilege_t *save_priv) const; +}; + + +/** Singleton instance of PFS_readonly_world_acl */ +extern PFS_truncatable_world_acl pfs_truncatable_world_acl; + + +/** + Privileges for readable processlist tables. +*/ +class PFS_readonly_processlist_acl : public PFS_readonly_acl { + public: + PFS_readonly_processlist_acl() + {} + + ~PFS_readonly_processlist_acl() + {} + + virtual ACL_internal_access_result check(privilege_t want_access, + privilege_t *save_priv) const; +}; + +/** Singleton instance of PFS_readonly_processlist_acl */ +extern PFS_readonly_processlist_acl pfs_readonly_processlist_acl; + + +/** Position of a cursor, for simple iterations. */ +struct PFS_simple_index +{ + /** Current row index. */ + uint m_index; + + /** + Constructor. + @param index the index initial value. + */ + PFS_simple_index(uint index) + : m_index(index) + {} + + /** + Set this index at a given position. + @param index an index + */ + void set_at(uint index) + { m_index= index; } + + /** + Set this index at a given position. + @param other a position + */ + void set_at(const struct PFS_simple_index *other) + { m_index= other->m_index; } + + /** + Set this index after a given position. + @param other a position + */ + void set_after(const struct PFS_simple_index *other) + { m_index= other->m_index + 1; } + + /** Set this index to the next record. */ + void next(void) + { m_index++; } +}; + +/** Position of a double cursor, for iterations using 2 nested loops. */ +struct PFS_double_index +{ + /** Outer index. */ + uint m_index_1; + /** Current index within index_1. */ + uint m_index_2; + + /** + Constructor. + @param index_1 the first index initial value. + @param index_2 the second index initial value. + */ + PFS_double_index(uint index_1, uint index_2) + : m_index_1(index_1), m_index_2(index_2) + {} + + /** + Set this index at a given position. + */ + void set_at(uint index_1, uint index_2) + { + m_index_1= index_1; + m_index_2= index_2; + } + + /** + Set this index at a given position. + @param other a position + */ + void set_at(const struct PFS_double_index *other) + { + m_index_1= other->m_index_1; + m_index_2= other->m_index_2; + } + + /** + Set this index after a given position. + @param other a position + */ + void set_after(const struct PFS_double_index *other) + { + m_index_1= other->m_index_1; + m_index_2= other->m_index_2 + 1; + } +}; + +/** Position of a triple cursor, for iterations using 3 nested loops. */ +struct PFS_triple_index +{ + /** Outer index. */ + uint m_index_1; + /** Current index within index_1. */ + uint m_index_2; + /** Current index within index_2. */ + uint m_index_3; + + /** + Constructor. + @param index_1 the first index initial value. + @param index_2 the second index initial value. + @param index_3 the third index initial value. + */ + PFS_triple_index(uint index_1, uint index_2, uint index_3) + : m_index_1(index_1), m_index_2(index_2), m_index_3(index_3) + {} + + /** + Set this index at a given position. + */ + void set_at(uint index_1, uint index_2, uint index_3) + { + m_index_1= index_1; + m_index_2= index_2; + m_index_3= index_3; + } + + /** + Set this index at a given position. + @param other a position + */ + void set_at(const struct PFS_triple_index *other) + { + m_index_1= other->m_index_1; + m_index_2= other->m_index_2; + m_index_3= other->m_index_3; + } + + /** + Set this index after a given position. + @param other a position + */ + void set_after(const struct PFS_triple_index *other) + { + m_index_1= other->m_index_1; + m_index_2= other->m_index_2; + m_index_3= other->m_index_3 + 1; + } +}; + +bool pfs_show_status(handlerton *hton, THD *thd, + stat_print_fn *print, enum ha_stat_type stat); + +int pfs_discover_table_names(handlerton *hton, LEX_CSTRING *db, + MY_DIR *dir, + handlerton::discovered_list *result); + +/** @} */ +#endif diff --git a/storage/perfschema/pfs_events.h b/storage/perfschema/pfs_events.h new file mode 100644 index 00000000..0f5022fe --- /dev/null +++ b/storage/perfschema/pfs_events.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_EVENTS_H +#define PFS_EVENTS_H + +/** + @file storage/perfschema/pfs_events.h + Events data structures (declarations). +*/ + +#include "pfs_column_types.h" + +struct PFS_instr_class; + +/** An event record. */ +struct PFS_events +{ + /** THREAD_ID. */ + ulonglong m_thread_internal_id; + /** EVENT_ID. */ + ulonglong m_event_id; + /** END_EVENT_ID. */ + ulonglong m_end_event_id; + /** (EVENT_TYPE) */ + enum_event_type m_event_type; + /** NESTING_EVENT_ID. */ + ulonglong m_nesting_event_id; + /** NESTING_EVENT_TYPE */ + enum_event_type m_nesting_event_type; + /** NESTING_EVENT_LEVEL */ + uint m_nesting_event_level; + /** Instrument metadata. */ + PFS_instr_class *m_class; + /** + Timer start. + This member is populated only if m_class->m_timed is true. + */ + ulonglong m_timer_start; + /** + Timer end. + This member is populated only if m_class->m_timed is true. + */ + ulonglong m_timer_end; + /** Location of the instrumentation in the source code (file name). */ + const char *m_source_file; + /** Location of the instrumentation in the source code (line number). */ + uint m_source_line; +}; + +#endif + diff --git a/storage/perfschema/pfs_events_stages.cc b/storage/perfschema/pfs_events_stages.cc new file mode 100644 index 00000000..aa1bdf4f --- /dev/null +++ b/storage/perfschema/pfs_events_stages.cc @@ -0,0 +1,246 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_events_stages.cc + Events stages data structures (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "pfs_global.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_account.h" +#include "pfs_host.h" +#include "pfs_user.h" +#include "pfs_events_stages.h" +#include "pfs_atomic.h" +#include "pfs_buffer_container.h" +#include "pfs_builtin_memory.h" +#include "m_string.h" + +PFS_ALIGNED ulong events_stages_history_long_size= 0; +/** Consumer flag for table EVENTS_STAGES_CURRENT. */ +PFS_ALIGNED bool flag_events_stages_current= false; +/** Consumer flag for table EVENTS_STAGES_HISTORY. */ +PFS_ALIGNED bool flag_events_stages_history= false; +/** Consumer flag for table EVENTS_STAGES_HISTORY_LONG. */ +PFS_ALIGNED bool flag_events_stages_history_long= false; + +/** True if EVENTS_STAGES_HISTORY_LONG circular buffer is full. */ +PFS_ALIGNED bool events_stages_history_long_full= false; +/** Index in EVENTS_STAGES_HISTORY_LONG circular buffer. */ +PFS_ALIGNED PFS_cacheline_uint32 events_stages_history_long_index; +/** EVENTS_STAGES_HISTORY_LONG circular buffer. */ +PFS_ALIGNED PFS_events_stages *events_stages_history_long_array= NULL; + +/** + Initialize table EVENTS_STAGES_HISTORY_LONG. + @param events_stages_history_long_sizing table sizing +*/ +int init_events_stages_history_long(uint events_stages_history_long_sizing) +{ + events_stages_history_long_size= events_stages_history_long_sizing; + events_stages_history_long_full= false; + PFS_atomic::store_u32(&events_stages_history_long_index.m_u32, 0); + + if (events_stages_history_long_size == 0) + return 0; + + events_stages_history_long_array= + PFS_MALLOC_ARRAY(& builtin_memory_stages_history_long, + events_stages_history_long_size, + sizeof(PFS_events_stages), PFS_events_stages, + MYF(MY_ZEROFILL)); + + return (events_stages_history_long_array ? 0 : 1); +} + +/** Cleanup table EVENTS_STAGES_HISTORY_LONG. */ +void cleanup_events_stages_history_long(void) +{ + PFS_FREE_ARRAY(& builtin_memory_stages_history_long, + events_stages_history_long_size, sizeof(PFS_events_stages), + events_stages_history_long_array); + events_stages_history_long_array= NULL; +} + +static inline void copy_events_stages(PFS_events_stages *dest, + const PFS_events_stages *source) +{ + memcpy(dest, source, sizeof(PFS_events_stages)); +} + +/** + Insert a stage record in table EVENTS_STAGES_HISTORY. + @param thread thread that executed the wait + @param stage record to insert +*/ +void insert_events_stages_history(PFS_thread *thread, PFS_events_stages *stage) +{ + if (unlikely(events_stages_history_per_thread == 0)) + return; + + assert(thread->m_stages_history != NULL); + + uint index= thread->m_stages_history_index; + + /* + A concurrent thread executing TRUNCATE TABLE EVENTS_STAGES_CURRENT + could alter the data that this thread is inserting, + causing a potential race condition. + We are not testing for this and insert a possibly empty record, + to make this thread (the writer) faster. + This is ok, the readers of m_stages_history will filter this out. + */ + copy_events_stages(&thread->m_stages_history[index], stage); + + index++; + if (index >= events_stages_history_per_thread) + { + index= 0; + thread->m_stages_history_full= true; + } + thread->m_stages_history_index= index; +} + +/** + Insert a stage record in table EVENTS_STAGES_HISTORY_LONG. + @param stage record to insert +*/ +void insert_events_stages_history_long(PFS_events_stages *stage) +{ + if (unlikely(events_stages_history_long_size == 0)) + return; + + assert(events_stages_history_long_array != NULL); + + uint index= PFS_atomic::add_u32(&events_stages_history_long_index.m_u32, 1); + + index= index % events_stages_history_long_size; + if (index == 0) + events_stages_history_long_full= true; + + /* See related comment in insert_events_stages_history. */ + copy_events_stages(&events_stages_history_long_array[index], stage); +} + +static void fct_reset_events_stages_current(PFS_thread *pfs) +{ + pfs->m_stage_current.m_class= NULL; +} + +/** Reset table EVENTS_STAGES_CURRENT data. */ +void reset_events_stages_current(void) +{ + global_thread_container.apply_all(fct_reset_events_stages_current); +} + +static void fct_reset_events_stages_history(PFS_thread *pfs_thread) +{ + PFS_events_stages *pfs= pfs_thread->m_stages_history; + PFS_events_stages *pfs_last= pfs + events_stages_history_per_thread; + + pfs_thread->m_stages_history_index= 0; + pfs_thread->m_stages_history_full= false; + for ( ; pfs < pfs_last; pfs++) + pfs->m_class= NULL; +} + +/** Reset table EVENTS_STAGES_HISTORY data. */ +void reset_events_stages_history(void) +{ + global_thread_container.apply_all(fct_reset_events_stages_history); +} + +/** Reset table EVENTS_STAGES_HISTORY_LONG data. */ +void reset_events_stages_history_long(void) +{ + PFS_atomic::store_u32(&events_stages_history_long_index.m_u32, 0); + events_stages_history_long_full= false; + + PFS_events_stages *pfs= events_stages_history_long_array; + PFS_events_stages *pfs_last= pfs + events_stages_history_long_size; + for ( ; pfs < pfs_last; pfs++) + pfs->m_class= NULL; +} + +static void fct_reset_events_stages_by_thread(PFS_thread *thread) +{ + PFS_account *account= sanitize_account(thread->m_account); + PFS_user *user= sanitize_user(thread->m_user); + PFS_host *host= sanitize_host(thread->m_host); + aggregate_thread_stages(thread, account, user, host); +} + +/** Reset table EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME data. */ +void reset_events_stages_by_thread() +{ + global_thread_container.apply(fct_reset_events_stages_by_thread); +} + +static void fct_reset_events_stages_by_account(PFS_account *pfs) +{ + PFS_user *user= sanitize_user(pfs->m_user); + PFS_host *host= sanitize_host(pfs->m_host); + pfs->aggregate_stages(user, host); +} + +/** Reset table EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME data. */ +void reset_events_stages_by_account() +{ + global_account_container.apply(fct_reset_events_stages_by_account); +} + +static void fct_reset_events_stages_by_user(PFS_user *pfs) +{ + pfs->aggregate_stages(); +} + +/** Reset table EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME data. */ +void reset_events_stages_by_user() +{ + global_user_container.apply(fct_reset_events_stages_by_user); +} + +static void fct_reset_events_stages_by_host(PFS_host *pfs) +{ + pfs->aggregate_stages(); +} + +/** Reset table EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME data. */ +void reset_events_stages_by_host() +{ + global_host_container.apply(fct_reset_events_stages_by_host); +} + +/** Reset table EVENTS_STAGES_GLOBAL_BY_EVENT_NAME data. */ +void reset_events_stages_global() +{ + PFS_stage_stat *stat= global_instr_class_stages_array; + PFS_stage_stat *stat_last= global_instr_class_stages_array + stage_class_max; + + for ( ; stat < stat_last; stat++) + stat->reset(); +} + diff --git a/storage/perfschema/pfs_events_stages.h b/storage/perfschema/pfs_events_stages.h new file mode 100644 index 00000000..f0b669cf --- /dev/null +++ b/storage/perfschema/pfs_events_stages.h @@ -0,0 +1,73 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + Copyright (c) 2022, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_EVENTS_STAGES_H +#define PFS_EVENTS_STAGES_H + +/** + @file storage/perfschema/pfs_events_stages.h + Events waits data structures (declarations). +*/ + +#include "pfs_events.h" + +struct PFS_thread; +struct PFS_account; +struct PFS_user; +struct PFS_host; + +/** A stage record. */ +struct PFS_events_stages : public PFS_events +{ + PSI_stage_progress m_progress; +}; + +void insert_events_stages_history(PFS_thread *thread, PFS_events_stages *stage); +void insert_events_stages_history_long(PFS_events_stages *stage); + +extern bool flag_events_stages_current; +extern bool flag_events_stages_history; +extern bool flag_events_stages_history_long; + +extern bool events_stages_history_long_full; +PFS_ALIGNED extern PFS_cacheline_uint32 events_stages_history_long_index; +extern PFS_events_stages *events_stages_history_long_array; +extern ulong events_stages_history_long_size; + +int init_events_stages_history_long(uint events_stages_history_long_sizing); +void cleanup_events_stages_history_long(); + +void reset_events_stages_current(); +void reset_events_stages_history(); +void reset_events_stages_history_long(); +void reset_events_stages_by_thread(); +void reset_events_stages_by_account(); +void reset_events_stages_by_user(); +void reset_events_stages_by_host(); +void reset_events_stages_global(); +void aggregate_account_stages(PFS_account *account); +void aggregate_user_stages(PFS_user *user); +void aggregate_host_stages(PFS_host *host); + +#endif + diff --git a/storage/perfschema/pfs_events_statements.cc b/storage/perfschema/pfs_events_statements.cc new file mode 100644 index 00000000..05bdc83f --- /dev/null +++ b/storage/perfschema/pfs_events_statements.cc @@ -0,0 +1,328 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_events_statements.cc + Events statements data structures (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "pfs_global.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_account.h" +#include "pfs_host.h" +#include "pfs_user.h" +#include "pfs_events_statements.h" +#include "pfs_atomic.h" +#include "pfs_buffer_container.h" +#include "pfs_builtin_memory.h" +#include "m_string.h" + +PFS_ALIGNED size_t events_statements_history_long_size= 0; +/** Consumer flag for table EVENTS_STATEMENTS_CURRENT. */ +PFS_ALIGNED bool flag_events_statements_current= false; +/** Consumer flag for table EVENTS_STATEMENTS_HISTORY. */ +PFS_ALIGNED bool flag_events_statements_history= false; +/** Consumer flag for table EVENTS_STATEMENTS_HISTORY_LONG. */ +PFS_ALIGNED bool flag_events_statements_history_long= false; + +/** True if EVENTS_STATEMENTS_HISTORY_LONG circular buffer is full. */ +PFS_ALIGNED bool events_statements_history_long_full= false; +/** Index in EVENTS_STATEMENTS_HISTORY_LONG circular buffer. */ +PFS_ALIGNED PFS_cacheline_uint32 events_statements_history_long_index; +/** EVENTS_STATEMENTS_HISTORY_LONG circular buffer. */ +PFS_ALIGNED PFS_events_statements *events_statements_history_long_array= NULL; +static unsigned char *h_long_stmts_digest_token_array= NULL; +static char *h_long_stmts_text_array= NULL; + +/** + Initialize table EVENTS_STATEMENTS_HISTORY_LONG. + @param events_statements_history_long_sizing table sizing +*/ +int init_events_statements_history_long(size_t events_statements_history_long_sizing) +{ + events_statements_history_long_size= events_statements_history_long_sizing; + events_statements_history_long_full= false; + PFS_atomic::store_u32(&events_statements_history_long_index.m_u32, 0); + + if (events_statements_history_long_size == 0) + return 0; + + events_statements_history_long_array= + PFS_MALLOC_ARRAY(& builtin_memory_statements_history_long, + events_statements_history_long_size, sizeof(PFS_events_statements), + PFS_events_statements, MYF(MY_ZEROFILL)); + + if (events_statements_history_long_array == NULL) + { + cleanup_events_statements_history_long(); + return 1; + } + + if (pfs_max_digest_length > 0) + { + /* Size of each digest text array. */ + size_t digest_text_size= pfs_max_digest_length * sizeof(unsigned char); + + h_long_stmts_digest_token_array= + PFS_MALLOC_ARRAY(& builtin_memory_statements_history_long_tokens, + events_statements_history_long_size, digest_text_size, + unsigned char, MYF(MY_ZEROFILL)); + + if (h_long_stmts_digest_token_array == NULL) + { + cleanup_events_statements_history_long(); + return 1; + } + } + + if (pfs_max_sqltext > 0) + { + /* Size of each sql text array. */ + size_t sqltext_size= pfs_max_sqltext * sizeof(char); + + h_long_stmts_text_array= + PFS_MALLOC_ARRAY(& builtin_memory_statements_history_long_sqltext, + events_statements_history_long_size, sqltext_size, + char, MYF(MY_ZEROFILL)); + + if (h_long_stmts_text_array == NULL) + { + cleanup_events_statements_history_long(); + return 1; + } + } + + for (size_t index= 0; index < events_statements_history_long_size; index++) + { + events_statements_history_long_array[index].m_digest_storage.reset(h_long_stmts_digest_token_array + + index * pfs_max_digest_length, pfs_max_digest_length); + events_statements_history_long_array[index].m_sqltext= h_long_stmts_text_array + index * pfs_max_sqltext; + } + + return 0; +} + +/** Cleanup table EVENTS_STATEMENTS_HISTORY_LONG. */ +void cleanup_events_statements_history_long(void) +{ + PFS_FREE_ARRAY(& builtin_memory_statements_history_long, + events_statements_history_long_size, + sizeof(PFS_events_statements), + events_statements_history_long_array); + + PFS_FREE_ARRAY(& builtin_memory_statements_history_long_tokens, + events_statements_history_long_size, + (pfs_max_digest_length * sizeof(unsigned char)), + h_long_stmts_digest_token_array); + + PFS_FREE_ARRAY(& builtin_memory_statements_history_long_sqltext, + events_statements_history_long_size, + (pfs_max_sqltext * sizeof(char)), + h_long_stmts_text_array); + + events_statements_history_long_array= NULL; + h_long_stmts_digest_token_array= NULL; + h_long_stmts_text_array= NULL; +} + +inline void PFS_events_statements::copy(const PFS_events_statements &source) +{ + /* Copy all attributes except SQL TEXT and DIGEST */ + memcpy((void*) this, &source, offsetof(PFS_events_statements, m_sqltext)); + + /* Copy SQL TEXT */ + int sqltext_length= source.m_sqltext_length; + + if (sqltext_length > 0) + { + memcpy(m_sqltext, source.m_sqltext, sqltext_length); + m_sqltext_length= sqltext_length; + } + else + { + m_sqltext_length= 0; + } + + /* Copy DIGEST */ + m_digest_storage.copy(&source.m_digest_storage); +} + +/** + Insert a statement record in table EVENTS_STATEMENTS_HISTORY. + @param thread thread that executed the wait + @param statement record to insert +*/ +void insert_events_statements_history(PFS_thread *thread, PFS_events_statements *statement) +{ + if (unlikely(events_statements_history_per_thread == 0)) + return; + + assert(thread->m_statements_history != NULL); + + uint index= thread->m_statements_history_index; + + /* + A concurrent thread executing TRUNCATE TABLE EVENTS_STATEMENTS_CURRENT + could alter the data that this thread is inserting, + causing a potential race condition. + We are not testing for this and insert a possibly empty record, + to make this thread (the writer) faster. + This is ok, the readers of m_statements_history will filter this out. + */ + thread->m_statements_history[index].copy(*statement); + + index++; + if (index >= events_statements_history_per_thread) + { + index= 0; + thread->m_statements_history_full= true; + } + thread->m_statements_history_index= index; +} + +/** + Insert a statement record in table EVENTS_STATEMENTS_HISTORY_LONG. + @param statement record to insert +*/ +void insert_events_statements_history_long(PFS_events_statements *statement) +{ + if (unlikely(events_statements_history_long_size == 0)) + return ; + + assert(events_statements_history_long_array != NULL); + + uint index= PFS_atomic::add_u32(&events_statements_history_long_index.m_u32, 1); + + index= index % events_statements_history_long_size; + if (index == 0) + events_statements_history_long_full= true; + + /* See related comment in insert_events_statements_history. */ + events_statements_history_long_array[index].copy(*statement); +} + +static void fct_reset_events_statements_current(PFS_thread *pfs_thread) +{ + PFS_events_statements *pfs_stmt= & pfs_thread->m_statement_stack[0]; + PFS_events_statements *pfs_stmt_last= pfs_stmt + statement_stack_max; + + for ( ; pfs_stmt < pfs_stmt_last; pfs_stmt++) + pfs_stmt->m_event.m_class= nullptr; +} + +/** Reset table EVENTS_STATEMENTS_CURRENT data. */ +void reset_events_statements_current(void) +{ + global_thread_container.apply_all(fct_reset_events_statements_current); +} + +static void fct_reset_events_statements_history(PFS_thread *pfs_thread) +{ + PFS_events_statements *pfs= pfs_thread->m_statements_history; + PFS_events_statements *pfs_last= pfs + events_statements_history_per_thread; + + pfs_thread->m_statements_history_index= 0; + pfs_thread->m_statements_history_full= false; + for ( ; pfs < pfs_last; pfs++) + pfs->m_event.m_class= nullptr; +} + +/** Reset table EVENTS_STATEMENTS_HISTORY data. */ +void reset_events_statements_history(void) +{ + global_thread_container.apply_all(fct_reset_events_statements_history); +} + +/** Reset table EVENTS_STATEMENTS_HISTORY_LONG data. */ +void reset_events_statements_history_long(void) +{ + PFS_atomic::store_u32(&events_statements_history_long_index.m_u32, 0); + events_statements_history_long_full= false; + + PFS_events_statements *pfs= events_statements_history_long_array; + PFS_events_statements *pfs_last= pfs + events_statements_history_long_size; + for ( ; pfs < pfs_last; pfs++) + pfs->m_event.m_class= nullptr; +} + +static void fct_reset_events_statements_by_thread(PFS_thread *thread) +{ + PFS_account *account= sanitize_account(thread->m_account); + PFS_user *user= sanitize_user(thread->m_user); + PFS_host *host= sanitize_host(thread->m_host); + aggregate_thread_statements(thread, account, user, host); +} + +/** Reset table EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME data. */ +void reset_events_statements_by_thread() +{ + global_thread_container.apply(fct_reset_events_statements_by_thread); +} + +static void fct_reset_events_statements_by_account(PFS_account *pfs) +{ + PFS_user *user= sanitize_user(pfs->m_user); + PFS_host *host= sanitize_host(pfs->m_host); + pfs->aggregate_statements(user, host); +} + +/** Reset table EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME data. */ +void reset_events_statements_by_account() +{ + global_account_container.apply(fct_reset_events_statements_by_account); +} + +static void fct_reset_events_statements_by_user(PFS_user *pfs) +{ + pfs->aggregate_statements(); +} + +/** Reset table EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME data. */ +void reset_events_statements_by_user() +{ + global_user_container.apply(fct_reset_events_statements_by_user); +} + +static void fct_reset_events_statements_by_host(PFS_host *pfs) +{ + pfs->aggregate_statements(); +} + +/** Reset table EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME data. */ +void reset_events_statements_by_host() +{ + global_host_container.apply(fct_reset_events_statements_by_host); +} + +/** Reset table EVENTS_STATEMENTS_GLOBAL_BY_EVENT_NAME data. */ +void reset_events_statements_global() +{ + PFS_statement_stat *stat= global_instr_class_statements_array; + PFS_statement_stat *stat_last= global_instr_class_statements_array + statement_class_max; + + for ( ; stat < stat_last; stat++) + stat->reset(); +} + diff --git a/storage/perfschema/pfs_events_statements.h b/storage/perfschema/pfs_events_statements.h new file mode 100644 index 00000000..d0655ad5 --- /dev/null +++ b/storage/perfschema/pfs_events_statements.h @@ -0,0 +1,156 @@ +/* Copyright (c) 2010, 2022, Oracle and/or its affiliates. + Copyright (c) 2022, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_EVENTS_STATEMENTS_H +#define PFS_EVENTS_STATEMENTS_H + +/** + @file storage/perfschema/pfs_events_statements.h + Events statements data structures (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_events.h" +#include "pfs_digest.h" + +struct PFS_thread; +struct PFS_account; +struct PFS_user; +struct PFS_host; + +/** A statement record. */ +struct PFS_events_statements +{ + PFS_events m_event; + enum_object_type m_sp_type; + char m_schema_name[NAME_LEN]; + uint m_schema_name_length; + char m_object_name[NAME_LEN]; + uint m_object_name_length; + + /** Database name. */ + char m_current_schema_name[NAME_LEN]; + /** Length of @c m_current_schema_name. */ + uint m_current_schema_name_length; + + /** Locked time. */ + ulonglong m_lock_time; + + /** Diagnostics area, message text. */ + char m_message_text[MYSQL_ERRMSG_SIZE+1]; + /** Diagnostics area, error number. */ + uint m_sql_errno; + /** Diagnostics area, SQLSTATE. */ + char m_sqlstate[SQLSTATE_LENGTH]; + /** Diagnostics area, error count. */ + uint m_error_count; + /** Diagnostics area, warning count. */ + uint m_warning_count; + /** Diagnostics area, rows affected. */ + ulonglong m_rows_affected; + + /** Optimizer metric, number of rows sent. */ + ulonglong m_rows_sent; + /** Optimizer metric, number of rows examined. */ + ulonglong m_rows_examined; + /** Optimizer metric, number of temporary tables created on disk. */ + ulonglong m_created_tmp_disk_tables; + /** Optimizer metric, number of temporary tables created. */ + ulonglong m_created_tmp_tables; + /** Optimizer metric, number of full join. */ + ulonglong m_select_full_join; + /** Optimizer metric, number of full range join. */ + ulonglong m_select_full_range_join; + /** Optimizer metric, number of select range. */ + ulonglong m_select_range; + /** Optimizer metric, number of select range checks. */ + ulonglong m_select_range_check; + /** Optimizer metric, number of select scans. */ + ulonglong m_select_scan; + /** Optimizer metric, number of sort merge passes. */ + ulonglong m_sort_merge_passes; + /** Optimizer metric, number of sort ranges. */ + ulonglong m_sort_range; + /** Optimizer metric, number of sort rows. */ + ulonglong m_sort_rows; + /** Optimizer metric, number of sort scans. */ + ulonglong m_sort_scan; + /** Optimizer metric, number of 'no index used'. */ + ulonglong m_no_index_used; + /** Optimizer metric, number of 'no good index used'. */ + ulonglong m_no_good_index_used; + + /** True if sqltext was truncated. */ + bool m_sqltext_truncated; + /** Statement character set number. */ + uint m_sqltext_cs_number; + + /** + SQL_TEXT. + This pointer is immutable, + and always point to pre allocated memory. + */ + char *m_sqltext; + /** Length of @ m_info. */ + uint m_sqltext_length; + /** + Statement digest. + This underlying token array storage pointer is immutable, + and always point to pre allocated memory. + */ + sql_digest_storage m_digest_storage; + + inline void copy(const PFS_events_statements &source); +}; + +void insert_events_statements_history(PFS_thread *thread, PFS_events_statements *statement); +void insert_events_statements_history_long(PFS_events_statements *statement); + +extern ulong nested_statement_lost; + +extern bool flag_events_statements_current; +extern bool flag_events_statements_history; +extern bool flag_events_statements_history_long; + +extern bool events_statements_history_long_full; +PFS_ALIGNED extern PFS_cacheline_uint32 events_statements_history_long_index; +extern PFS_events_statements *events_statements_history_long_array; +extern size_t events_statements_history_long_size; + +int init_events_statements_history_long(size_t events_statements_history_long_sizing); +void cleanup_events_statements_history_long(); + +void reset_events_statements_current(); +void reset_events_statements_history(); +void reset_events_statements_history_long(); +void reset_events_statements_by_thread(); +void reset_events_statements_by_account(); +void reset_events_statements_by_user(); +void reset_events_statements_by_host(); +void reset_events_statements_global(); +void aggregate_account_statements(PFS_account *account); +void aggregate_user_statements(PFS_user *user); +void aggregate_host_statements(PFS_host *host); + +#endif + diff --git a/storage/perfschema/pfs_events_transactions.cc b/storage/perfschema/pfs_events_transactions.cc new file mode 100644 index 00000000..5ccdb034 --- /dev/null +++ b/storage/perfschema/pfs_events_transactions.cc @@ -0,0 +1,267 @@ +/* Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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/pfs_events_transactions.cc + Events transactions data structures (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "pfs_global.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_account.h" +#include "pfs_host.h" +#include "pfs_user.h" +#include "pfs_events_transactions.h" +#include "pfs_atomic.h" +#include "pfs_buffer_container.h" +#include "pfs_builtin_memory.h" +#include "m_string.h" + +PFS_ALIGNED ulong events_transactions_history_long_size= 0; +/** Consumer flag for table EVENTS_TRANSACTIONS_CURRENT. */ +PFS_ALIGNED bool flag_events_transactions_current= false; +/** Consumer flag for table EVENTS_TRANSACTIONS_HISTORY. */ +PFS_ALIGNED bool flag_events_transactions_history= false; +/** Consumer flag for table EVENTS_TRANSACTIONS_HISTORY_LONG. */ +PFS_ALIGNED bool flag_events_transactions_history_long= false; + +/** True if EVENTS_TRANSACTIONS_HISTORY_LONG circular buffer is full. */ +PFS_ALIGNED bool events_transactions_history_long_full= false; +/** Index in EVENTS_TRANSACTIONS_HISTORY_LONG circular buffer. */ +PFS_ALIGNED PFS_cacheline_uint32 events_transactions_history_long_index; +/** EVENTS_TRANSACTIONS_HISTORY_LONG circular buffer. */ +PFS_ALIGNED PFS_events_transactions *events_transactions_history_long_array= NULL; + +/** + Initialize table EVENTS_TRANSACTIONS_HISTORY_LONG. + @param events_transactions_history_long_sizing table sizing +*/ +int init_events_transactions_history_long(uint events_transactions_history_long_sizing) +{ + events_transactions_history_long_size= events_transactions_history_long_sizing; + events_transactions_history_long_full= false; + PFS_atomic::store_u32(&events_transactions_history_long_index.m_u32, 0); + + if (events_transactions_history_long_size == 0) + return 0; + + events_transactions_history_long_array= + PFS_MALLOC_ARRAY(& builtin_memory_transactions_history_long, + events_transactions_history_long_size, + sizeof(PFS_events_transactions), PFS_events_transactions, + MYF(MY_ZEROFILL)); + + return (events_transactions_history_long_array ? 0 : 1); +} + +/** Cleanup table EVENTS_TRANSACTIONS_HISTORY_LONG. */ +void cleanup_events_transactions_history_long(void) +{ + PFS_FREE_ARRAY(& builtin_memory_transactions_history_long, + events_transactions_history_long_size, sizeof(PFS_events_transactions), + events_transactions_history_long_array); + events_transactions_history_long_array= NULL; +} + +static inline void copy_events_transactions(PFS_events_transactions *dest, + const PFS_events_transactions *source) +{ + memcpy(dest, source, sizeof(PFS_events_transactions)); +} + +/** + Insert a transaction record in table EVENTS_TRANSACTIONS_HISTORY. + @param thread thread that executed the wait + @param transaction record to insert +*/ +void insert_events_transactions_history(PFS_thread *thread, PFS_events_transactions *transaction) +{ + if (unlikely(events_transactions_history_per_thread == 0)) + return; + + assert(thread->m_transactions_history != NULL); + + uint index= thread->m_transactions_history_index; + + /* + A concurrent thread executing TRUNCATE TABLE EVENTS_TRANSACTIONS_CURRENT + could alter the data that this thread is inserting, + causing a potential race condition. + We are not testing for this and insert a possibly empty record, + to make this thread (the writer) faster. + This is ok, the readers of m_transactions_history will filter this out. + */ + copy_events_transactions(&thread->m_transactions_history[index], transaction); + + index++; + if (index >= events_transactions_history_per_thread) + { + index= 0; + thread->m_transactions_history_full= true; + } + thread->m_transactions_history_index= index; +} + +/** + Insert a transaction record in table EVENTS_TRANSACTIONS_HISTORY_LONG. + @param transaction record to insert +*/ +void insert_events_transactions_history_long(PFS_events_transactions *transaction) +{ + if (unlikely(events_transactions_history_long_size == 0)) + return ; + + assert(events_transactions_history_long_array != NULL); + + uint index= PFS_atomic::add_u32(&events_transactions_history_long_index.m_u32, 1); + + index= index % events_transactions_history_long_size; + if (index == 0) + events_transactions_history_long_full= true; + + /* See related comment in insert_events_transactions_history. */ + copy_events_transactions(&events_transactions_history_long_array[index], transaction); +} + +static void fct_reset_events_transactions_current(PFS_thread *pfs) +{ + pfs->m_transaction_current.m_class= NULL; +} + +/** Reset table EVENTS_TRANSACTIONS_CURRENT data. */ +void reset_events_transactions_current(void) +{ + global_thread_container.apply_all(fct_reset_events_transactions_current); +} + +static void fct_reset_events_transactions_history(PFS_thread *pfs_thread) +{ + PFS_events_transactions *pfs= pfs_thread->m_transactions_history; + PFS_events_transactions *pfs_last= pfs + events_transactions_history_per_thread; + + pfs_thread->m_transactions_history_index= 0; + pfs_thread->m_transactions_history_full= false; + for ( ; pfs < pfs_last; pfs++) + pfs->m_class= NULL; +} + +/** Reset table EVENTS_TRANSACTIONS_HISTORY data. */ +void reset_events_transactions_history(void) +{ + global_thread_container.apply_all(fct_reset_events_transactions_history); +} + +/** Reset table EVENTS_TRANSACTIONS_HISTORY_LONG data. */ +void reset_events_transactions_history_long(void) +{ + PFS_atomic::store_u32(&events_transactions_history_long_index.m_u32, 0); + events_transactions_history_long_full= false; + + PFS_events_transactions *pfs= events_transactions_history_long_array; + PFS_events_transactions *pfs_last= pfs + events_transactions_history_long_size; + for ( ; pfs < pfs_last; pfs++) + pfs->m_class= NULL; +} + +static void fct_reset_events_transactions_by_thread(PFS_thread *thread) +{ + PFS_account *account= sanitize_account(thread->m_account); + PFS_user *user= sanitize_user(thread->m_user); + PFS_host *host= sanitize_host(thread->m_host); + aggregate_thread_transactions(thread, account, user, host); +} + +/** Reset table EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME data. */ +void reset_events_transactions_by_thread() +{ + global_thread_container.apply(fct_reset_events_transactions_by_thread); +} + +static void fct_reset_events_transactions_by_account(PFS_account *pfs) +{ + PFS_user *user= sanitize_user(pfs->m_user); + PFS_host *host= sanitize_host(pfs->m_host); + pfs->aggregate_transactions(user, host); +} + +/** Reset table EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME data. */ +void reset_events_transactions_by_account() +{ + global_account_container.apply(fct_reset_events_transactions_by_account); +} + +static void fct_reset_events_transactions_by_user(PFS_user *pfs) +{ + pfs->aggregate_transactions(); +} + +/** Reset table EVENTS_TRANSACTIONS_SUMMARY_BY_USER_BY_EVENT_NAME data. */ +void reset_events_transactions_by_user() +{ + global_user_container.apply(fct_reset_events_transactions_by_user); +} + +static void fct_reset_events_transactions_by_host(PFS_host *pfs) +{ + pfs->aggregate_transactions(); +} + +/** Reset table EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME data. */ +void reset_events_transactions_by_host() +{ + global_host_container.apply(fct_reset_events_transactions_by_host); +} + +/** Reset table EVENTS_TRANSACTIONS_GLOBAL_BY_EVENT_NAME data. */ +void reset_events_transactions_global() +{ + global_transaction_stat.reset(); +} + +/** + Check if the XID consists of printable characters, ASCII 32 - 127. + @param xid XID structure + @param offset offset into XID.data[] + @param length number of bytes to process + @return true if all bytes are in printable range +*/ +bool xid_printable(PSI_xid *xid, size_t offset, size_t length) +{ + if (xid->is_null()) + return false; + + assert(offset + length <= MYSQL_XIDDATASIZE); + + unsigned char *c= (unsigned char*)&xid->data + offset; + + for (size_t i= 0; i < length; i++, c++) + { + if(*c < 32 || *c > 127) + return false; + } + + return true; +} + diff --git a/storage/perfschema/pfs_events_transactions.h b/storage/perfschema/pfs_events_transactions.h new file mode 100644 index 00000000..b649f7f8 --- /dev/null +++ b/storage/perfschema/pfs_events_transactions.h @@ -0,0 +1,132 @@ +/* Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef PFS_EVENTS_TRANSACTIONS_H +#define PFS_EVENTS_TRANSACTIONS_H + +/** + @file storage/perfschema/pfs_events_transactions.h + Events transactions data structures (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_events.h" +#include "rpl_gtid.h" +#include "mysql/plugin.h" /* MYSQL_XIDDATASIZE */ +#include "my_thread.h" + +struct PFS_thread; +struct PFS_account; +struct PFS_user; +struct PFS_host; + +/** + struct PSI_xid is binary compatible with the XID structure as + in the X/Open CAE Specification, Distributed Transaction Processing: + The XA Specification, X/Open Company Ltd., 1991. + http://www.opengroup.org/bookstore/catalog/c193.htm + + A value of -1 in formatID means that the XID is null. + Max length for bqual and gtrid is 64 bytes each. + + @see XID in sql/handler.h + @see MYSQL_XID in mysql/plugin.h +*/ +struct PSI_xid +{ + /** Format identifier. */ + long formatID; + /** GTRID length, value 1-64. */ + long gtrid_length; + /** BQUAL length, value 1-64. */ + long bqual_length; + /** XID raw data, not \0-terminated */ + char data[MYSQL_XIDDATASIZE]; + + PSI_xid() {null();} + bool is_null() { return formatID == -1; } + void null() { formatID= -1; gtrid_length= 0; bqual_length= 0;} +}; +typedef struct PSI_xid PSI_xid; + +/** A transaction record. */ +struct PFS_events_transactions : public PFS_events +{ + /** Source identifier, mapped from internal format. */ + //rpl_sid m_sid; + /** InnoDB transaction ID. */ + ulonglong m_trxid; + /** Status */ + enum_transaction_state m_state; + /** Global Transaction ID specifier. */ + Gtid_specification m_gtid_spec; + /** True if XA transaction. */ + my_bool m_xa; + /** XA transaction ID. */ + PSI_xid m_xid; + /** XA status */ + enum_xa_transaction_state m_xa_state; + /** Transaction isolation level. */ + enum_isolation_level m_isolation_level; + /** True if read-only transaction, otherwise read-write. */ + my_bool m_read_only; + /** True if autocommit transaction. */ + my_bool m_autocommit; + /** Total number of savepoints. */ + ulonglong m_savepoint_count; + /** Number of rollback_to_savepoint. */ + ulonglong m_rollback_to_savepoint_count; + /** Number of release_savepoint. */ + ulonglong m_release_savepoint_count; +}; + +bool xid_printable(PSI_xid *xid, size_t offset, size_t length); + +void insert_events_transactions_history(PFS_thread *thread, PFS_events_transactions *transaction); +void insert_events_transactions_history_long(PFS_events_transactions *transaction); + +extern bool flag_events_transactions_current; +extern bool flag_events_transactions_history; +extern bool flag_events_transactions_history_long; + +extern bool events_transactions_history_long_full; +extern PFS_cacheline_uint32 events_transactions_history_long_index; +extern PFS_events_transactions *events_transactions_history_long_array; +extern ulong events_transactions_history_long_size; + +int init_events_transactions_history_long(uint events_transactions_history_long_sizing); +void cleanup_events_transactions_history_long(); + +void reset_events_transactions_current(); +void reset_events_transactions_history(); +void reset_events_transactions_history_long(); +void reset_events_transactions_by_thread(); +void reset_events_transactions_by_account(); +void reset_events_transactions_by_user(); +void reset_events_transactions_by_host(); +void reset_events_transactions_global(); +void aggregate_account_transactions(PFS_account *account); +void aggregate_user_transactions(PFS_user *user); +void aggregate_host_transactions(PFS_host *host); + +#endif + diff --git a/storage/perfschema/pfs_events_waits.cc b/storage/perfschema/pfs_events_waits.cc new file mode 100644 index 00000000..3ec6a671 --- /dev/null +++ b/storage/perfschema/pfs_events_waits.cc @@ -0,0 +1,301 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_events_waits.cc + Events waits data structures (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "pfs_global.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_user.h" +#include "pfs_host.h" +#include "pfs_account.h" +#include "pfs_events_waits.h" +#include "pfs_atomic.h" +#include "pfs_buffer_container.h" +#include "pfs_builtin_memory.h" +#include "m_string.h" + +PFS_ALIGNED ulong events_waits_history_long_size= 0; +/** Consumer flag for table EVENTS_WAITS_CURRENT. */ +PFS_ALIGNED bool flag_events_waits_current= false; +/** Consumer flag for table EVENTS_WAITS_HISTORY. */ +PFS_ALIGNED bool flag_events_waits_history= false; +/** Consumer flag for table EVENTS_WAITS_HISTORY_LONG. */ +PFS_ALIGNED bool flag_events_waits_history_long= false; +/** Consumer flag for the global instrumentation. */ +PFS_ALIGNED bool flag_global_instrumentation= false; +/** Consumer flag for the per thread instrumentation. */ +PFS_ALIGNED bool flag_thread_instrumentation= false; + +/** True if EVENTS_WAITS_HISTORY_LONG circular buffer is full. */ +PFS_ALIGNED bool events_waits_history_long_full= false; +/** Index in EVENTS_WAITS_HISTORY_LONG circular buffer. */ +PFS_ALIGNED PFS_cacheline_uint32 events_waits_history_long_index; +/** EVENTS_WAITS_HISTORY_LONG circular buffer. */ +PFS_ALIGNED PFS_events_waits *events_waits_history_long_array= NULL; + +/** + Initialize table EVENTS_WAITS_HISTORY_LONG. + @param events_waits_history_long_sizing table sizing +*/ +int init_events_waits_history_long(uint events_waits_history_long_sizing) +{ + events_waits_history_long_size= events_waits_history_long_sizing; + events_waits_history_long_full= false; + PFS_atomic::store_u32(&events_waits_history_long_index.m_u32, 0); + + if (events_waits_history_long_size == 0) + return 0; + + events_waits_history_long_array= + PFS_MALLOC_ARRAY(& builtin_memory_waits_history_long, + events_waits_history_long_size, + sizeof(PFS_events_waits), PFS_events_waits, + MYF(MY_ZEROFILL)); + + return (events_waits_history_long_array ? 0 : 1); +} + +/** Cleanup table EVENTS_WAITS_HISTORY_LONG. */ +void cleanup_events_waits_history_long(void) +{ + PFS_FREE_ARRAY(& builtin_memory_waits_history_long, + events_waits_history_long_size, sizeof(PFS_events_waits), + events_waits_history_long_array); + events_waits_history_long_array= NULL; +} + +static inline void copy_events_waits(PFS_events_waits *dest, + const PFS_events_waits *source) +{ + memcpy(dest, source, sizeof(PFS_events_waits)); +} + +/** + Insert a wait record in table EVENTS_WAITS_HISTORY. + @param thread thread that executed the wait + @param wait record to insert +*/ +void insert_events_waits_history(PFS_thread *thread, PFS_events_waits *wait) +{ + if (unlikely(events_waits_history_per_thread == 0)) + return; + + uint index= thread->m_waits_history_index; + + /* + A concurrent thread executing TRUNCATE TABLE EVENTS_WAITS_CURRENT + could alter the data that this thread is inserting, + causing a potential race condition. + We are not testing for this and insert a possibly empty record, + to make this thread (the writer) faster. + This is ok, the readers of m_waits_history will filter this out. + */ + copy_events_waits(&thread->m_waits_history[index], wait); + + index++; + if (index >= events_waits_history_per_thread) + { + index= 0; + thread->m_waits_history_full= true; + } + thread->m_waits_history_index= index; +} + +/** + Insert a wait record in table EVENTS_WAITS_HISTORY_LONG. + @param wait record to insert +*/ +void insert_events_waits_history_long(PFS_events_waits *wait) +{ + if (unlikely(events_waits_history_long_size == 0)) + return; + + uint index= PFS_atomic::add_u32(&events_waits_history_long_index.m_u32, 1); + + index= index % events_waits_history_long_size; + if (index == 0) + events_waits_history_long_full= true; + + /* See related comment in insert_events_waits_history. */ + copy_events_waits(&events_waits_history_long_array[index], wait); +} + +static void fct_reset_events_waits_current(PFS_thread *pfs_thread) +{ + PFS_events_waits *pfs_wait= pfs_thread->m_events_waits_stack; + PFS_events_waits *pfs_wait_last= pfs_wait + WAIT_STACK_SIZE; + + for ( ; pfs_wait < pfs_wait_last; pfs_wait++) + pfs_wait->m_wait_class= NO_WAIT_CLASS; +} + + +/** Reset table EVENTS_WAITS_CURRENT data. */ +void reset_events_waits_current(void) +{ + global_thread_container.apply_all(fct_reset_events_waits_current); +} + +static void fct_reset_events_waits_history(PFS_thread *pfs_thread) +{ + PFS_events_waits *wait= pfs_thread->m_waits_history; + PFS_events_waits *wait_last= wait + events_waits_history_per_thread; + + pfs_thread->m_waits_history_index= 0; + pfs_thread->m_waits_history_full= false; + for ( ; wait < wait_last; wait++) + wait->m_wait_class= NO_WAIT_CLASS; +} + +/** Reset table EVENTS_WAITS_HISTORY data. */ +void reset_events_waits_history(void) +{ + global_thread_container.apply_all(fct_reset_events_waits_history); +} + +/** Reset table EVENTS_WAITS_HISTORY_LONG data. */ +void reset_events_waits_history_long(void) +{ + PFS_atomic::store_u32(&events_waits_history_long_index.m_u32, 0); + events_waits_history_long_full= false; + + PFS_events_waits *wait= events_waits_history_long_array; + PFS_events_waits *wait_last= wait + events_waits_history_long_size; + for ( ; wait < wait_last; wait++) + wait->m_wait_class= NO_WAIT_CLASS; +} + +static void fct_reset_events_waits_by_thread(PFS_thread *thread) +{ + PFS_account *account= sanitize_account(thread->m_account); + PFS_user *user= sanitize_user(thread->m_user); + PFS_host *host= sanitize_host(thread->m_host); + aggregate_thread_waits(thread, account, user, host); +} + +/** Reset table EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME data. */ +void reset_events_waits_by_thread() +{ + global_thread_container.apply(fct_reset_events_waits_by_thread); +} + +static void fct_reset_events_waits_by_account(PFS_account *pfs) +{ + PFS_user *user= sanitize_user(pfs->m_user); + PFS_host *host= sanitize_host(pfs->m_host); + pfs->aggregate_waits(user, host); +} + +/** Reset table EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME data. */ +void reset_events_waits_by_account() +{ + global_account_container.apply(fct_reset_events_waits_by_account); +} + +static void fct_reset_events_waits_by_user(PFS_user *pfs) +{ + pfs->aggregate_waits(); +} + +/** Reset table EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME data. */ +void reset_events_waits_by_user() +{ + global_user_container.apply(fct_reset_events_waits_by_user); +} + +static void fct_reset_events_waits_by_host(PFS_host *pfs) +{ + pfs->aggregate_waits(); +} + +/** Reset table EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME data. */ +void reset_events_waits_by_host() +{ + global_host_container.apply(fct_reset_events_waits_by_host); +} + +static void fct_reset_table_waits_by_table(PFS_table_share *pfs) +{ + pfs->aggregate(); +} + +void reset_table_waits_by_table() +{ + global_table_share_container.apply(fct_reset_table_waits_by_table); +} + +static void fct_reset_table_io_waits_by_table(PFS_table_share *pfs) +{ + pfs->aggregate_io(); +} + +void reset_table_io_waits_by_table() +{ + global_table_share_container.apply(fct_reset_table_io_waits_by_table); +} + +static void fct_reset_table_lock_waits_by_table(PFS_table_share *pfs) +{ + pfs->aggregate_lock(); +} + +void reset_table_lock_waits_by_table() +{ + global_table_share_container.apply(fct_reset_table_lock_waits_by_table); +} + +void fct_reset_table_waits_by_table_handle(PFS_table *pfs) +{ + pfs->sanitized_aggregate(); +} + +void reset_table_waits_by_table_handle() +{ + global_table_container.apply(fct_reset_table_waits_by_table_handle); +} + +void fct_reset_table_io_waits_by_table_handle(PFS_table *pfs) +{ + pfs->sanitized_aggregate_io(); +} + +void reset_table_io_waits_by_table_handle() +{ + global_table_container.apply(fct_reset_table_io_waits_by_table_handle); +} + +void fct_reset_table_lock_waits_by_table_handle(PFS_table *pfs) +{ + pfs->sanitized_aggregate_lock(); +} + +void reset_table_lock_waits_by_table_handle() +{ + global_table_container.apply(fct_reset_table_lock_waits_by_table_handle); +} + diff --git a/storage/perfschema/pfs_events_waits.h b/storage/perfschema/pfs_events_waits.h new file mode 100644 index 00000000..93dcb136 --- /dev/null +++ b/storage/perfschema/pfs_events_waits.h @@ -0,0 +1,157 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates.. + Copyright (c) 2017, 2012, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_EVENTS_WAITS_H +#define PFS_EVENTS_WAITS_H + +/** + @file storage/perfschema/pfs_events_waits.h + Events waits data structures (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_lock.h" +#include "pfs_events.h" + +struct PFS_mutex; +struct PFS_rwlock; +struct PFS_cond; +struct PFS_table; +struct PFS_file; +struct PFS_thread; +struct PFS_socket; +struct PFS_instr_class; +struct PFS_table_share; +struct PFS_account; +struct PFS_user; +struct PFS_host; +struct PFS_metadata_lock; + +/** Class of a wait event. */ +enum events_waits_class +{ + NO_WAIT_CLASS= 0, + WAIT_CLASS_MUTEX, + WAIT_CLASS_RWLOCK, + WAIT_CLASS_COND, + WAIT_CLASS_TABLE, + WAIT_CLASS_FILE, + WAIT_CLASS_SOCKET, + WAIT_CLASS_IDLE, + WAIT_CLASS_METADATA +}; + +/** A wait event record. */ +struct PFS_events_waits : public PFS_events +{ + /** + The type of wait. + Readers: + - the consumer threads. + Writers: + - the producer threads, in the instrumentation. + Out of bound Writers: + - TRUNCATE EVENTS_WAITS_CURRENT + - TRUNCATE EVENTS_WAITS_HISTORY + - TRUNCATE EVENTS_WAITS_HISTORY_LONG + */ + events_waits_class m_wait_class; + /** Object type */ + enum_object_type m_object_type; + /** Table share, for table operations only. */ + PFS_table_share *m_weak_table_share; + /** File, for file operations only. */ + PFS_file *m_weak_file; + /** Socket, for socket operations only. */ + PFS_socket *m_weak_socket; + /** Metadata lock, for mdl operations only. */ + PFS_metadata_lock *m_weak_metadata_lock; + /** For weak pointers, target object version. */ + uint32 m_weak_version; + /** Address in memory of the object instance waited on. */ + const void *m_object_instance_addr; + /** Operation performed. */ + enum_operation_type m_operation; + /** + Number of bytes/rows read/written. + This member is populated for FILE READ/WRITE operations, with a number of bytes. + This member is populated for TABLE IO operations, with a number of rows. + */ + size_t m_number_of_bytes; + /** + Index used. + This member is populated for TABLE IO operations only. + */ + uint m_index; + /** Flags */ + ulong m_flags; +}; + +/** TIMED bit in the state flags bitfield. */ +#define STATE_FLAG_TIMED (1U<<0) +/** THREAD bit in the state flags bitfield. */ +#define STATE_FLAG_THREAD (1U<<1) +/** EVENT bit in the state flags bitfield. */ +#define STATE_FLAG_EVENT (1U<<2) +/** DIGEST bit in the state flags bitfield. */ +#define STATE_FLAG_DIGEST (1U<<3) + +void insert_events_waits_history(PFS_thread *thread, PFS_events_waits *wait); + +void insert_events_waits_history_long(PFS_events_waits *wait); + +extern bool flag_events_waits_current; +extern bool flag_events_waits_history; +extern bool flag_events_waits_history_long; +extern bool flag_global_instrumentation; +extern bool flag_thread_instrumentation; + +extern bool events_waits_history_long_full; +PFS_ALIGNED extern PFS_cacheline_uint32 events_waits_history_long_index; +extern PFS_events_waits *events_waits_history_long_array; +extern ulong events_waits_history_long_size; + +int init_events_waits_history_long(uint events_waits_history_long_sizing); +void cleanup_events_waits_history_long(); + +void reset_events_waits_current(); +void reset_events_waits_history(); +void reset_events_waits_history_long(); +void reset_events_waits_by_thread(); +void reset_events_waits_by_account(); +void reset_events_waits_by_user(); +void reset_events_waits_by_host(); +void reset_events_waits_global(); +void aggregate_account_waits(PFS_account *account); +void aggregate_user_waits(PFS_user *user); +void aggregate_host_waits(PFS_host *host); + +void reset_table_waits_by_table(); +void reset_table_io_waits_by_table(); +void reset_table_lock_waits_by_table(); +void reset_table_waits_by_table_handle(); +void reset_table_io_waits_by_table_handle(); +void reset_table_lock_waits_by_table_handle(); + +#endif + diff --git a/storage/perfschema/pfs_global.cc b/storage/perfschema/pfs_global.cc new file mode 100644 index 00000000..8860be79 --- /dev/null +++ b/storage/perfschema/pfs_global.cc @@ -0,0 +1,225 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights + reserved. + Copyright (c) 2022, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_global.cc + Miscellaneous global dependencies (implementation). +*/ + +#include <my_global.h> +#include "pfs_global.h" +#include "pfs_builtin_memory.h" +#include "log.h" +#include "aligned.h" +#include "assume_aligned.h" + +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef _WIN32 +#include <winsock2.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +bool pfs_initialized= false; + +/** + Memory allocation for the performance schema. + The memory used internally in the performance schema implementation. + It is allocated at startup, or during runtime with scalable buffers. +*/ +void *pfs_malloc(PFS_builtin_memory_class *klass, size_t size, myf flags) +{ + assert(klass != NULL); + assert(size > 0); + + const size_t aligned_size= MY_ALIGN(size, CPU_LEVEL1_DCACHE_LINESIZE); + + void *ptr= aligned_malloc(aligned_size, CPU_LEVEL1_DCACHE_LINESIZE); + if (unlikely(ptr == NULL)) + return NULL; + + klass->count_alloc(size); + + if (flags & MY_ZEROFILL) + memset_aligned<CPU_LEVEL1_DCACHE_LINESIZE>(ptr, 0, aligned_size); + return ptr; +} + +void pfs_free(PFS_builtin_memory_class *klass, size_t size, void *ptr) +{ + if (ptr == NULL) + return; + + aligned_free(ptr); + klass->count_free(size); +} + +/** + Array allocation for the performance schema. + Checks for overflow of n * size before allocating. + @param klass performance schema memory class + @param n number of array elements + @param size element size + @param flags malloc flags + @return pointer to memory on success, else NULL +*/ +void *pfs_malloc_array(PFS_builtin_memory_class *klass, size_t n, size_t size, myf flags) +{ + assert(klass != NULL); + assert(n > 0); + assert(size > 0); + void *ptr= NULL; + size_t array_size= n * size; + /* Check for overflow before allocating. */ + if (is_overflow(array_size, n, size)) + { + sql_print_warning("Failed to allocate memory for %zu chunks each of size " + "%zu for buffer '%s' due to overflow", n, size, + klass->m_class.m_name); + return NULL; + } + + if(NULL == (ptr= pfs_malloc(klass, array_size, flags))) + { + sql_print_warning("Failed to allocate %zu bytes for buffer '%s' due to " + "out-of-memory", array_size, klass->m_class.m_name); + } + return ptr; +} + +/** + Free array allocated by @sa pfs_malloc_array. + @param klass performance schema memory class + @param n number of array elements + @param size element size + @param ptr pointer to memory +*/ +void pfs_free_array(PFS_builtin_memory_class *klass, size_t n, size_t size, void *ptr) +{ + if (ptr == NULL) + return; + size_t array_size= n * size; + /* Overflow should have been detected by pfs_malloc_array. */ + assert(!is_overflow(array_size, n, size)); + return pfs_free(klass, array_size, ptr); +} + +/** + Detect multiplication overflow. + @param product multiplication product + @param n1 operand + @param n2 operand + @return true if multiplication caused an overflow. +*/ +bool is_overflow(size_t product, size_t n1, size_t n2) +{ + if (n1 != 0 && (product / n1 != n2)) + return true; + else + return false; +} + +void pfs_print_error(const char *format, ...) +{ + va_list args; + va_start(args, format); + /* + Printing to anything else, like the error log, would generate even more + recursive calls to the performance schema implementation + (file io is instrumented), so that could lead to catastrophic results. + Printing to something safe, and low level: stderr only. + */ + vfprintf(stderr, format, args); + va_end(args); + fflush(stderr); +} + + +/** Convert raw ip address into readable format. Do not do a reverse DNS lookup. */ + +uint pfs_get_socket_address(char *host, + uint host_len, + uint *port, + const struct sockaddr_storage *src_addr, + socklen_t src_len) +{ + assert(host); + assert(src_addr); + assert(port); + + memset(host, 0, host_len); + *port= 0; + + switch (src_addr->ss_family) + { + case AF_INET: + { + if (host_len < INET_ADDRSTRLEN+1) + return 0; + struct sockaddr_in *sa4= (struct sockaddr_in *)(src_addr); + #ifdef _WIN32 + /* Older versions of Windows do not support inet_ntop() */ + getnameinfo((struct sockaddr *)sa4, sizeof(struct sockaddr_in), + host, host_len, NULL, 0, NI_NUMERICHOST); + #else + inet_ntop(AF_INET, &(sa4->sin_addr), host, INET_ADDRSTRLEN); + #endif + *port= ntohs(sa4->sin_port); + } + break; + +#ifdef HAVE_IPV6 + case AF_INET6: + { + if (host_len < INET6_ADDRSTRLEN+1) + return 0; + struct sockaddr_in6 *sa6= (struct sockaddr_in6 *)(src_addr); + #ifdef _WIN32 + /* Older versions of Windows do not support inet_ntop() */ + getnameinfo((struct sockaddr *)sa6, sizeof(struct sockaddr_in6), + host, host_len, NULL, 0, NI_NUMERICHOST); + #else + inet_ntop(AF_INET6, &(sa6->sin6_addr), host, INET6_ADDRSTRLEN); + #endif + *port= ntohs(sa6->sin6_port); + } + break; +#endif + + default: + break; + } + + /* Return actual IP address string length */ + return ((uint)strlen((const char*)host)); +} diff --git a/storage/perfschema/pfs_global.h b/storage/perfschema/pfs_global.h new file mode 100644 index 00000000..6b73fd9f --- /dev/null +++ b/storage/perfschema/pfs_global.h @@ -0,0 +1,186 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + Copyright (c) 2022, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_GLOBAL_H +#define PFS_GLOBAL_H + +#include "my_compiler.h" + +/** + @file storage/perfschema/pfs_global.h + Miscellaneous global dependencies (declarations). +*/ + +/** True when the performance schema is initialized. */ +extern bool pfs_initialized; +/** Total memory allocated by the performance schema, in bytes. */ +extern size_t pfs_allocated_memory; + +#define PFS_ALIGNED alignas(CPU_LEVEL1_DCACHE_LINESIZE) + +/** + A uint32 variable, guaranteed to be alone in a CPU cache line. + This is for performance, for variables accessed very frequently. +*/ +struct PFS_cacheline_uint32 +{ + uint32 m_u32; + char m_full_cache_line[CPU_LEVEL1_DCACHE_LINESIZE - sizeof(uint32)]; + + PFS_cacheline_uint32() + : m_u32(0) + {} +}; + +/** + A uint64 variable, guaranteed to be alone in a CPU cache line. + This is for performance, for variables accessed very frequently. +*/ +struct PFS_cacheline_uint64 +{ + uint64 m_u64; + char m_full_cache_line[CPU_LEVEL1_DCACHE_LINESIZE - sizeof(uint64)]; + + PFS_cacheline_uint64() + : m_u64(0) + {} +}; + +struct PFS_builtin_memory_class; + +/** Memory allocation for the performance schema. */ +void *pfs_malloc(PFS_builtin_memory_class *klass, size_t size, myf flags); + +/** Allocate an array of structures with overflow check. */ +void *pfs_malloc_array(PFS_builtin_memory_class *klass, size_t n, size_t size, myf flags); + +/** + Helper, to allocate an array of structures. + @param k memory class + @param n number of elements in the array + @param s size of array element + @param T type of an element + @param f flags to use when allocating memory +*/ +#define PFS_MALLOC_ARRAY(k, n, s, T, f) \ + reinterpret_cast<T*>(pfs_malloc_array((k), (n), (s), (f))) + +/** Free memory allocated with @sa pfs_malloc. */ +void pfs_free(PFS_builtin_memory_class *klass, size_t size, void *ptr); + +/** Free memory allocated with @sa pfs_malloc_array. */ +void pfs_free_array(PFS_builtin_memory_class *klass, size_t n, size_t size, void *ptr); + +/** + Helper, to free an array of structures. + @param k memory class + @param n number of elements in the array + @param s size of array element + @param p the array to free +*/ +#define PFS_FREE_ARRAY(k, n, s, p) \ + pfs_free_array((k), (n), (s), (p)) + +/** Detect multiplication overflow. */ +bool is_overflow(size_t product, size_t n1, size_t n2); + +uint pfs_get_socket_address(char *host, + uint host_len, + uint *port, + const struct sockaddr_storage *src_addr, + socklen_t src_len); + +/** + Compute a random index value in an interval. + @param ptr seed address + @param max_size maximun size of the interval + @return a random value in [0, max_size-1] +*/ +inline uint randomized_index(const void *ptr, uint max_size) +{ + static uint seed1= 0; + static uint seed2= 0; + uint result; + intptr value; + + if (unlikely(max_size == 0)) + return 0; + + /* + ptr is typically an aligned structure, and can be in an array. + - The last bits are not random because of alignment, + so we divide by 8. + - The high bits are mostly constant, especially with 64 bits architectures, + but we keep most of them anyway, by doing computation in intptr. + The high bits are significant depending on where the data is + stored (the data segment, the stack, the heap, ...). + - To spread consecutive cells in an array further, we multiply by + a factor A. This factor should not be too high, which would cause + an overflow and cause loss of randomness (droping the top high bits). + The factor is a prime number, to help spread the distribution. + - To add more noise, and to be more robust if the calling code is + passing a constant value instead of a random identity, + we add the previous results, for hysteresys, with a degree 2 polynom, + X^2 + X + 1. + - Last, a modulo is applied to be within the [0, max_size - 1] range. + Note that seed1 and seed2 are static, and are *not* thread safe, + which is even better. + Effect with arrays: T array[N] + - ptr(i) = & array[i] = & array[0] + i * sizeof(T) + - ptr(i+1) = ptr(i) + sizeof(T). + What we want here, is to have index(i) and index(i+1) fall into + very different areas in [0, max_size - 1], to avoid locality. + */ + value= (reinterpret_cast<intptr> (ptr)) >> 3; + value*= 1789; + value+= seed2 + seed1 + 1; + + result= (static_cast<uint> (value)) % max_size; + + seed2= seed1*seed1; + seed1= result; + + assert(result < max_size); + return result; +} + +void pfs_print_error(const char *format, ...); + +/** + Given an array defined as T ARRAY[MAX], + check that an UNSAFE pointer actually points to an element + within the array. +*/ +#define SANITIZE_ARRAY_BODY(T, ARRAY, MAX, UNSAFE) \ + intptr offset; \ + if ((&ARRAY[0] <= UNSAFE) && \ + (UNSAFE < &ARRAY[MAX])) \ + { \ + offset= ((intptr) UNSAFE - (intptr) ARRAY) % sizeof(T); \ + if (offset == 0) \ + return UNSAFE; \ + } \ + return NULL + +#endif + diff --git a/storage/perfschema/pfs_host.cc b/storage/perfschema/pfs_host.cc new file mode 100644 index 00000000..d54725fe --- /dev/null +++ b/storage/perfschema/pfs_host.cc @@ -0,0 +1,375 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + Copyright (c) 2022, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_host.cc + Performance schema host (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "pfs.h" +#include "pfs_stat.h" +#include "pfs_instr.h" +#include "pfs_setup_actor.h" +#include "pfs_host.h" +#include "pfs_global.h" +#include "pfs_instr_class.h" +#include "pfs_buffer_container.h" + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +LF_HASH host_hash; +static bool host_hash_inited= false; + +/** + Initialize the host buffers. + @param param sizing parameters + @return 0 on success +*/ +int init_host(const PFS_global_param *param) +{ + if (global_host_container.init(param->m_host_sizing)) + return 1; + + return 0; +} + +/** Cleanup all the host buffers. */ +void cleanup_host(void) +{ + global_host_container.cleanup(); +} + +C_MODE_START +static uchar *host_hash_get_key(const uchar *entry, size_t *length, + my_bool) +{ + const PFS_host * const *typed_entry; + const PFS_host *host; + const void *result; + typed_entry= reinterpret_cast<const PFS_host* const *> (entry); + assert(typed_entry != NULL); + host= *typed_entry; + assert(host != NULL); + *length= host->m_key.m_key_length; + result= host->m_key.m_hash_key; + return const_cast<uchar*> (reinterpret_cast<const uchar*> (result)); +} +C_MODE_END + +/** + Initialize the host hash. + @return 0 on success +*/ +int init_host_hash(const PFS_global_param *param) +{ + if ((! host_hash_inited) && (param->m_host_sizing != 0)) + { + lf_hash_init(&host_hash, sizeof(PFS_host*), LF_HASH_UNIQUE, + 0, 0, host_hash_get_key, &my_charset_bin); + host_hash_inited= true; + } + return 0; +} + +/** Cleanup the host hash. */ +void cleanup_host_hash(void) +{ + if (host_hash_inited) + { + lf_hash_destroy(&host_hash); + host_hash_inited= false; + } +} + +static LF_PINS* get_host_hash_pins(PFS_thread *thread) +{ + if (unlikely(thread->m_host_hash_pins == NULL)) + { + if (! host_hash_inited) + return NULL; + thread->m_host_hash_pins= lf_hash_get_pins(&host_hash); + } + return thread->m_host_hash_pins; +} + +static void set_host_key(PFS_host_key *key, + const char *host, uint host_length) +{ + assert(host_length <= HOSTNAME_LENGTH); + + char *ptr= &key->m_hash_key[0]; + if (host_length > 0) + { + memcpy(ptr, host, host_length); + ptr+= host_length; + } + ptr[0]= 0; + ptr++; + key->m_key_length= (uint)(ptr - &key->m_hash_key[0]); +} + +PFS_host *find_or_create_host(PFS_thread *thread, + const char *hostname, uint hostname_length) +{ + PFS_ALIGNED static PFS_cacheline_uint32 monotonic; + + LF_PINS *pins= get_host_hash_pins(thread); + if (unlikely(pins == NULL)) + { + global_host_container.m_lost++; + return NULL; + } + + PFS_host_key key; + set_host_key(&key, hostname, hostname_length); + + PFS_host **entry; + PFS_host *pfs; + uint retry_count= 0; + const uint retry_max= 3; + pfs_dirty_state dirty_state; + +search: + entry= reinterpret_cast<PFS_host**> + (lf_hash_search(&host_hash, pins, + key.m_hash_key, key.m_key_length)); + if (entry && (entry != MY_ERRPTR)) + { + PFS_host *pfs; + pfs= *entry; + pfs->inc_refcount(); + lf_hash_search_unpin(pins); + return pfs; + } + + lf_hash_search_unpin(pins); + + pfs= global_host_container.allocate(& dirty_state); + if (pfs != NULL) + { + pfs->m_key= key; + if (hostname_length > 0) + pfs->m_hostname= &pfs->m_key.m_hash_key[0]; + else + pfs->m_hostname= NULL; + pfs->m_hostname_length= hostname_length; + + pfs->init_refcount(); + pfs->reset_stats(); + pfs->m_disconnected_count= 0; + + int res; + pfs->m_lock.dirty_to_allocated(& dirty_state); + res= lf_hash_insert(&host_hash, pins, &pfs); + if (likely(res == 0)) + { + return pfs; + } + + global_host_container.deallocate(pfs); + + if (res > 0) + { + if (++retry_count > retry_max) + { + global_host_container.m_lost++; + return NULL; + } + goto search; + } + + global_host_container.m_lost++; + return NULL; + } + + return NULL; +} + +void PFS_host::aggregate(bool alive) +{ + aggregate_waits(); + aggregate_stages(); + aggregate_statements(); + aggregate_transactions(); + aggregate_memory(alive); + aggregate_status(); + aggregate_stats(); +} + +void PFS_host::aggregate_waits() +{ + /* No parent to aggregate to, clean the stats */ + reset_waits_stats(); +} + +void PFS_host::aggregate_stages() +{ + if (read_instr_class_stages_stats() == NULL) + return; + + /* + Aggregate EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME to: + - EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME + */ + aggregate_all_stages(write_instr_class_stages_stats(), + global_instr_class_stages_array); +} + +void PFS_host::aggregate_statements() +{ + if (read_instr_class_statements_stats() == NULL) + return; + + /* + Aggregate EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME to: + - EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME + */ + aggregate_all_statements(write_instr_class_statements_stats(), + global_instr_class_statements_array); +} + +void PFS_host::aggregate_transactions() +{ + if (read_instr_class_transactions_stats() == NULL) + return; + + /* + Aggregate EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME to: + - EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME + */ + aggregate_all_transactions(write_instr_class_transactions_stats(), + &global_transaction_stat); +} + +void PFS_host::aggregate_memory(bool alive) +{ + if (read_instr_class_memory_stats() == NULL) + return; + + /* + Aggregate MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME to: + - MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME + */ + aggregate_all_memory(alive, + write_instr_class_memory_stats(), + global_instr_class_memory_array); +} + +void PFS_host::aggregate_status() +{ + /* No parent to aggregate to, clean the stats */ + m_status_stats.reset(); +} + +void PFS_host::aggregate_stats() +{ + /* No parent to aggregate to, clean the stats */ + m_disconnected_count= 0; +} + +void PFS_host::release() +{ + dec_refcount(); +} + +void PFS_host::carry_memory_stat_delta(PFS_memory_stat_delta *delta, uint index) +{ + PFS_memory_stat *event_name_array; + PFS_memory_stat *stat; + PFS_memory_stat_delta delta_buffer; + PFS_memory_stat_delta *remaining_delta; + + event_name_array= write_instr_class_memory_stats(); + stat= & event_name_array[index]; + remaining_delta= stat->apply_delta(delta, &delta_buffer); + + if (remaining_delta != NULL) + carry_global_memory_stat_delta(remaining_delta, index); +} + +PFS_host *sanitize_host(PFS_host *unsafe) +{ + return global_host_container.sanitize(unsafe); +} + +void purge_host(PFS_thread *thread, PFS_host *host) +{ + LF_PINS *pins= get_host_hash_pins(thread); + if (unlikely(pins == NULL)) + return; + + PFS_host **entry; + entry= reinterpret_cast<PFS_host**> + (lf_hash_search(&host_hash, pins, + host->m_key.m_hash_key, host->m_key.m_key_length)); + if (entry && (entry != MY_ERRPTR)) + { + assert(*entry == host); + if (host->get_refcount() == 0) + { + lf_hash_delete(&host_hash, pins, + host->m_key.m_hash_key, host->m_key.m_key_length); + host->aggregate(false); + global_host_container.deallocate(host); + } + } + + lf_hash_search_unpin(pins); +} + +class Proc_purge_host + : public PFS_buffer_processor<PFS_host> +{ +public: + Proc_purge_host(PFS_thread *thread) + : m_thread(thread) + {} + + virtual void operator()(PFS_host *pfs) + { + pfs->aggregate(true); + if (pfs->get_refcount() == 0) + purge_host(m_thread, pfs); + } + +private: + PFS_thread *m_thread; +}; + +/** Purge non connected hosts, reset stats of connected hosts. */ +void purge_all_host(void) +{ + PFS_thread *thread= PFS_thread::get_current_thread(); + if (unlikely(thread == NULL)) + return; + + Proc_purge_host proc(thread); + global_host_container.apply(proc); +} + +/** @} */ diff --git a/storage/perfschema/pfs_host.h b/storage/perfschema/pfs_host.h new file mode 100644 index 00000000..56bcccb9 --- /dev/null +++ b/storage/perfschema/pfs_host.h @@ -0,0 +1,120 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_HOST_H +#define PFS_HOST_H + +/** + @file storage/perfschema/pfs_host.h + Performance schema host (declarations). +*/ + +#include "pfs_lock.h" +#include "lf.h" +#include "pfs_con_slice.h" + +struct PFS_global_param; +struct PFS_thread; + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +/** Hash key for a host. */ +struct PFS_host_key +{ + /** + Hash search key. + This has to be a string for LF_HASH, + the format is "<hostname><0x00>" + */ + char m_hash_key[HOSTNAME_LENGTH + 1]; + uint m_key_length; +}; + +/** Per host statistics. */ +struct PFS_ALIGNED PFS_host : PFS_connection_slice +{ +public: + inline void init_refcount(void) + { + PFS_atomic::store_32(& m_refcount, 1); + } + + inline int get_refcount(void) + { + return PFS_atomic::load_32(& m_refcount); + } + + inline void inc_refcount(void) + { + PFS_atomic::add_32(& m_refcount, 1); + } + + inline void dec_refcount(void) + { + PFS_atomic::add_32(& m_refcount, -1); + } + + void aggregate(bool alive); + void aggregate_waits(void); + void aggregate_stages(void); + void aggregate_statements(void); + void aggregate_transactions(void); + void aggregate_memory(bool alive); + void aggregate_status(void); + void aggregate_stats(void); + void release(void); + + void carry_memory_stat_delta(PFS_memory_stat_delta *delta, uint index); + + /* Internal lock. */ + pfs_lock m_lock; + PFS_host_key m_key; + const char *m_hostname; + uint m_hostname_length; + + ulonglong m_disconnected_count; + +private: + int m_refcount; +}; + +int init_host(const PFS_global_param *param); +void cleanup_host(void); +int init_host_hash(const PFS_global_param *param); +void cleanup_host_hash(void); + +PFS_host *find_or_create_host(PFS_thread *thread, + const char *hostname, uint hostname_length); + +PFS_host *sanitize_host(PFS_host *unsafe); +void purge_all_host(void); + +/* For show status. */ + +extern LF_HASH host_hash; + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_instr.cc b/storage/perfschema/pfs_instr.cc new file mode 100644 index 00000000..f08d1a25 --- /dev/null +++ b/storage/perfschema/pfs_instr.cc @@ -0,0 +1,2346 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + Copyright (c) 2022, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_instr.cc + Performance schema instruments (implementation). +*/ + +#include <my_global.h> +#include <string.h> + +#include "my_sys.h" +#include "pfs.h" +#include "pfs_stat.h" +#include "pfs_instr.h" +#include "pfs_host.h" +#include "pfs_user.h" +#include "pfs_account.h" +#include "pfs_global.h" +#include "pfs_instr_class.h" +#include "pfs_buffer_container.h" +#include "pfs_builtin_memory.h" + +ulong nested_statement_lost= 0; + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +/** + Size of the file handle array. @sa file_handle_array. + Signed value, for easier comparisons with a file descriptor number. +*/ +long file_handle_max= 0; +/** True when @c file_handle_array is full. */ +bool file_handle_full; +/** Number of file handle lost. @sa file_handle_array */ +ulong file_handle_lost= 0; +/** Number of EVENTS_WAITS_HISTORY records per thread. */ +ulong events_waits_history_per_thread= 0; +/** Number of EVENTS_STAGES_HISTORY records per thread. */ +ulong events_stages_history_per_thread= 0; +/** Number of EVENTS_STATEMENTS_HISTORY records per thread. */ +ulong events_statements_history_per_thread= 0; +uint statement_stack_max= 0; +size_t pfs_max_digest_length= 0; +size_t pfs_max_sqltext= 0; +/** Number of locker lost. @sa LOCKER_STACK_SIZE. */ +ulong locker_lost= 0; +/** Number of statements lost. @sa STATEMENT_STACK_SIZE. */ +ulong statement_lost= 0; +/** Size of connection attribute storage per thread */ +ulong session_connect_attrs_size_per_thread; +/** Number of connection attributes lost */ +ulong session_connect_attrs_lost= 0; + +/** Number of EVENTS_TRANSACTIONS_HISTORY records per thread. */ +ulong events_transactions_history_per_thread= 0; + +/** + File instrumentation handle array. + @sa file_handle_max + @sa file_handle_lost +*/ +PFS_file **file_handle_array= NULL; + +PFS_stage_stat *global_instr_class_stages_array= NULL; +PFS_statement_stat *global_instr_class_statements_array= NULL; +PFS_memory_stat *global_instr_class_memory_array= NULL; + +PFS_ALIGNED static PFS_cacheline_uint64 thread_internal_id_counter; + +/** Hash table for instrumented files. */ +LF_HASH pfs_filename_hash; +/** True if pfs_filename_hash is initialized. */ +static bool filename_hash_inited= false; + +my_bool show_compatibility_56= 0; + +/** + Initialize all the instruments instance buffers. + @param param sizing parameters + @return 0 on success +*/ +int init_instruments(const PFS_global_param *param) +{ + uint index; + + /* Make sure init_event_name_sizing is called */ + assert(wait_class_max != 0); + + file_handle_max= param->m_file_handle_sizing; + file_handle_full= false; + file_handle_lost= 0; + + pfs_max_digest_length= param->m_max_digest_length; + pfs_max_sqltext= param->m_max_sql_text_length; + + events_waits_history_per_thread= param->m_events_waits_history_sizing; + + events_stages_history_per_thread= param->m_events_stages_history_sizing; + + events_statements_history_per_thread= param->m_events_statements_history_sizing; + + statement_stack_max= param->m_statement_stack_sizing; + + events_transactions_history_per_thread= param->m_events_transactions_history_sizing; + + session_connect_attrs_size_per_thread= param->m_session_connect_attrs_sizing; + session_connect_attrs_lost= 0; + + file_handle_array= NULL; + + thread_internal_id_counter.m_u64= 0; + + if (global_mutex_container.init(param->m_mutex_sizing)) + return 1; + + if (global_rwlock_container.init(param->m_rwlock_sizing)) + return 1; + + if (global_cond_container.init(param->m_cond_sizing)) + return 1; + + if (global_file_container.init(param->m_file_sizing)) + return 1; + + if (file_handle_max > 0) + { + file_handle_array= PFS_MALLOC_ARRAY(& builtin_memory_file_handle, + file_handle_max, + sizeof(PFS_file*), PFS_file*, + MYF(MY_ZEROFILL)); + if (unlikely(file_handle_array == NULL)) + return 1; + } + + if (global_table_container.init(param->m_table_sizing)) + return 1; + + if (global_socket_container.init(param->m_socket_sizing)) + return 1; + + if (global_mdl_container.init(param->m_metadata_lock_sizing)) + return 1; + + if (global_thread_container.init(param->m_thread_sizing)) + return 1; + + if (stage_class_max > 0) + { + global_instr_class_stages_array= + PFS_MALLOC_ARRAY(& builtin_memory_global_stages, + stage_class_max, + sizeof(PFS_stage_stat), PFS_stage_stat, + MYF(MY_ZEROFILL)); + if (unlikely(global_instr_class_stages_array == NULL)) + return 1; + + for (index= 0; index < stage_class_max; index++) + global_instr_class_stages_array[index].reset(); + } + + if (statement_class_max > 0) + { + global_instr_class_statements_array= + PFS_MALLOC_ARRAY(& builtin_memory_global_statements, + statement_class_max, + sizeof(PFS_statement_stat), PFS_statement_stat, + MYF(MY_ZEROFILL)); + if (unlikely(global_instr_class_statements_array == NULL)) + return 1; + + for (index= 0; index < statement_class_max; index++) + global_instr_class_statements_array[index].reset(); + } + + if (memory_class_max > 0) + { + global_instr_class_memory_array= + PFS_MALLOC_ARRAY(& builtin_memory_global_memory, + memory_class_max, + sizeof(PFS_memory_stat), PFS_memory_stat, + MYF(MY_ZEROFILL)); + if (unlikely(global_instr_class_memory_array == NULL)) + return 1; + + for (index= 0; index < memory_class_max; index++) + global_instr_class_memory_array[index].reset(); + } + + return 0; +} + +/** Cleanup all the instruments buffers. */ +void cleanup_instruments(void) +{ + global_mutex_container.cleanup(); + global_rwlock_container.cleanup(); + global_cond_container.cleanup(); + global_file_container.cleanup(); + + PFS_FREE_ARRAY(& builtin_memory_file_handle, + file_handle_max, sizeof(PFS_file*), + file_handle_array); + file_handle_array= NULL; + file_handle_max= 0; + + global_table_container.cleanup(); + global_socket_container.cleanup(); + global_mdl_container.cleanup(); + global_thread_container.cleanup(); + + PFS_FREE_ARRAY(& builtin_memory_global_stages, + stage_class_max, + sizeof(PFS_stage_stat), + global_instr_class_stages_array); + global_instr_class_stages_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_global_statements, + statement_class_max, + sizeof(PFS_statement_stat), + global_instr_class_statements_array); + global_instr_class_statements_array= NULL; + + PFS_FREE_ARRAY(& builtin_memory_global_memory, + memory_class_max, + sizeof(PFS_memory_stat), + global_instr_class_memory_array); + global_instr_class_memory_array= NULL; +} + +C_MODE_START +/** Get hash table key for instrumented files. */ +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); + assert(typed_entry != NULL); + file= *typed_entry; + assert(file != NULL); + *length= file->m_filename_length; + result= file->m_filename; + return const_cast<uchar*> (reinterpret_cast<const uchar*> (result)); +} +C_MODE_END + +/** + Initialize the file name hash. + @return 0 on success +*/ +int init_file_hash(const PFS_global_param *param) +{ + if ((! filename_hash_inited) && (param->m_file_sizing != 0)) + { + lf_hash_init(&pfs_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(&pfs_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) +{ + PFS_mutex *pfs; + pfs_dirty_state dirty_state; + + pfs= global_mutex_container.allocate(& dirty_state, klass->m_volatility); + if (pfs != NULL) + { + pfs->m_identity= identity; + pfs->m_class= klass; + pfs->m_enabled= klass->m_enabled && flag_global_instrumentation; + pfs->m_timed= klass->m_timed; + pfs->m_mutex_stat.reset(); + pfs->m_owner= NULL; + pfs->m_last_locked= 0; + pfs->m_lock.dirty_to_allocated(& dirty_state); + if (klass->is_singleton()) + klass->m_singleton= pfs; + } + + return pfs; +} + +/** + Destroy instrumentation for a mutex instance. + @param pfs the mutex to destroy +*/ +void destroy_mutex(PFS_mutex *pfs) +{ + assert(pfs != NULL); + PFS_mutex_class *klass= pfs->m_class; + /* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME */ + klass->m_mutex_stat.aggregate(& pfs->m_mutex_stat); + pfs->m_mutex_stat.reset(); + if (klass->is_singleton()) + klass->m_singleton= NULL; + + global_mutex_container.deallocate(pfs); +} + +/** + 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) +{ + PFS_rwlock *pfs; + pfs_dirty_state dirty_state; + + pfs= global_rwlock_container.allocate(& dirty_state); + if (pfs != NULL) + { + pfs->m_identity= identity; + pfs->m_class= klass; + pfs->m_enabled= klass->m_enabled && flag_global_instrumentation; + pfs->m_timed= klass->m_timed; + pfs->m_rwlock_stat.reset(); + pfs->m_writer= NULL; + pfs->m_readers= 0; + pfs->m_last_written= 0; + pfs->m_last_read= 0; + pfs->m_lock.dirty_to_allocated(& dirty_state); + if (klass->is_singleton()) + klass->m_singleton= pfs; + } + + return pfs; +} + +/** + Destroy instrumentation for a rwlock instance. + @param pfs the rwlock to destroy +*/ +void destroy_rwlock(PFS_rwlock *pfs) +{ + assert(pfs != NULL); + PFS_rwlock_class *klass= pfs->m_class; + /* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME */ + klass->m_rwlock_stat.aggregate(& pfs->m_rwlock_stat); + pfs->m_rwlock_stat.reset(); + if (klass->is_singleton()) + klass->m_singleton= NULL; + + global_rwlock_container.deallocate(pfs); +} + +/** + 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) +{ + PFS_cond *pfs; + pfs_dirty_state dirty_state; + + pfs= global_cond_container.allocate(& dirty_state); + if (pfs != NULL) + { + pfs->m_identity= identity; + pfs->m_class= klass; + pfs->m_enabled= klass->m_enabled && flag_global_instrumentation; + pfs->m_timed= klass->m_timed; + pfs->m_cond_stat.reset(); + pfs->m_lock.dirty_to_allocated(& dirty_state); + if (klass->is_singleton()) + klass->m_singleton= pfs; + } + + return pfs; +} + +/** + Destroy instrumentation for a condition instance. + @param pfs the condition to destroy +*/ +void destroy_cond(PFS_cond *pfs) +{ + assert(pfs != NULL); + PFS_cond_class *klass= pfs->m_class; + /* Aggregate to EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME */ + klass->m_cond_stat.aggregate(& pfs->m_cond_stat); + pfs->m_cond_stat.reset(); + if (klass->is_singleton()) + klass->m_singleton= NULL; + + global_cond_container.deallocate(pfs); +} + +PFS_thread* PFS_thread::get_current_thread() +{ + return static_cast<PFS_thread*>(my_get_thread_local(THR_PFS)); +} + +void PFS_thread::reset_session_connect_attrs() +{ + m_session_connect_attrs_length= 0; + m_session_connect_attrs_cs_number= 0; + + if ((m_session_connect_attrs != NULL) && + (session_connect_attrs_size_per_thread > 0) ) + { + /* Do not keep user data */ + memset(m_session_connect_attrs, 0, session_connect_attrs_size_per_thread); + } +} + +void PFS_thread::set_history_derived_flags() +{ + if (m_history) + { + m_flag_events_waits_history= flag_events_waits_history; + m_flag_events_waits_history_long= flag_events_waits_history_long; + m_flag_events_stages_history= flag_events_stages_history; + m_flag_events_stages_history_long= flag_events_stages_history_long; + m_flag_events_statements_history= flag_events_statements_history; + m_flag_events_statements_history_long= flag_events_statements_history_long; + m_flag_events_transactions_history= flag_events_transactions_history; + m_flag_events_transactions_history_long= flag_events_transactions_history_long; + } + else + { + m_flag_events_waits_history= false; + m_flag_events_waits_history_long= false; + m_flag_events_stages_history= false; + m_flag_events_stages_history_long= false; + m_flag_events_statements_history= false; + m_flag_events_statements_history_long= false; + m_flag_events_transactions_history= false; + m_flag_events_transactions_history_long= false; + } +} + +void PFS_thread::carry_memory_stat_delta(PFS_memory_stat_delta *delta, uint index) +{ + if (m_account != NULL) + { + m_account->carry_memory_stat_delta(delta, index); + return; + } + + if (m_user != NULL) + { + m_user->carry_memory_stat_delta(delta, index); + /* do not return, need to process m_host below */ + } + + if (m_host != NULL) + { + m_host->carry_memory_stat_delta(delta, index); + return; + } + + carry_global_memory_stat_delta(delta, index); +} + +void carry_global_memory_stat_delta(PFS_memory_stat_delta *delta, uint index) +{ + PFS_memory_stat *stat; + PFS_memory_stat_delta delta_buffer; + + stat= & global_instr_class_memory_array[index]; + (void) stat->apply_delta(delta, &delta_buffer); +} + +/** + Create instrumentation for a thread instance. + @param klass the thread class + @param identity the thread address, + or a value characteristic of this thread + @param processlist_id the PROCESSLIST id, + or 0 if unknown + @return a thread instance, or NULL +*/ +PFS_thread* create_thread(PFS_thread_class *klass, const void *identity, + ulonglong processlist_id) +{ + PFS_thread *pfs; + pfs_dirty_state dirty_state; + + pfs= global_thread_container.allocate(& dirty_state); + if (pfs != NULL) + { + pfs->m_thread_internal_id= + PFS_atomic::add_u64(&thread_internal_id_counter.m_u64, 1); + pfs->m_parent_thread_internal_id= 0; + pfs->m_processlist_id= static_cast<ulong>(processlist_id); + pfs->m_thread_os_id= my_thread_os_id(); + pfs->m_event_id= 1; + pfs->m_stmt_lock.set_allocated(); + pfs->m_session_lock.set_allocated(); + pfs->set_enabled(true); + pfs->set_history(true); + pfs->m_class= klass; + pfs->m_events_waits_current= & pfs->m_events_waits_stack[WAIT_STACK_BOTTOM]; + pfs->m_waits_history_full= false; + pfs->m_waits_history_index= 0; + pfs->m_stages_history_full= false; + pfs->m_stages_history_index= 0; + pfs->m_statements_history_full= false; + pfs->m_statements_history_index= 0; + pfs->m_transactions_history_full= false; + pfs->m_transactions_history_index= 0; + + pfs->reset_stats(); + pfs->reset_session_connect_attrs(); + + pfs->m_filename_hash_pins= NULL; + pfs->m_table_share_hash_pins= NULL; + pfs->m_setup_actor_hash_pins= NULL; + pfs->m_setup_object_hash_pins= NULL; + pfs->m_user_hash_pins= NULL; + pfs->m_account_hash_pins= NULL; + pfs->m_host_hash_pins= NULL; + pfs->m_digest_hash_pins= NULL; + pfs->m_program_hash_pins= NULL; + + pfs->m_username_length= 0; + pfs->m_hostname_length= 0; + pfs->m_dbname_length= 0; + pfs->m_command= 0; + pfs->m_start_time= 0; + pfs->m_stage= 0; + pfs->m_stage_progress= NULL; + pfs->m_processlist_info[0]= '\0'; + pfs->m_processlist_info_length= 0; + pfs->m_connection_type= VIO_CLOSED; + + pfs->m_thd= NULL; + pfs->m_host= NULL; + pfs->m_user= NULL; + pfs->m_account= NULL; + set_thread_account(pfs); + + pfs->m_peer_port = 0; + + /* + For child waits, by default, + - NESTING_EVENT_ID is NULL + - NESTING_EVENT_TYPE is NULL + */ + PFS_events_waits *child_wait= & pfs->m_events_waits_stack[0]; + child_wait->m_event_id= 0; + + /* + For child stages, by default, + - NESTING_EVENT_ID is NULL + - NESTING_EVENT_TYPE is NULL + */ + PFS_events_stages *child_stage= & pfs->m_stage_current; + child_stage->m_nesting_event_id= 0; + + pfs->m_events_statements_count= 0; + pfs->m_transaction_current.m_event_id= 0; + + pfs->m_lock.dirty_to_allocated(& dirty_state); + } + + return pfs; +} + +PFS_mutex *sanitize_mutex(PFS_mutex *unsafe) +{ + return global_mutex_container.sanitize(unsafe); +} + +PFS_rwlock *sanitize_rwlock(PFS_rwlock *unsafe) +{ + return global_rwlock_container.sanitize(unsafe); +} + +PFS_cond *sanitize_cond(PFS_cond *unsafe) +{ + return global_cond_container.sanitize(unsafe); +} + +/** + 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) +{ + return global_thread_container.sanitize(unsafe); +} + +PFS_file *sanitize_file(PFS_file *unsafe) +{ + return global_file_container.sanitize(unsafe); +} + +PFS_socket *sanitize_socket(PFS_socket *unsafe) +{ + return global_socket_container.sanitize(unsafe); +} + +PFS_metadata_lock *sanitize_metadata_lock(PFS_metadata_lock *unsafe) +{ + return global_mdl_container.sanitize(unsafe); +} + +/** + Destroy instrumentation for a thread instance. + @param pfs the thread to destroy +*/ +void destroy_thread(PFS_thread *pfs) +{ + assert(pfs != NULL); + pfs->reset_session_connect_attrs(); + if (pfs->m_account != NULL) + { + pfs->m_account->release(); + pfs->m_account= NULL; + assert(pfs->m_user == NULL); + assert(pfs->m_host == NULL); + } + else + { + if (pfs->m_user != NULL) + { + pfs->m_user->release(); + pfs->m_user= NULL; + } + if (pfs->m_host != NULL) + { + pfs->m_host->release(); + pfs->m_host= 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; + } + if (pfs->m_setup_actor_hash_pins) + { + lf_hash_put_pins(pfs->m_setup_actor_hash_pins); + pfs->m_setup_actor_hash_pins= NULL; + } + if (pfs->m_setup_object_hash_pins) + { + lf_hash_put_pins(pfs->m_setup_object_hash_pins); + pfs->m_setup_object_hash_pins= NULL; + } + if (pfs->m_user_hash_pins) + { + lf_hash_put_pins(pfs->m_user_hash_pins); + pfs->m_user_hash_pins= NULL; + } + if (pfs->m_account_hash_pins) + { + lf_hash_put_pins(pfs->m_account_hash_pins); + pfs->m_account_hash_pins= NULL; + } + if (pfs->m_host_hash_pins) + { + lf_hash_put_pins(pfs->m_host_hash_pins); + pfs->m_host_hash_pins= NULL; + } + if (pfs->m_digest_hash_pins) + { + lf_hash_put_pins(pfs->m_digest_hash_pins); + pfs->m_digest_hash_pins= NULL; + } + if (pfs->m_program_hash_pins) + { + lf_hash_put_pins(pfs->m_program_hash_pins); + pfs->m_program_hash_pins= NULL; + } + global_thread_container.deallocate(pfs); +} + +/** + Get the hash pins for @pfs_filename_hash. + @param thread The running thread. + @returns The LF_HASH pins for the thread. +*/ +LF_PINS* get_filename_hash_pins(PFS_thread *thread) +{ + if (unlikely(thread->m_filename_hash_pins == NULL)) + { + if (! filename_hash_inited) + return NULL; + thread->m_filename_hash_pins= lf_hash_get_pins(&pfs_filename_hash); + } + return thread->m_filename_hash_pins; +} + +/** + 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 + @param create create a file instance if none found + @return a file instance, or NULL +*/ +PFS_file* +find_or_create_file(PFS_thread *thread, PFS_file_class *klass, + const char *filename, uint len, bool create) +{ + PFS_file *pfs; + + assert(klass != NULL || ! create); + + LF_PINS *pins= get_filename_hash_pins(thread); + if (unlikely(pins == NULL)) + { + global_file_container.m_lost++; + return NULL; + } + + char safe_buffer[FN_REFLEN]; + const char *safe_filename; + + if (len >= FN_REFLEN) + { + /* + The instrumented code uses file names that exceeds FN_REFLEN. + This could be legal for instrumentation on non mysys APIs, + so we support it. + Truncate the file name so that: + - it fits into pfs->m_filename + - it is safe to use mysys apis to normalize the file name. + */ + memcpy(safe_buffer, filename, FN_REFLEN - 1); + safe_buffer[FN_REFLEN - 1]= 0; + safe_filename= safe_buffer; + } + else + safe_filename= filename; + + /* + Normalize the file name to avoid duplicates when using aliases: + - absolute or relative paths + - symbolic links + Names are resolved as follows: + - /real/path/to/real_file ==> same + - /path/with/link/to/real_file ==> /real/path/to/real_file + - real_file ==> /real/path/to/real_file + - ./real_file ==> /real/path/to/real_file + - /real/path/to/sym_link ==> same + - /path/with/link/to/sym_link ==> /real/path/to/sym_link + - sym_link ==> /real/path/to/sym_link + - ./sym_link ==> /real/path/to/sym_link + When the last component of a file is a symbolic link, + the last component is *not* resolved, so that all file io + operations on a link (create, read, write, delete) are counted + against the link itself, not the target file. + Resolving the name would lead to create counted against the link, + and read/write/delete counted against the target, leading to + incoherent results and instrumentation leaks. + Also note that, when creating files, this name resolution + works properly for files that do not exist (yet) on the file system. + */ + char buffer[FN_REFLEN]; + char dirbuffer[FN_REFLEN]; + size_t dirlen; + const char *normalized_filename; + uint normalized_length; + + dirlen= dirname_length(safe_filename); + if (dirlen == 0) + { + dirbuffer[0]= FN_CURLIB; + dirbuffer[1]= FN_LIBCHAR; + dirbuffer[2]= '\0'; + } + else + { + memcpy(dirbuffer, safe_filename, dirlen); + dirbuffer[dirlen]= '\0'; + } + + if (my_realpath(buffer, dirbuffer, MYF(0)) != 0) + { + global_file_container.m_lost++; + return NULL; + } + + /* Append the unresolved file name to the resolved path */ + char *ptr= buffer + strlen(buffer); + char *buf_end= &buffer[sizeof(buffer)-1]; + if ((buf_end > ptr) && (*(ptr-1) != FN_LIBCHAR)) + *ptr++= FN_LIBCHAR; + if (buf_end > ptr) + strncpy(ptr, safe_filename + dirlen, buf_end - ptr); + *buf_end= '\0'; + + normalized_filename= buffer; + normalized_length= (uint)strlen(normalized_filename); + + PFS_file **entry; + uint retry_count= 0; + const uint retry_max= 3; + pfs_dirty_state dirty_state; + +search: + + entry= reinterpret_cast<PFS_file**> + (lf_hash_search(&pfs_filename_hash, pins, + normalized_filename, normalized_length)); + if (entry && (entry != MY_ERRPTR)) + { + pfs= *entry; + pfs->m_file_stat.m_open_count++; + lf_hash_search_unpin(pins); + return pfs; + } + + lf_hash_search_unpin(pins); + + if (! create) + { + /* No lost counter, just looking for the file existence. */ + return NULL; + } + + pfs= global_file_container.allocate(& dirty_state); + if (pfs != NULL) + { + pfs->m_class= klass; + pfs->m_enabled= klass->m_enabled && flag_global_instrumentation; + pfs->m_timed= klass->m_timed; + memcpy(pfs->m_filename, normalized_filename, normalized_length); + pfs->m_filename[normalized_length]= '\0'; + pfs->m_filename_length= normalized_length; + pfs->m_file_stat.m_open_count= 1; + pfs->m_file_stat.m_io_stat.reset(); + pfs->m_identity= (const void *)pfs; + pfs->m_temporary= false; + + int res; + pfs->m_lock.dirty_to_allocated(& dirty_state); + res= lf_hash_insert(&pfs_filename_hash, pins, + &pfs); + if (likely(res == 0)) + { + if (klass->is_singleton()) + klass->m_singleton= pfs; + return pfs; + } + + global_file_container.deallocate(pfs); + + if (res > 0) + { + /* Duplicate insert by another thread */ + if (++retry_count > retry_max) + { + /* Avoid infinite loops */ + global_file_container.m_lost++; + return NULL; + } + goto search; + } + + /* OOM in lf_hash_insert */ + global_file_container.m_lost++; + return NULL; + } + + return NULL; +} + +/** + Find a file instrumentation instance by name, and rename it + @param thread the executing instrumented thread + @param old_filename the file to be renamed + @param old_len the length in bytes of the old filename + @param new_filename the new file name + @param new_len the length in bytes of the new filename +*/ +void find_and_rename_file(PFS_thread *thread, const char *old_filename, + uint old_len, const char *new_filename, uint new_len) +{ + PFS_file *pfs; + + assert(thread != NULL); + + LF_PINS *pins= get_filename_hash_pins(thread); + if (unlikely(pins == NULL)) + { + global_file_container.m_lost++; + return; + } + + /* + Normalize the old file name. + */ + char safe_buffer[FN_REFLEN]; + const char *safe_filename; + + if (old_len >= FN_REFLEN) + { + memcpy(safe_buffer, old_filename, FN_REFLEN - 1); + safe_buffer[FN_REFLEN - 1]= 0; + safe_filename= safe_buffer; + } + else + safe_filename= old_filename; + + char buffer[FN_REFLEN]; + char dirbuffer[FN_REFLEN]; + size_t dirlen; + const char *normalized_filename; + uint normalized_length; + + dirlen= dirname_length(safe_filename); + if (dirlen == 0) + { + dirbuffer[0]= FN_CURLIB; + dirbuffer[1]= FN_LIBCHAR; + dirbuffer[2]= '\0'; + } + else + { + memcpy(dirbuffer, safe_filename, dirlen); + dirbuffer[dirlen]= '\0'; + } + + if (my_realpath(buffer, dirbuffer, MYF(0)) != 0) + { + global_file_container.m_lost++; + return; + } + + /* Append the unresolved file name to the resolved path */ + char *ptr= buffer + strlen(buffer); + char *buf_end= &buffer[sizeof(buffer)-1]; + if ((buf_end > ptr) && (*(ptr-1) != FN_LIBCHAR)) + *ptr++= FN_LIBCHAR; + if (buf_end > ptr) + strncpy(ptr, safe_filename + dirlen, buf_end - ptr); + *buf_end= '\0'; + + normalized_filename= buffer; + normalized_length= (uint)strlen(normalized_filename); + + PFS_file **entry; + entry= reinterpret_cast<PFS_file**> + (lf_hash_search(&pfs_filename_hash, pins, + normalized_filename, normalized_length)); + + if (entry && (entry != MY_ERRPTR)) + pfs= *entry; + else + { + lf_hash_search_unpin(pins); + return; + } + + lf_hash_delete(&pfs_filename_hash, pins, + pfs->m_filename, pfs->m_filename_length); + + /* + Normalize the new file name. + */ + if (new_len >= FN_REFLEN) + { + memcpy(safe_buffer, new_filename, FN_REFLEN - 1); + safe_buffer[FN_REFLEN - 1]= 0; + safe_filename= safe_buffer; + } + else + safe_filename= new_filename; + + dirlen= dirname_length(safe_filename); + if (dirlen == 0) + { + dirbuffer[0]= FN_CURLIB; + dirbuffer[1]= FN_LIBCHAR; + dirbuffer[2]= '\0'; + } + else + { + memcpy(dirbuffer, safe_filename, dirlen); + dirbuffer[dirlen]= '\0'; + } + + if (my_realpath(buffer, dirbuffer, MYF(0)) != 0) + { + global_file_container.m_lost++; + return; + } + + /* Append the unresolved file name to the resolved path */ + ptr= buffer + strlen(buffer); + buf_end= &buffer[sizeof(buffer)-1]; + if ((buf_end > ptr) && (*(ptr-1) != FN_LIBCHAR)) + *ptr++= FN_LIBCHAR; + if (buf_end > ptr) + strncpy(ptr, safe_filename + dirlen, buf_end - ptr); + *buf_end= '\0'; + + normalized_filename= buffer; + normalized_length= (uint)strlen(normalized_filename); + + memcpy(pfs->m_filename, normalized_filename, normalized_length); + pfs->m_filename[normalized_length]= '\0'; + pfs->m_filename_length= normalized_length; + + int res; + res= lf_hash_insert(&pfs_filename_hash, pins, &pfs); + + if (likely(res == 0)) + return; + else + { + global_file_container.deallocate(pfs); + global_file_container.m_lost++; + return; + } +} + +/** + Release instrumentation for a file instance. + @param pfs the file to release +*/ +void release_file(PFS_file *pfs) +{ + 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) +{ + assert(thread != NULL); + assert(pfs != NULL); + PFS_file_class *klass= pfs->m_class; + + /* Aggregate to FILE_SUMMARY_BY_EVENT_NAME */ + klass->m_file_stat.aggregate(& pfs->m_file_stat); + pfs->m_file_stat.reset(); + + if (klass->is_singleton()) + klass->m_singleton= NULL; + + LF_PINS *pins= get_filename_hash_pins(thread); + assert(pins != NULL); + + lf_hash_delete(&pfs_filename_hash, pins, + pfs->m_filename, pfs->m_filename_length); + if (klass->is_singleton()) + klass->m_singleton= NULL; + + global_file_container.deallocate(pfs); +} + +/** + Create instrumentation for a table instance. + @param share the table share + @param opening_thread the opening thread + @param identity the table address + @return a table instance, or NULL +*/ +PFS_table* create_table(PFS_table_share *share, PFS_thread *opening_thread, + const void *identity) +{ + PFS_table *pfs; + pfs_dirty_state dirty_state; + + pfs= global_table_container.allocate(& dirty_state); + if (pfs != NULL) + { + pfs->m_identity= identity; + pfs->m_share= share; + pfs->m_io_enabled= share->m_enabled && + flag_global_instrumentation && global_table_io_class.m_enabled; + pfs->m_io_timed= share->m_timed && global_table_io_class.m_timed; + pfs->m_lock_enabled= share->m_enabled && + flag_global_instrumentation && global_table_lock_class.m_enabled; + pfs->m_lock_timed= share->m_timed && global_table_lock_class.m_timed; + pfs->m_has_io_stats= false; + pfs->m_has_lock_stats= false; + pfs->m_internal_lock= PFS_TL_NONE; + pfs->m_external_lock= PFS_TL_NONE; + share->inc_refcount(); + pfs->m_table_stat.fast_reset(); + pfs->m_thread_owner= opening_thread; + pfs->m_owner_event_id= opening_thread->m_event_id; + pfs->m_lock.dirty_to_allocated(& dirty_state); + } + + return pfs; +} + +void PFS_table::sanitized_aggregate(void) +{ + /* + This thread could be a TRUNCATE on an aggregated summary table, + and not own the table handle. + */ + PFS_table_share *safe_share= sanitize_table_share(m_share); + if (safe_share != NULL) + { + if (m_has_io_stats) + { + safe_aggregate_io(NULL, & m_table_stat, safe_share); + m_has_io_stats= false; + } + if (m_has_lock_stats) + { + safe_aggregate_lock(& m_table_stat, safe_share); + m_has_lock_stats= false; + } + } +} + +void PFS_table::sanitized_aggregate_io(void) +{ + PFS_table_share *safe_share= sanitize_table_share(m_share); + if (safe_share != NULL && m_has_io_stats) + { + safe_aggregate_io(NULL, & m_table_stat, safe_share); + m_has_io_stats= false; + } +} + +void PFS_table::sanitized_aggregate_lock(void) +{ + PFS_table_share *safe_share= sanitize_table_share(m_share); + if (safe_share != NULL && m_has_lock_stats) + { + safe_aggregate_lock(& m_table_stat, safe_share); + m_has_lock_stats= false; + } +} + +void PFS_table::safe_aggregate_io(const TABLE_SHARE *optional_server_share, + PFS_table_stat *table_stat, + PFS_table_share *table_share) +{ + assert(table_stat != NULL); + assert(table_share != NULL); + + uint key_count= sanitize_index_count(table_share->m_key_count); + + PFS_table_share_index *to_stat; + PFS_table_io_stat *from_stat; + uint index; + + assert(key_count <= MAX_INDEXES); + + /* Aggregate stats for each index, if any */ + for (index= 0; index < key_count; index++) + { + from_stat= & table_stat->m_index_stat[index]; + if (from_stat->m_has_data) + { + if (optional_server_share != NULL) + { + /* + An instrumented thread is closing a table, + and capable of providing index names when + creating index statistics on the fly. + */ + to_stat= table_share->find_or_create_index_stat(optional_server_share, index); + } + else + { + /* + A monitoring thread, performing TRUNCATE TABLE, + is asking to flush existing stats from table handles, + but it does not know about index names used in handles. + If the index stat already exists, find it and aggregate to it. + It the index stat does not exist yet, drop the stat and do nothing. + */ + to_stat= table_share->find_index_stat(index); + } + if (to_stat != NULL) + { + /* Aggregate to TABLE_IO_SUMMARY */ + to_stat->m_stat.aggregate(from_stat); + } + } + } + + /* Aggregate stats for the table */ + from_stat= & table_stat->m_index_stat[MAX_INDEXES]; + if (from_stat->m_has_data) + { + to_stat= table_share->find_or_create_index_stat(NULL, MAX_INDEXES); + if (to_stat != NULL) + { + /* Aggregate to TABLE_IO_SUMMARY */ + to_stat->m_stat.aggregate(from_stat); + } + } + + table_stat->fast_reset_io(); +} + +void PFS_table::safe_aggregate_lock(PFS_table_stat *table_stat, + PFS_table_share *table_share) +{ + assert(table_stat != NULL); + assert(table_share != NULL); + + PFS_table_lock_stat *from_stat= & table_stat->m_lock_stat; + + PFS_table_share_lock *to_stat; + + to_stat= table_share->find_or_create_lock_stat(); + if (to_stat != NULL) + { + /* Aggregate to TABLE_LOCK_SUMMARY */ + to_stat->m_stat.aggregate(from_stat); + } + + table_stat->fast_reset_lock(); +} + +/** + Destroy instrumentation for a table instance. + @param pfs the table to destroy +*/ +void destroy_table(PFS_table *pfs) +{ + assert(pfs != NULL); + pfs->m_share->dec_refcount(); + global_table_container.deallocate(pfs); +} + +/** + Create instrumentation for a socket instance. + @param klass the socket class + @param fd the socket file descriptor + @param addr the socket address + @param addr_len the socket address length + @return a socket instance, or NULL +*/ +PFS_socket* create_socket(PFS_socket_class *klass, const my_socket *fd, + const struct sockaddr *addr, socklen_t addr_len) +{ + PFS_socket *pfs; + pfs_dirty_state dirty_state; + + uint fd_used= 0; + uint addr_len_used= addr_len; + + if (fd != NULL) + fd_used= (int)*fd; + + if (addr_len_used > sizeof(sockaddr_storage)) + addr_len_used= sizeof(sockaddr_storage); + + pfs= global_socket_container.allocate(& dirty_state); + + if (pfs != NULL) + { + pfs->m_fd= fd_used; + /* There is no socket object, so we use the instrumentation. */ + pfs->m_identity= pfs; + pfs->m_class= klass; + pfs->m_enabled= klass->m_enabled && flag_global_instrumentation; + pfs->m_timed= klass->m_timed; + pfs->m_idle= false; + pfs->m_socket_stat.reset(); + pfs->m_thread_owner= NULL; + + pfs->m_addr_len= addr_len_used; + if ((addr != NULL) && (addr_len_used > 0)) + { + pfs->m_addr_len= addr_len_used; + memcpy(&pfs->m_sock_addr, addr, addr_len_used); + } + else + { + pfs->m_addr_len= 0; + } + + pfs->m_lock.dirty_to_allocated(& dirty_state); + + if (klass->is_singleton()) + klass->m_singleton= pfs; + } + + return pfs; +} + +/** + Destroy instrumentation for a socket instance. + @param pfs the socket to destroy +*/ +void destroy_socket(PFS_socket *pfs) +{ + assert(pfs != NULL); + PFS_socket_class *klass= pfs->m_class; + + /* Aggregate to SOCKET_SUMMARY_BY_EVENT_NAME */ + klass->m_socket_stat.m_io_stat.aggregate(&pfs->m_socket_stat.m_io_stat); + + if (klass->is_singleton()) + klass->m_singleton= NULL; + + /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME */ + PFS_thread *thread= pfs->m_thread_owner; + if (thread != NULL) + { + /* Combine stats for all operations */ + PFS_single_stat stat; + pfs->m_socket_stat.m_io_stat.sum_waits(&stat); + if (stat.m_count != 0) + { + PFS_single_stat *event_name_array; + event_name_array= thread->write_instr_class_waits_stats(); + uint index= pfs->m_class->m_event_name_index; + + event_name_array[index].aggregate(&stat); + } + } + + pfs->m_socket_stat.reset(); + pfs->m_thread_owner= NULL; + pfs->m_fd= 0; + pfs->m_addr_len= 0; + + global_socket_container.deallocate(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) +{ + PFS_metadata_lock *pfs; + pfs_dirty_state dirty_state; + + pfs= global_mdl_container.allocate(& dirty_state); + if (pfs != NULL) + { + pfs->m_identity= identity; + pfs->m_enabled= global_metadata_class.m_enabled && flag_global_instrumentation; + pfs->m_timed= global_metadata_class.m_timed; + pfs->m_mdl_key.mdl_key_init(mdl_key); + pfs->m_mdl_type= mdl_type; + pfs->m_mdl_duration= mdl_duration; + pfs->m_mdl_status= mdl_status; + pfs->m_src_file= src_file; + pfs->m_src_line= src_line; + pfs->m_owner_thread_id= 0; + pfs->m_owner_event_id= 0; + pfs->m_lock.dirty_to_allocated(& dirty_state); + } + + return pfs; +} + +void destroy_metadata_lock(PFS_metadata_lock *pfs) +{ + assert(pfs != NULL); + global_mdl_container.deallocate(pfs); +} + +static void fct_reset_mutex_waits(PFS_mutex *pfs) +{ + pfs->m_mutex_stat.reset(); +} + +static void reset_mutex_waits_by_instance(void) +{ + global_mutex_container.apply_all(fct_reset_mutex_waits); +} + +static void fct_reset_rwlock_waits(PFS_rwlock *pfs) +{ + pfs->m_rwlock_stat.reset(); +} + +static void reset_rwlock_waits_by_instance(void) +{ + global_rwlock_container.apply_all(fct_reset_rwlock_waits); +} + +static void fct_reset_cond_waits(PFS_cond *pfs) +{ + pfs->m_cond_stat.reset(); +} + +static void reset_cond_waits_by_instance(void) +{ + global_cond_container.apply_all(fct_reset_cond_waits); +} + +static void fct_reset_file_waits(PFS_file *pfs) +{ + pfs->m_file_stat.reset(); +} + +static void reset_file_waits_by_instance(void) +{ + global_file_container.apply_all(fct_reset_file_waits); +} + +static void fct_reset_socket_waits(PFS_socket *pfs) +{ + pfs->m_socket_stat.reset(); +} + +static void reset_socket_waits_by_instance(void) +{ + global_socket_container.apply_all(fct_reset_socket_waits); +} + +/** 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_socket_waits_by_instance(); +} + +static void fct_reset_file_io(PFS_file *pfs) +{ + pfs->m_file_stat.m_io_stat.reset(); +} + +/** Reset the io statistics per file instance. */ +void reset_file_instance_io(void) +{ + global_file_container.apply_all(fct_reset_file_io); +} + +static void fct_reset_socket_io(PFS_socket *pfs) +{ + pfs->m_socket_stat.m_io_stat.reset(); +} + +/** Reset the io statistics per socket instance. */ +void reset_socket_instance_io(void) +{ + global_socket_container.apply_all(fct_reset_socket_io); +} + +void aggregate_all_event_names(PFS_single_stat *from_array, + PFS_single_stat *to_array) +{ + PFS_single_stat *from; + PFS_single_stat *from_last; + PFS_single_stat *to; + + from= from_array; + from_last= from_array + wait_class_max; + to= to_array; + + for ( ; from < from_last ; from++, to++) + { + if (from->m_count > 0) + { + to->aggregate(from); + from->reset(); + } + } +} + +void aggregate_all_event_names(PFS_single_stat *from_array, + PFS_single_stat *to_array_1, + PFS_single_stat *to_array_2) +{ + PFS_single_stat *from; + PFS_single_stat *from_last; + PFS_single_stat *to_1; + PFS_single_stat *to_2; + + from= from_array; + from_last= from_array + wait_class_max; + to_1= to_array_1; + to_2= to_array_2; + + for ( ; from < from_last ; from++, to_1++, to_2++) + { + if (from->m_count > 0) + { + to_1->aggregate(from); + to_2->aggregate(from); + from->reset(); + } + } +} + +void aggregate_all_stages(PFS_stage_stat *from_array, + PFS_stage_stat *to_array) +{ + PFS_stage_stat *from; + PFS_stage_stat *from_last; + PFS_stage_stat *to; + + from= from_array; + from_last= from_array + stage_class_max; + to= to_array; + + for ( ; from < from_last ; from++, to++) + { + if (from->m_timer1_stat.m_count > 0) + { + to->aggregate(from); + from->reset(); + } + } +} + +void aggregate_all_stages(PFS_stage_stat *from_array, + PFS_stage_stat *to_array_1, + PFS_stage_stat *to_array_2) +{ + PFS_stage_stat *from; + PFS_stage_stat *from_last; + PFS_stage_stat *to_1; + PFS_stage_stat *to_2; + + from= from_array; + from_last= from_array + stage_class_max; + to_1= to_array_1; + to_2= to_array_2; + + for ( ; from < from_last ; from++, to_1++, to_2++) + { + if (from->m_timer1_stat.m_count > 0) + { + to_1->aggregate(from); + to_2->aggregate(from); + from->reset(); + } + } +} + +void aggregate_all_statements(PFS_statement_stat *from_array, + PFS_statement_stat *to_array) +{ + PFS_statement_stat *from; + PFS_statement_stat *from_last; + PFS_statement_stat *to; + + from= from_array; + from_last= from_array + statement_class_max; + to= to_array; + + for ( ; from < from_last ; from++, to++) + { + if (from->m_timer1_stat.m_count > 0) + { + to->aggregate(from); + from->reset(); + } + } +} + +void aggregate_all_statements(PFS_statement_stat *from_array, + PFS_statement_stat *to_array_1, + PFS_statement_stat *to_array_2) +{ + PFS_statement_stat *from; + PFS_statement_stat *from_last; + PFS_statement_stat *to_1; + PFS_statement_stat *to_2; + + from= from_array; + from_last= from_array + statement_class_max; + to_1= to_array_1; + to_2= to_array_2; + + for ( ; from < from_last ; from++, to_1++, to_2++) + { + if (from->m_timer1_stat.m_count > 0) + { + to_1->aggregate(from); + to_2->aggregate(from); + from->reset(); + } + } +} + +void aggregate_all_transactions(PFS_transaction_stat *from_array, + PFS_transaction_stat *to_array) +{ + assert(from_array != NULL); + assert(to_array != NULL); + + if (from_array->count() > 0) + { + to_array->aggregate(from_array); + from_array->reset(); + } +} + +void aggregate_all_transactions(PFS_transaction_stat *from_array, + PFS_transaction_stat *to_array_1, + PFS_transaction_stat *to_array_2) +{ + assert(from_array != NULL); + assert(to_array_1 != NULL); + assert(to_array_2 != NULL); + + if (from_array->count() > 0) + { + to_array_1->aggregate(from_array); + to_array_2->aggregate(from_array); + from_array->reset(); + } +} + +void aggregate_all_memory(bool alive, + PFS_memory_stat *from_array, + PFS_memory_stat *to_array) +{ + PFS_memory_stat *from; + PFS_memory_stat *from_last; + PFS_memory_stat *to; + + from= from_array; + from_last= from_array + memory_class_max; + to= to_array; + + if (alive) + { + for ( ; from < from_last ; from++, to++) + { + from->partial_aggregate_to(to); + } + } + else + { + for ( ; from < from_last ; from++, to++) + { + from->full_aggregate_to(to); + from->reset(); + } + } +} + +void aggregate_all_memory(bool alive, + PFS_memory_stat *from_array, + PFS_memory_stat *to_array_1, + PFS_memory_stat *to_array_2) +{ + PFS_memory_stat *from; + PFS_memory_stat *from_last; + PFS_memory_stat *to_1; + PFS_memory_stat *to_2; + + from= from_array; + from_last= from_array + memory_class_max; + to_1= to_array_1; + to_2= to_array_2; + + if (alive) + { + for ( ; from < from_last ; from++, to_1++, to_2++) + { + from->partial_aggregate_to(to_1, to_2); + } + } + else + { + for ( ; from < from_last ; from++, to_1++, to_2++) + { + from->full_aggregate_to(to_1, to_2); + from->reset(); + } + } +} + +void aggregate_thread_status(PFS_thread *thread, + PFS_account *safe_account, + PFS_user *safe_user, + PFS_host *safe_host) +{ + THD *thd= thread->m_thd; + + if (thd == NULL) + return; + + if (likely(safe_account != NULL)) + { + safe_account->aggregate_status_stats(&thd->status_var); + return; + } + + if (safe_user != NULL) + { + safe_user->aggregate_status_stats(&thd->status_var); + } + + if (safe_host != NULL) + { + safe_host->aggregate_status_stats(&thd->status_var); + } + return; +} + +void aggregate_thread_stats(PFS_thread *thread, + PFS_account *safe_account, + PFS_user *safe_user, + PFS_host *safe_host) +{ + if (likely(safe_account != NULL)) + { + safe_account->m_disconnected_count++; + } + + if (safe_user != NULL) + { + safe_user->m_disconnected_count++; + } + + if (safe_host != NULL) + { + safe_host->m_disconnected_count++; + } + + /* There is no global table for connections statistics. */ + return; +} + +void aggregate_thread(PFS_thread *thread, + PFS_account *safe_account, + PFS_user *safe_user, + PFS_host *safe_host) +{ + /* No HAVE_PSI_???_INTERFACE flag, waits cover multiple instrumentations */ + aggregate_thread_waits(thread, safe_account, safe_user, safe_host); + +#ifdef HAVE_PSI_STAGE_INTERFACE + aggregate_thread_stages(thread, safe_account, safe_user, safe_host); +#endif + +#ifdef HAVE_PSI_STATEMENT_INTERFACE + aggregate_thread_statements(thread, safe_account, safe_user, safe_host); +#endif + +#ifdef HAVE_PSI_TRANSACTION_INTERFACE + aggregate_thread_transactions(thread, safe_account, safe_user, safe_host); +#endif + +#ifdef HAVE_PSI_MEMORY_INTERFACE + aggregate_thread_memory(false, thread, safe_account, safe_user, safe_host); +#endif + + if (!show_compatibility_56) + aggregate_thread_status(thread, safe_account, safe_user, safe_host); + + aggregate_thread_stats(thread, safe_account, safe_user, safe_host); +} + +void aggregate_thread_waits(PFS_thread *thread, + PFS_account *safe_account, + PFS_user *safe_user, + PFS_host *safe_host) +{ + if (thread->read_instr_class_waits_stats() == NULL) + return; + + if (likely(safe_account != NULL)) + { + /* + Aggregate EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME + to EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. + */ + aggregate_all_event_names(thread->write_instr_class_waits_stats(), + safe_account->write_instr_class_waits_stats()); + + return; + } + + if ((safe_user != NULL) && (safe_host != NULL)) + { + /* + Aggregate EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME to: + - EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME + in parallel. + */ + aggregate_all_event_names(thread->write_instr_class_waits_stats(), + safe_user->write_instr_class_waits_stats(), + safe_host->write_instr_class_waits_stats()); + return; + } + + if (safe_user != NULL) + { + /* + Aggregate EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME + to EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME, directly. + */ + aggregate_all_event_names(thread->write_instr_class_waits_stats(), + safe_user->write_instr_class_waits_stats()); + return; + } + + if (safe_host != NULL) + { + /* + Aggregate EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME + to EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME, directly. + */ + aggregate_all_event_names(thread->write_instr_class_waits_stats(), + safe_host->write_instr_class_waits_stats()); + return; + } + + /* Orphan thread, clean the waits stats. */ + thread->reset_waits_stats(); +} + +void aggregate_thread_stages(PFS_thread *thread, + PFS_account *safe_account, + PFS_user *safe_user, + PFS_host *safe_host) +{ + if (thread->read_instr_class_stages_stats() == NULL) + return; + + if (likely(safe_account != NULL)) + { + /* + Aggregate EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME + to EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. + */ + aggregate_all_stages(thread->write_instr_class_stages_stats(), + safe_account->write_instr_class_stages_stats()); + + return; + } + + if ((safe_user != NULL) && (safe_host != NULL)) + { + /* + Aggregate EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME to: + - EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME + in parallel. + */ + aggregate_all_stages(thread->write_instr_class_stages_stats(), + safe_user->write_instr_class_stages_stats(), + safe_host->write_instr_class_stages_stats()); + return; + } + + if (safe_user != NULL) + { + /* + Aggregate EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME to: + - EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME + in parallel. + */ + aggregate_all_stages(thread->write_instr_class_stages_stats(), + safe_user->write_instr_class_stages_stats(), + global_instr_class_stages_array); + return; + } + + if (safe_host != NULL) + { + /* + Aggregate EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME + to EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME, directly. + */ + aggregate_all_stages(thread->write_instr_class_stages_stats(), + safe_host->write_instr_class_stages_stats()); + return; + } + + /* + Aggregate EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME + to EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME. + */ + aggregate_all_stages(thread->write_instr_class_stages_stats(), + global_instr_class_stages_array); +} + +void aggregate_thread_statements(PFS_thread *thread, + PFS_account *safe_account, + PFS_user *safe_user, + PFS_host *safe_host) +{ + if (thread->read_instr_class_statements_stats() == NULL) + return; + + if (likely(safe_account != NULL)) + { + /* + Aggregate EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME + to EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. + */ + aggregate_all_statements(thread->write_instr_class_statements_stats(), + safe_account->write_instr_class_statements_stats()); + + return; + } + + if ((safe_user != NULL) && (safe_host != NULL)) + { + /* + Aggregate EVENTS_STATEMENT_SUMMARY_BY_THREAD_BY_EVENT_NAME to: + - EVENTS_STATEMENT_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_STATEMENT_SUMMARY_BY_HOST_BY_EVENT_NAME + in parallel. + */ + aggregate_all_statements(thread->write_instr_class_statements_stats(), + safe_user->write_instr_class_statements_stats(), + safe_host->write_instr_class_statements_stats()); + return; + } + + if (safe_user != NULL) + { + /* + Aggregate EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME to: + - EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME + in parallel. + */ + aggregate_all_statements(thread->write_instr_class_statements_stats(), + safe_user->write_instr_class_statements_stats(), + global_instr_class_statements_array); + return; + } + + if (safe_host != NULL) + { + /* + Aggregate EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME + to EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME, directly. + */ + aggregate_all_statements(thread->write_instr_class_statements_stats(), + safe_host->write_instr_class_statements_stats()); + return; + } + + /* + Aggregate EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME + to EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME. + */ + aggregate_all_statements(thread->write_instr_class_statements_stats(), + global_instr_class_statements_array); +} + +void aggregate_thread_transactions(PFS_thread *thread, + PFS_account *safe_account, + PFS_user *safe_user, + PFS_host *safe_host) +{ + if (thread->read_instr_class_transactions_stats() == NULL) + return; + + if (likely(safe_account != NULL)) + { + /* + Aggregate EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME + to EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. + */ + aggregate_all_transactions(thread->write_instr_class_transactions_stats(), + safe_account->write_instr_class_transactions_stats()); + + return; + } + + if ((safe_user != NULL) && (safe_host != NULL)) + { + /* + Aggregate EVENTS_TRANSACTION_SUMMARY_BY_THREAD_BY_EVENT_NAME to: + - EVENTS_TRANSACTION_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_TRANSACTION_SUMMARY_BY_HOST_BY_EVENT_NAME + in parallel. + */ + aggregate_all_transactions(thread->write_instr_class_transactions_stats(), + safe_user->write_instr_class_transactions_stats(), + safe_host->write_instr_class_transactions_stats()); + return; + } + + if (safe_user != NULL) + { + /* + Aggregate EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME to: + - EVENTS_TRANSACTIONS_SUMMARY_BY_USER_BY_EVENT_NAME + - EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME + in parallel. + */ + aggregate_all_transactions(thread->write_instr_class_transactions_stats(), + safe_user->write_instr_class_transactions_stats(), + &global_transaction_stat); + return; + } + + if (safe_host != NULL) + { + /* + Aggregate EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME + to EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME, directly. + */ + aggregate_all_transactions(thread->write_instr_class_transactions_stats(), + safe_host->write_instr_class_transactions_stats()); + return; + } + + /* + Aggregate EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME + to EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME. + */ + aggregate_all_transactions(thread->write_instr_class_transactions_stats(), + &global_transaction_stat); +} + +void aggregate_thread_memory(bool alive, PFS_thread *thread, + PFS_account *safe_account, + PFS_user *safe_user, + PFS_host *safe_host) +{ + if (thread->read_instr_class_memory_stats() == NULL) + return; + + if (likely(safe_account != NULL)) + { + /* + Aggregate MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME + to MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. + */ + aggregate_all_memory(alive, + thread->write_instr_class_memory_stats(), + safe_account->write_instr_class_memory_stats()); + + return; + } + + if ((safe_user != NULL) && (safe_host != NULL)) + { + /* + Aggregate MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME to: + - MEMORY_SUMMARY_BY_USER_BY_EVENT_NAME + - MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME + in parallel. + */ + aggregate_all_memory(alive, + thread->write_instr_class_memory_stats(), + safe_user->write_instr_class_memory_stats(), + safe_host->write_instr_class_memory_stats()); + return; + } + + if (safe_user != NULL) + { + /* + Aggregate MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME to: + - MEMORY_SUMMARY_BY_USER_BY_EVENT_NAME + - MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME + in parallel. + */ + aggregate_all_memory(alive, + thread->write_instr_class_memory_stats(), + safe_user->write_instr_class_memory_stats(), + global_instr_class_memory_array); + return; + } + + if (safe_host != NULL) + { + /* + Aggregate MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME + to MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME, directly. + */ + aggregate_all_memory(alive, + thread->write_instr_class_memory_stats(), + safe_host->write_instr_class_memory_stats()); + return; + } + + /* + Aggregate MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME + to MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME. + */ + aggregate_all_memory(alive, + thread->write_instr_class_memory_stats(), + global_instr_class_memory_array); +} + +void clear_thread_account(PFS_thread *thread) +{ + if (thread->m_account != NULL) + { + thread->m_account->release(); + thread->m_account= NULL; + } + + if (thread->m_user != NULL) + { + thread->m_user->release(); + thread->m_user= NULL; + } + + if (thread->m_host != NULL) + { + thread->m_host->release(); + thread->m_host= NULL; + } +} + +void set_thread_account(PFS_thread *thread) +{ + assert(thread->m_account == NULL); + assert(thread->m_user == NULL); + assert(thread->m_host == NULL); + + thread->m_account= find_or_create_account(thread, + thread->m_username, + thread->m_username_length, + thread->m_hostname, + thread->m_hostname_length); + + if ((thread->m_account == NULL) && (thread->m_username_length > 0)) + thread->m_user= find_or_create_user(thread, + thread->m_username, + thread->m_username_length); + + if ((thread->m_account == NULL) && (thread->m_hostname_length > 0)) + thread->m_host= find_or_create_host(thread, + thread->m_hostname, + thread->m_hostname_length); +} + +static void fct_update_mutex_derived_flags(PFS_mutex *pfs) +{ + PFS_mutex_class *klass= sanitize_mutex_class(pfs->m_class); + if (likely(klass != NULL)) + { + pfs->m_enabled= klass->m_enabled && flag_global_instrumentation; + pfs->m_timed= klass->m_timed; + } + else + { + pfs->m_enabled= false; + pfs->m_timed= false; + } +} + +void update_mutex_derived_flags() +{ + global_mutex_container.apply_all(fct_update_mutex_derived_flags); +} + +static void fct_update_rwlock_derived_flags(PFS_rwlock *pfs) +{ + PFS_rwlock_class *klass= sanitize_rwlock_class(pfs->m_class); + if (likely(klass != NULL)) + { + pfs->m_enabled= klass->m_enabled && flag_global_instrumentation; + pfs->m_timed= klass->m_timed; + } + else + { + pfs->m_enabled= false; + pfs->m_timed= false; + } +} + +void update_rwlock_derived_flags() +{ + global_rwlock_container.apply_all(fct_update_rwlock_derived_flags); +} + +static void fct_update_cond_derived_flags(PFS_cond *pfs) +{ + PFS_cond_class *klass= sanitize_cond_class(pfs->m_class); + if (likely(klass != NULL)) + { + pfs->m_enabled= klass->m_enabled && flag_global_instrumentation; + pfs->m_timed= klass->m_timed; + } + else + { + pfs->m_enabled= false; + pfs->m_timed= false; + } +} + +void update_cond_derived_flags() +{ + global_cond_container.apply_all(fct_update_cond_derived_flags); +} + +static void fct_update_file_derived_flags(PFS_file *pfs) +{ + PFS_file_class *klass= sanitize_file_class(pfs->m_class); + if (likely(klass != NULL)) + { + pfs->m_enabled= klass->m_enabled && flag_global_instrumentation; + pfs->m_timed= klass->m_timed; + } + else + { + pfs->m_enabled= false; + pfs->m_timed= false; + } +} + +void update_file_derived_flags() +{ + global_file_container.apply_all(fct_update_file_derived_flags); +} + +void fct_update_table_derived_flags(PFS_table *pfs) +{ + PFS_table_share *share= sanitize_table_share(pfs->m_share); + if (likely(share != NULL)) + { + pfs->m_io_enabled= share->m_enabled && + flag_global_instrumentation && global_table_io_class.m_enabled; + pfs->m_io_timed= share->m_timed && global_table_io_class.m_timed; + pfs->m_lock_enabled= share->m_enabled && + flag_global_instrumentation && global_table_lock_class.m_enabled; + pfs->m_lock_timed= share->m_timed && global_table_lock_class.m_timed; + } + else + { + pfs->m_io_enabled= false; + pfs->m_io_timed= false; + pfs->m_lock_enabled= false; + pfs->m_lock_timed= false; + } +} + +void update_table_derived_flags() +{ + global_table_container.apply_all(fct_update_table_derived_flags); +} + +static void fct_update_socket_derived_flags(PFS_socket *pfs) +{ + PFS_socket_class *klass= sanitize_socket_class(pfs->m_class); + if (likely(klass != NULL)) + { + pfs->m_enabled= klass->m_enabled && flag_global_instrumentation; + pfs->m_timed= klass->m_timed; + } + else + { + pfs->m_enabled= false; + pfs->m_timed= false; + } +} + +void update_socket_derived_flags() +{ + global_socket_container.apply_all(fct_update_socket_derived_flags); +} + +static void fct_update_metadata_derived_flags(PFS_metadata_lock *pfs) +{ + pfs->m_enabled= global_metadata_class.m_enabled && flag_global_instrumentation; + pfs->m_timed= global_metadata_class.m_timed; +} + +void update_metadata_derived_flags() +{ + global_mdl_container.apply_all(fct_update_metadata_derived_flags); +} + +static void fct_update_thread_derived_flags(PFS_thread *pfs) +{ + pfs->set_history_derived_flags(); +} + +void update_thread_derived_flags() +{ + global_thread_container.apply(fct_update_thread_derived_flags); +} + +void update_instruments_derived_flags() +{ + update_mutex_derived_flags(); + update_rwlock_derived_flags(); + update_cond_derived_flags(); + update_file_derived_flags(); + update_table_derived_flags(); + update_socket_derived_flags(); + update_metadata_derived_flags(); + /* nothing for stages, statements and transactions (no instances) */ +} + +/** @} */ diff --git a/storage/perfschema/pfs_instr.h b/storage/perfschema/pfs_instr.h new file mode 100644 index 00000000..868f7722 --- /dev/null +++ b/storage/perfschema/pfs_instr.h @@ -0,0 +1,817 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, 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" +#ifdef _WIN32 +#include <winsock2.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#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. */ + uint32 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; + + /** Remote (peer) port */ + uint m_peer_port; + + /** 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 pfs_filename_hash; + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_instr_class.cc b/storage/perfschema/pfs_instr_class.cc new file mode 100644 index 00000000..2b1a80d3 --- /dev/null +++ b/storage/perfschema/pfs_instr_class.cc @@ -0,0 +1,2064 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + Copyright (c) 2020, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_instr_class.cc + Performance schema instruments meta data (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "table.h" +#include "pfs_instr_class.h" +#include "pfs_builtin_memory.h" +#include "pfs_instr.h" +#include "pfs_global.h" +#include "pfs_timer.h" +#include "pfs_events_waits.h" +#include "pfs_setup_object.h" +#include "pfs_atomic.h" +#include "pfs_program.h" +#include "pfs_buffer_container.h" +#include "mysql/psi/mysql_thread.h" +#include "lf.h" + +#include <string.h> + +/** + @defgroup Performance_schema_buffers Performance Schema Buffers + @ingroup Performance_schema_implementation + @{ +*/ + +/** + Global performance schema flag. + Indicate if the performance schema is enabled. + This flag is set at startup, and never changes. +*/ +my_bool pfs_enabled= TRUE; + +/** + PFS_INSTRUMENT option settings array + */ +Pfs_instr_config_array *pfs_instr_config_array= NULL; + +static void configure_instr_class(PFS_instr_class *entry); + +static void init_instr_class(PFS_instr_class *klass, + const char *name, + uint name_length, + int flags, + PFS_class_type class_type); + +/** + Current number of elements in mutex_class_array. + This global variable is written to during: + - the performance schema initialization + - a plugin initialization +*/ +static uint32 mutex_class_dirty_count= 0; +static uint32 mutex_class_allocated_count= 0; +static uint32 rwlock_class_dirty_count= 0; +static uint32 rwlock_class_allocated_count= 0; +static uint32 cond_class_dirty_count= 0; +static uint32 cond_class_allocated_count= 0; + +/** Size of the mutex class array. @sa mutex_class_array */ +ulong mutex_class_max= 0; +/** Number of mutex class lost. @sa mutex_class_array */ +ulong mutex_class_lost= 0; +/** Size of the rwlock class array. @sa rwlock_class_array */ +ulong rwlock_class_max= 0; +/** Number of rwlock class lost. @sa rwlock_class_array */ +ulong rwlock_class_lost= 0; +/** Size of the condition class array. @sa cond_class_array */ +ulong cond_class_max= 0; +/** Number of condition class lost. @sa cond_class_array */ +ulong cond_class_lost= 0; +/** Size of the thread class array. @sa thread_class_array */ +ulong thread_class_max= 0; +/** Number of thread class lost. @sa thread_class_array */ +ulong thread_class_lost= 0; +/** Size of the file class array. @sa file_class_array */ +ulong file_class_max= 0; +/** Number of file class lost. @sa file_class_array */ +ulong file_class_lost= 0; +/** Size of the stage class array. @sa stage_class_array */ +ulong stage_class_max= 0; +/** Number of stage class lost. @sa stage_class_array */ +ulong stage_class_lost= 0; +/** Size of the statement class array. @sa statement_class_array */ +ulong statement_class_max= 0; +/** Number of statement class lost. @sa statement_class_array */ +ulong statement_class_lost= 0; +/** Size of the socket class array. @sa socket_class_array */ +ulong socket_class_max= 0; +/** Number of socket class lost. @sa socket_class_array */ +ulong socket_class_lost= 0; +/** Size of the memory class array. @sa memory_class_array */ +ulong memory_class_max= 0; +/** Number of memory class lost. @sa memory_class_array */ +ulong memory_class_lost= 0; + +/** + Number of transaction classes. Although there is only one transaction class, + this is used for sizing by other event classes. + @sa global_transaction_class +*/ +ulong transaction_class_max= 0; + +PFS_mutex_class *mutex_class_array= NULL; +PFS_rwlock_class *rwlock_class_array= NULL; +PFS_cond_class *cond_class_array= NULL; + +/** + Current number or elements in thread_class_array. + This global variable is written to during: + - the performance schema initialization + - a plugin initialization +*/ +static uint32 thread_class_dirty_count= 0; +static uint32 thread_class_allocated_count= 0; + +static PFS_thread_class *thread_class_array= NULL; + +PFS_ALIGNED PFS_single_stat global_idle_stat; +PFS_ALIGNED PFS_table_io_stat global_table_io_stat; +PFS_ALIGNED PFS_table_lock_stat global_table_lock_stat; +PFS_ALIGNED PFS_single_stat global_metadata_stat; +PFS_ALIGNED PFS_transaction_stat global_transaction_stat; +PFS_ALIGNED PFS_instr_class global_table_io_class; +PFS_ALIGNED PFS_instr_class global_table_lock_class; +PFS_ALIGNED PFS_instr_class global_idle_class; +PFS_ALIGNED PFS_instr_class global_metadata_class; +PFS_ALIGNED PFS_transaction_class global_transaction_class; + +/** Class-timer map */ +enum_timer_name *class_timers[] = +{&wait_timer, /* PFS_CLASS_NONE */ + &wait_timer, /* PFS_CLASS_MUTEX */ + &wait_timer, /* PFS_CLASS_RWLOCK */ + &wait_timer, /* PFS_CLASS_COND */ + &wait_timer, /* PFS_CLASS_FILE */ + &wait_timer, /* PFS_CLASS_TABLE */ + &stage_timer, /* PFS_CLASS_STAGE */ + &statement_timer, /* PFS_CLASS_STATEMENT */ + &transaction_timer, /* PFS_CLASS_TRANSACTION */ + &wait_timer, /* PFS_CLASS_SOCKET */ + &wait_timer, /* PFS_CLASS_TABLE_IO */ + &wait_timer, /* PFS_CLASS_TABLE_LOCK */ + &idle_timer, /* PFS_CLASS_IDLE */ + &wait_timer, /* PFS_CLASS_METADATA */ + &wait_timer /* PFS_CLASS_MEMORY */ +}; + +/** + Hash index for instrumented table shares. + This index is searched by table fully qualified name (@c PFS_table_share_key), + and points to instrumented table shares (@c PFS_table_share). + @sa PFS_table_share_key + @sa PFS_table_share + @sa table_share_hash_get_key + @sa get_table_share_hash_pins +*/ +LF_HASH table_share_hash; +/** True if table_share_hash is initialized. */ +static bool table_share_hash_inited= false; + +static uint32 file_class_dirty_count= 0; +static uint32 file_class_allocated_count= 0; + +PFS_file_class *file_class_array= NULL; + +static uint32 stage_class_dirty_count= 0; +static uint32 stage_class_allocated_count= 0; + +static PFS_stage_class *stage_class_array= NULL; + +static uint32 statement_class_dirty_count= 0; +static uint32 statement_class_allocated_count= 0; + +static PFS_statement_class *statement_class_array= NULL; + +static uint32 socket_class_dirty_count= 0; +static uint32 socket_class_allocated_count= 0; + +static PFS_socket_class *socket_class_array= NULL; + +static uint32 memory_class_dirty_count= 0; +static uint32 memory_class_allocated_count= 0; + +static PFS_memory_class *memory_class_array= NULL; + +uint mutex_class_start= 0; +uint rwlock_class_start= 0; +uint cond_class_start= 0; +uint file_class_start= 0; +uint wait_class_max= 0; +uint socket_class_start= 0; + +void init_event_name_sizing(const PFS_global_param *param) +{ + /* global table io, table lock, idle, metadata */ + mutex_class_start= COUNT_GLOBAL_EVENT_INDEX; + rwlock_class_start= mutex_class_start + param->m_mutex_class_sizing; + cond_class_start= rwlock_class_start + param->m_rwlock_class_sizing; + file_class_start= cond_class_start + param->m_cond_class_sizing; + socket_class_start= file_class_start + param->m_file_class_sizing; + wait_class_max= socket_class_start + param->m_socket_class_sizing; +} + +void register_global_classes() +{ + /* Table IO class */ + init_instr_class(&global_table_io_class, "wait/io/table/sql/handler", 25, + 0, PFS_CLASS_TABLE_IO); + global_table_io_class.m_event_name_index= GLOBAL_TABLE_IO_EVENT_INDEX; + configure_instr_class(&global_table_io_class); + + /* Table lock class */ + init_instr_class(&global_table_lock_class, "wait/lock/table/sql/handler", 27, + 0, PFS_CLASS_TABLE_LOCK); + global_table_lock_class.m_event_name_index= GLOBAL_TABLE_LOCK_EVENT_INDEX; + configure_instr_class(&global_table_lock_class); + + /* Idle class */ + init_instr_class(&global_idle_class, "idle", 4, + 0, PFS_CLASS_IDLE); + global_idle_class.m_event_name_index= GLOBAL_IDLE_EVENT_INDEX; + configure_instr_class(&global_idle_class); + + /* Metadata class */ + init_instr_class(&global_metadata_class, "wait/lock/metadata/sql/mdl", 26, + 0, PFS_CLASS_METADATA); + global_metadata_class.m_event_name_index= GLOBAL_METADATA_EVENT_INDEX; + global_metadata_class.m_enabled= false; /* Disabled by default */ + global_metadata_class.m_timed= false; + configure_instr_class(&global_metadata_class); + + /* Transaction class */ + init_instr_class(&global_transaction_class, "transaction", 11, + 0, PFS_CLASS_TRANSACTION); + global_transaction_class.m_event_name_index= GLOBAL_TRANSACTION_INDEX; + global_transaction_class.m_enabled= false; /* Disabled by default */ + global_transaction_class.m_timed= false; + configure_instr_class(&global_transaction_class); + transaction_class_max= 1; /* used for sizing by other event classes */ +} + +/** + Initialize the instrument synch class buffers. + @param mutex_class_sizing max number of mutex class + @param rwlock_class_sizing max number of rwlock class + @param cond_class_sizing max number of condition class + @return 0 on success +*/ +int init_sync_class(uint mutex_class_sizing, + uint rwlock_class_sizing, + uint cond_class_sizing) +{ + mutex_class_dirty_count= mutex_class_allocated_count= 0; + rwlock_class_dirty_count= rwlock_class_allocated_count= 0; + cond_class_dirty_count= cond_class_allocated_count= 0; + mutex_class_max= mutex_class_sizing; + rwlock_class_max= rwlock_class_sizing; + cond_class_max= cond_class_sizing; + mutex_class_lost= rwlock_class_lost= cond_class_lost= 0; + + mutex_class_array= NULL; + rwlock_class_array= NULL; + cond_class_array= NULL; + + if (mutex_class_max > 0) + { + mutex_class_array= PFS_MALLOC_ARRAY(& builtin_memory_mutex_class, + mutex_class_max, + sizeof(PFS_mutex_class), PFS_mutex_class, + MYF(MY_ZEROFILL)); + if (unlikely(mutex_class_array == NULL)) + return 1; + } + + if (rwlock_class_max > 0) + { + rwlock_class_array= PFS_MALLOC_ARRAY(& builtin_memory_rwlock_class, + rwlock_class_max, + sizeof(PFS_rwlock_class), PFS_rwlock_class, + MYF(MY_ZEROFILL)); + if (unlikely(rwlock_class_array == NULL)) + return 1; + } + + if (cond_class_max > 0) + { + cond_class_array= PFS_MALLOC_ARRAY(& builtin_memory_cond_class, + cond_class_max, + sizeof(PFS_cond_class), PFS_cond_class, + MYF(MY_ZEROFILL)); + if (unlikely(cond_class_array == NULL)) + return 1; + } + + return 0; +} + +/** Cleanup the instrument synch class buffers. */ +void cleanup_sync_class(void) +{ + PFS_FREE_ARRAY(& builtin_memory_mutex_class, + mutex_class_max, sizeof(PFS_mutex_class), + mutex_class_array); + mutex_class_array= NULL; + mutex_class_dirty_count= mutex_class_allocated_count= mutex_class_max= 0; + + PFS_FREE_ARRAY(& builtin_memory_rwlock_class, + rwlock_class_max, sizeof(PFS_rwlock_class), + rwlock_class_array); + rwlock_class_array= NULL; + rwlock_class_dirty_count= rwlock_class_allocated_count= rwlock_class_max= 0; + + PFS_FREE_ARRAY(& builtin_memory_cond_class, + cond_class_max, sizeof(PFS_cond_class), + cond_class_array); + cond_class_array= NULL; + cond_class_dirty_count= cond_class_allocated_count= cond_class_max= 0; +} + +/** + Initialize the thread class buffer. + @param thread_class_sizing max number of thread class + @return 0 on success +*/ +int init_thread_class(uint thread_class_sizing) +{ + int result= 0; + thread_class_dirty_count= thread_class_allocated_count= 0; + thread_class_max= thread_class_sizing; + thread_class_lost= 0; + + if (thread_class_max > 0) + { + thread_class_array= PFS_MALLOC_ARRAY(& builtin_memory_thread_class, + thread_class_max, + sizeof(PFS_thread_class), PFS_thread_class, + MYF(MY_ZEROFILL)); + if (unlikely(thread_class_array == NULL)) + result= 1; + } + else + thread_class_array= NULL; + + return result; +} + +/** Cleanup the thread class buffers. */ +void cleanup_thread_class(void) +{ + PFS_FREE_ARRAY(& builtin_memory_thread_class, + thread_class_max, sizeof(PFS_thread_class), + thread_class_array); + thread_class_array= NULL; + thread_class_dirty_count= thread_class_allocated_count= 0; + thread_class_max= 0; +} + +/** + Initialize the table share buffer. + @param table_share_sizing max number of table share + @return 0 on success +*/ +int init_table_share(uint table_share_sizing) +{ + if (global_table_share_container.init(table_share_sizing)) + return 1; + + return 0; +} + +/** Cleanup the table share buffers. */ +void cleanup_table_share(void) +{ + global_table_share_container.cleanup(); +} + +C_MODE_START +/** get_key function for @c table_share_hash. */ +static uchar *table_share_hash_get_key(const uchar *entry, size_t *length, + my_bool) +{ + const PFS_table_share * const *typed_entry; + const PFS_table_share *share; + const void *result; + typed_entry= reinterpret_cast<const PFS_table_share* const *> (entry); + assert(typed_entry != NULL); + share= *typed_entry; + assert(share != NULL); + *length= share->m_key.m_key_length; + result= &share->m_key.m_hash_key[0]; + return const_cast<uchar*> (reinterpret_cast<const uchar*> (result)); +} +C_MODE_END + +/** Initialize the table share hash table. */ +int init_table_share_hash(const PFS_global_param *param) +{ + if ((! table_share_hash_inited) && (param->m_table_share_sizing != 0)) + { + lf_hash_init(&table_share_hash, sizeof(PFS_table_share*), LF_HASH_UNIQUE, + 0, 0, table_share_hash_get_key, &my_charset_bin); + table_share_hash_inited= true; + } + return 0; +} + +/** Cleanup the table share hash table. */ +void cleanup_table_share_hash(void) +{ + if (table_share_hash_inited) + { + lf_hash_destroy(&table_share_hash); + table_share_hash_inited= false; + } +} + +/** + Get the hash pins for @sa table_share_hash. + @param thread The running thread. + @returns The LF_HASH pins for the thread. +*/ +LF_PINS* get_table_share_hash_pins(PFS_thread *thread) +{ + if (unlikely(thread->m_table_share_hash_pins == NULL)) + { + if (! table_share_hash_inited) + return NULL; + thread->m_table_share_hash_pins= lf_hash_get_pins(&table_share_hash); + } + return thread->m_table_share_hash_pins; +} + +/** + Set a table share hash key. + @param [out] key The key to populate. + @param temporary True for TEMPORARY TABLE. + @param schema_name The table schema name. + @param schema_name_length The table schema name length. + @param table_name The table name. + @param table_name_length The table name length. +*/ +static void set_table_share_key(PFS_table_share_key *key, + bool temporary, + const char *schema_name, size_t schema_name_length, + const char *table_name, size_t table_name_length) +{ + assert(schema_name_length <= NAME_LEN); + assert(table_name_length <= NAME_LEN); + char *saved_schema_name; + char *saved_table_name; + + char *ptr= &key->m_hash_key[0]; + ptr[0]= (temporary ? OBJECT_TYPE_TEMPORARY_TABLE : OBJECT_TYPE_TABLE); + ptr++; + saved_schema_name= ptr; + memcpy(ptr, schema_name, schema_name_length); + ptr+= schema_name_length; + ptr[0]= 0; + ptr++; + saved_table_name= ptr; + memcpy(ptr, table_name, table_name_length); + ptr+= table_name_length; + ptr[0]= 0; + ptr++; + key->m_key_length= (uint)(ptr - &key->m_hash_key[0]); + + if (lower_case_table_names) + { + my_casedn_str(files_charset_info, saved_schema_name); + my_casedn_str(files_charset_info, saved_table_name); + } +} + +/** + Find an existing table share lock instrumentation. + @return a table share lock. +*/ +PFS_table_share_lock* +PFS_table_share::find_lock_stat() const +{ + PFS_table_share *that= const_cast<PFS_table_share*>(this); + void *addr= & that->m_race_lock_stat; + void * volatile * typed_addr= static_cast<void * volatile *>(addr); + void *ptr; + + /* Atomic Load */ + ptr= my_atomic_loadptr(typed_addr); + + PFS_table_share_lock *pfs; + pfs= static_cast<PFS_table_share_lock *>(ptr); + return pfs; +} + +/** + Find or create a table share lock instrumentation. + @return a table share lock, or NULL. +*/ +PFS_table_share_lock* +PFS_table_share::find_or_create_lock_stat() +{ + void *addr= & this->m_race_lock_stat; + void * volatile * typed_addr= static_cast<void * volatile *>(addr); + void *ptr; + + /* (1) Atomic Load */ + ptr= my_atomic_loadptr(typed_addr); + + PFS_table_share_lock *pfs; + if (ptr != NULL) + { + pfs= static_cast<PFS_table_share_lock *>(ptr); + return pfs; + } + + /* (2) Create a lock stat */ + pfs= create_table_share_lock_stat(); + if (pfs == NULL) + return NULL; + pfs->m_owner= this; + + void *old_ptr= NULL; + ptr= pfs; + + /* (3) Atomic CAS */ + if (my_atomic_casptr(typed_addr, & old_ptr, ptr)) + { + /* Ok. */ + return pfs; + } + + /* Collision with another thread that also executed (2) and (3). */ + release_table_share_lock_stat(pfs); + + pfs= static_cast<PFS_table_share_lock *>(old_ptr); + return pfs; +} + +/** Destroy a table share lock instrumentation. */ +void PFS_table_share::destroy_lock_stat() +{ + void *addr= & this->m_race_lock_stat; + void * volatile * typed_addr= static_cast<void * volatile *>(addr); + void *new_ptr= NULL; + void *old_ptr; + + old_ptr= my_atomic_fasptr(typed_addr, new_ptr); + if (old_ptr != NULL) + { + PFS_table_share_lock *pfs; + pfs= static_cast<PFS_table_share_lock *>(old_ptr); + release_table_share_lock_stat(pfs); + } +} + +/** + Find an existing table share index instrumentation. + @return a table share index +*/ +PFS_table_share_index* +PFS_table_share::find_index_stat(uint index) const +{ + assert(index <= MAX_INDEXES); + + PFS_table_share *that= const_cast<PFS_table_share*>(this); + void *addr= & that->m_race_index_stat[index]; + void * volatile * typed_addr= static_cast<void * volatile *>(addr); + void *ptr; + + /* Atomic Load */ + ptr= my_atomic_loadptr(typed_addr); + + PFS_table_share_index *pfs; + pfs= static_cast<PFS_table_share_index *>(ptr); + return pfs; +} + +/** + Find or create a table share index instrumentation. + @param server_share + @index index + @return a table share index, or NULL +*/ +PFS_table_share_index* +PFS_table_share::find_or_create_index_stat(const TABLE_SHARE *server_share, uint index) +{ + assert(index <= MAX_INDEXES); + + void *addr= & this->m_race_index_stat[index]; + void * volatile * typed_addr= static_cast<void * volatile *>(addr); + void *ptr; + + /* (1) Atomic Load */ + ptr= my_atomic_loadptr(typed_addr); + + PFS_table_share_index *pfs; + if (ptr != NULL) + { + pfs= static_cast<PFS_table_share_index *>(ptr); + return pfs; + } + + /* (2) Create an index stat */ + pfs= create_table_share_index_stat(server_share, index); + if (pfs == NULL) + return NULL; + pfs->m_owner= this; + + void *old_ptr= NULL; + ptr= pfs; + + /* (3) Atomic CAS */ + if (my_atomic_casptr(typed_addr, & old_ptr, ptr)) + { + /* Ok. */ + return pfs; + } + + /* Collision with another thread that also executed (2) and (3). */ + release_table_share_index_stat(pfs); + + pfs= static_cast<PFS_table_share_index *>(old_ptr); + return pfs; +} + +/** Destroy table share index instrumentation. */ +void PFS_table_share::destroy_index_stats() +{ + uint index; + + for (index= 0; index <= MAX_INDEXES; index++) + { + void *addr= & this->m_race_index_stat[index]; + void * volatile * typed_addr= static_cast<void * volatile *>(addr); + void *new_ptr= NULL; + void *old_ptr; + + old_ptr= my_atomic_fasptr(typed_addr, new_ptr); + if (old_ptr != NULL) + { + PFS_table_share_index *pfs; + pfs= static_cast<PFS_table_share_index *>(old_ptr); + release_table_share_index_stat(pfs); + } + } +} + +void PFS_table_share::refresh_setup_object_flags(PFS_thread *thread) +{ + bool old_enabled= m_enabled; + + lookup_setup_object(thread, + OBJECT_TYPE_TABLE, + m_schema_name, m_schema_name_length, + m_table_name, m_table_name_length, + &m_enabled, &m_timed); + + /* + If instrumentation for this table was enabled earlier and is disabled now, + cleanup slots reserved for lock stats and index stats. + */ + if (old_enabled && ! m_enabled) + { + destroy_lock_stat(); + destroy_index_stats(); + } +} + +/** + Initialize the table lock stat buffer. + @param table_stat_sizing max number of table lock statistics + @return 0 on success +*/ +int init_table_share_lock_stat(uint table_stat_sizing) +{ + if (global_table_share_lock_container.init(table_stat_sizing)) + return 1; + + return 0; +} + +/** + Create a table share lock instrumentation. + @return table share lock instrumentation, or NULL +*/ +PFS_table_share_lock* +create_table_share_lock_stat() +{ + PFS_table_share_lock *pfs= NULL; + pfs_dirty_state dirty_state; + + /* Create a new record in table stat array. */ + pfs= global_table_share_lock_container.allocate(& dirty_state); + if (pfs != NULL) + { + /* Reset the stats. */ + pfs->m_stat.reset(); + + /* Use this stat buffer. */ + pfs->m_lock.dirty_to_allocated(& dirty_state); + } + + return pfs; +} + +/** Release a table share lock instrumentation. */ +void release_table_share_lock_stat(PFS_table_share_lock *pfs) +{ + pfs->m_owner= NULL; + global_table_share_lock_container.deallocate(pfs); + return; +} + +/** Cleanup the table stat buffers. */ +void cleanup_table_share_lock_stat(void) +{ + global_table_share_lock_container.cleanup(); +} + +/** + Initialize table index stat buffer. + @param index_stat_sizing max number of index statistics + @return 0 on success +*/ +int init_table_share_index_stat(uint index_stat_sizing) +{ + if (global_table_share_index_container.init(index_stat_sizing)) + return 1; + + return 0; +} + +/** + Create a table share index instrumentation. + @return table share index instrumentation, or NULL +*/ +PFS_table_share_index* +create_table_share_index_stat(const TABLE_SHARE *server_share, uint server_index) +{ + assert((server_share != NULL) || (server_index == MAX_INDEXES)); + + PFS_table_share_index *pfs= NULL; + pfs_dirty_state dirty_state; + + /* Create a new record in index stat array. */ + pfs= global_table_share_index_container.allocate(& dirty_state); + if (pfs != NULL) + { + if (server_index == MAX_INDEXES) + { + pfs->m_key.m_name_length= 0; + } + else + { + KEY *key_info= server_share->key_info + server_index; + size_t len= key_info->name.length; + + memcpy(pfs->m_key.m_name, key_info->name.str, len); + pfs->m_key.m_name_length= static_cast<uint>(len); + } + + /* Reset the stats. */ + pfs->m_stat.reset(); + + /* Use this stat buffer. */ + pfs->m_lock.dirty_to_allocated(& dirty_state); + } + + return pfs; +} + +/** Release a table share index instrumentation. */ +void release_table_share_index_stat(PFS_table_share_index *pfs) +{ + pfs->m_owner= NULL; + global_table_share_index_container.deallocate(pfs); + return; +} + +/** Cleanup the table stat buffers. */ +void cleanup_table_share_index_stat(void) +{ + global_table_share_index_container.cleanup(); +} + +/** + Initialize the file class buffer. + @param file_class_sizing max number of file class + @return 0 on success +*/ +int init_file_class(uint file_class_sizing) +{ + int result= 0; + file_class_dirty_count= file_class_allocated_count= 0; + file_class_max= file_class_sizing; + file_class_lost= 0; + + if (file_class_max > 0) + { + file_class_array= PFS_MALLOC_ARRAY(& builtin_memory_file_class, + file_class_max, + sizeof(PFS_file_class), PFS_file_class, + MYF(MY_ZEROFILL)); + if (unlikely(file_class_array == NULL)) + return 1; + } + else + file_class_array= NULL; + + return result; +} + +/** Cleanup the file class buffers. */ +void cleanup_file_class(void) +{ + PFS_FREE_ARRAY(& builtin_memory_file_class, + file_class_max, sizeof(PFS_file_class), + file_class_array); + file_class_array= NULL; + file_class_dirty_count= file_class_allocated_count= 0; + file_class_max= 0; +} + +/** + Initialize the stage class buffer. + @param stage_class_sizing max number of stage class + @return 0 on success +*/ +int init_stage_class(uint stage_class_sizing) +{ + int result= 0; + stage_class_dirty_count= stage_class_allocated_count= 0; + stage_class_max= stage_class_sizing; + stage_class_lost= 0; + + if (stage_class_max > 0) + { + stage_class_array= PFS_MALLOC_ARRAY(& builtin_memory_stage_class, + stage_class_max, + sizeof(PFS_stage_class), PFS_stage_class, + MYF(MY_ZEROFILL)); + if (unlikely(stage_class_array == NULL)) + return 1; + } + else + stage_class_array= NULL; + + return result; +} + +/** Cleanup the stage class buffers. */ +void cleanup_stage_class(void) +{ + PFS_FREE_ARRAY(& builtin_memory_stage_class, + stage_class_max, sizeof(PFS_stage_class), + stage_class_array); + stage_class_array= NULL; + stage_class_dirty_count= stage_class_allocated_count= 0; + stage_class_max= 0; +} + +/** + Initialize the statement class buffer. + @param statement_class_sizing max number of statement class + @return 0 on success +*/ +int init_statement_class(uint statement_class_sizing) +{ + int result= 0; + statement_class_dirty_count= statement_class_allocated_count= 0; + statement_class_max= statement_class_sizing; + statement_class_lost= 0; + + if (statement_class_max > 0) + { + statement_class_array= PFS_MALLOC_ARRAY(& builtin_memory_statement_class, + statement_class_max, + sizeof(PFS_statement_class), PFS_statement_class, + MYF(MY_ZEROFILL)); + if (unlikely(statement_class_array == NULL)) + return 1; + } + else + statement_class_array= NULL; + + return result; +} + +/** Cleanup the statement class buffers. */ +void cleanup_statement_class(void) +{ + PFS_FREE_ARRAY(& builtin_memory_statement_class, + statement_class_max, sizeof(PFS_statement_class), + statement_class_array); + statement_class_array= NULL; + statement_class_dirty_count= statement_class_allocated_count= 0; + statement_class_max= 0; +} + +/** + Initialize the socket class buffer. + @param socket_class_sizing max number of socket class + @return 0 on success +*/ +int init_socket_class(uint socket_class_sizing) +{ + int result= 0; + socket_class_dirty_count= socket_class_allocated_count= 0; + socket_class_max= socket_class_sizing; + socket_class_lost= 0; + + if (socket_class_max > 0) + { + socket_class_array= PFS_MALLOC_ARRAY(& builtin_memory_socket_class, + socket_class_max, + sizeof(PFS_socket_class), PFS_socket_class, + MYF(MY_ZEROFILL)); + if (unlikely(socket_class_array == NULL)) + return 1; + } + else + socket_class_array= NULL; + + return result; +} + +/** Cleanup the socket class buffers. */ +void cleanup_socket_class(void) +{ + PFS_FREE_ARRAY(& builtin_memory_socket_class, + socket_class_max, sizeof(PFS_socket_class), + socket_class_array); + socket_class_array= NULL; + socket_class_dirty_count= socket_class_allocated_count= 0; + socket_class_max= 0; +} + +/** + Initialize the memory class buffer. + @param memory_class_sizing max number of memory class + @return 0 on success +*/ +int init_memory_class(uint memory_class_sizing) +{ + int result= 0; + memory_class_dirty_count= memory_class_allocated_count= 0; + memory_class_max= memory_class_sizing; + memory_class_lost= 0; + + if (memory_class_max > 0) + { + memory_class_array= PFS_MALLOC_ARRAY(& builtin_memory_memory_class, + memory_class_max, + sizeof(PFS_memory_class), PFS_memory_class, + MYF(MY_ZEROFILL)); + if (unlikely(memory_class_array == NULL)) + return 1; + } + else + memory_class_array= NULL; + + return result; +} + +/** Cleanup the memory class buffers. */ +void cleanup_memory_class(void) +{ + PFS_FREE_ARRAY(& builtin_memory_memory_class, + memory_class_max, sizeof(PFS_memory_class), + memory_class_array); + memory_class_array= NULL; + memory_class_dirty_count= memory_class_allocated_count= 0; + memory_class_max= 0; +} + +static void init_instr_class(PFS_instr_class *klass, + const char *name, + uint name_length, + int flags, + PFS_class_type class_type) +{ + assert(name_length <= PFS_MAX_INFO_NAME_LENGTH); + memset(klass, 0, sizeof(PFS_instr_class)); + strncpy(klass->m_name, name, name_length); + klass->m_name[PFS_MAX_INFO_NAME_LENGTH - 1]= '\0'; + klass->m_name_length= name_length; + klass->m_flags= flags; + klass->m_enabled= true; + klass->m_timed= true; + klass->m_type= class_type; + klass->m_timer= class_timers[class_type]; +} + +/** + Set user-defined configuration values for an instrument. +*/ +static void configure_instr_class(PFS_instr_class *entry) +{ + uint match_length= 0; /* length of matching pattern */ + + // May be NULL in unit tests + if (pfs_instr_config_array == NULL) + return; + PFS_instr_config **it= pfs_instr_config_array->front(); + for ( ; it != pfs_instr_config_array->end(); it++) + { + PFS_instr_config* e= *it; + + /** + Compare class name to all configuration entries. In case of multiple + matches, the longer specification wins. For example, the pattern + 'ABC/DEF/GHI=ON' has precedence over 'ABC/DEF/%=OFF' regardless of + position within the configuration file or command line. + + Consecutive wildcards affect the count. + */ + if (!my_charset_latin1.wildcmp( + entry->m_name, entry->m_name+entry->m_name_length, + e->m_name, e->m_name+e->m_name_length, + '\\', '?','%')) + { + if (e->m_name_length >= match_length) + { + entry->m_enabled= e->m_enabled; + entry->m_timed= e->m_timed; + match_length= MY_MAX(e->m_name_length, match_length); + } + } + } +} + +#define REGISTER_CLASS_BODY_PART(INDEX, ARRAY, MAX, NAME, NAME_LENGTH) \ + for (INDEX= 0; INDEX < MAX; INDEX++) \ + { \ + entry= &ARRAY[INDEX]; \ + if ((entry->m_name_length == NAME_LENGTH) && \ + (strncmp(entry->m_name, NAME, NAME_LENGTH) == 0)) \ + { \ + assert(entry->m_flags == flags); \ + return (INDEX + 1); \ + } \ + } + +/** + Register a mutex instrumentation metadata. + @param name the instrumented name + @param name_length length in bytes of name + @param flags the instrumentation flags + @return a mutex instrumentation key +*/ +PFS_sync_key register_mutex_class(const char *name, uint name_length, + int flags) +{ + uint32 index; + PFS_mutex_class *entry; + + /* + This is a full array scan, which is not optimal. + This is acceptable since this code is only used at startup, + or when a plugin is loaded. + */ + REGISTER_CLASS_BODY_PART(index, mutex_class_array, mutex_class_max, + name, name_length) + /* + Note that: + mutex_class_dirty_count is incremented *before* an entry is added + mutex_class_allocated_count is incremented *after* an entry is added + */ + index= PFS_atomic::add_u32(&mutex_class_dirty_count, 1); + + if (index < mutex_class_max) + { + /* + The instrument was not found (from a possible previous + load / unload of a plugin), allocate it. + This code is safe when 2 threads execute in parallel + for different mutex classes: + - thread 1 registering class A + - thread 2 registering class B + will not collide in the same mutex_class_array[index] entry. + This code does not protect against 2 threads registering + in parallel the same class: + - thread 1 registering class A + - thread 2 registering class A + could lead to a duplicate class A entry. + This is ok, since this case can not happen in the caller: + - classes names are derived from a plugin name + ('wait/synch/mutex/<plugin>/xxx') + - 2 threads can not register concurrently the same plugin + in INSTALL PLUGIN. + */ + entry= &mutex_class_array[index]; + init_instr_class(entry, name, name_length, flags, PFS_CLASS_MUTEX); + entry->m_mutex_stat.reset(); + entry->m_event_name_index= mutex_class_start + index; + entry->m_singleton= NULL; + entry->m_enabled= false; /* disabled by default */ + entry->m_timed= false; + + /* Set user-defined configuration options for this instrument */ + configure_instr_class(entry); + + /* + Now that this entry is populated, advertise it + + Technically, there is a small race condition here: + T0: + mutex_class_dirty_count= 10 + mutex_class_allocated_count= 10 + T1: Thread A increment mutex_class_dirty_count to 11 + T2: Thread B increment mutex_class_dirty_count to 12 + T3: Thread A populate entry 11 + T4: Thread B populate entry 12 + T5: Thread B increment mutex_class_allocated_count to 11, + advertise thread A incomplete record 11, + but does not advertise thread B complete record 12 + T6: Thread A increment mutex_class_allocated_count to 12 + This has no impact, and is acceptable. + A reader will not see record 12 for a short time. + A reader will see an incomplete record 11 for a short time, + which is ok: the mutex name / statistics will be temporarily + empty/NULL/zero, but this won't cause a crash + (mutex_class_array is initialized with MY_ZEROFILL). + */ + PFS_atomic::add_u32(&mutex_class_allocated_count, 1); + return (index + 1); + } + + /* + Out of space, report to SHOW STATUS that + the allocated memory was too small. + */ + if (pfs_enabled) + mutex_class_lost++; + return 0; +} + +/** + Register a rwlock instrumentation metadata. + @param name the instrumented name + @param name_length length in bytes of name + @param flags the instrumentation flags + @return a rwlock instrumentation key +*/ +PFS_sync_key register_rwlock_class(const char *name, uint name_length, + int flags) +{ + /* See comments in register_mutex_class */ + uint32 index; + PFS_rwlock_class *entry; + + REGISTER_CLASS_BODY_PART(index, rwlock_class_array, rwlock_class_max, + name, name_length) + + index= PFS_atomic::add_u32(&rwlock_class_dirty_count, 1); + + if (index < rwlock_class_max) + { + entry= &rwlock_class_array[index]; + init_instr_class(entry, name, name_length, flags, PFS_CLASS_RWLOCK); + entry->m_rwlock_stat.reset(); + entry->m_event_name_index= rwlock_class_start + index; + entry->m_singleton= NULL; + entry->m_enabled= false; /* disabled by default */ + entry->m_timed= false; + /* Set user-defined configuration options for this instrument */ + configure_instr_class(entry); + PFS_atomic::add_u32(&rwlock_class_allocated_count, 1); + return (index + 1); + } + + if (pfs_enabled) + rwlock_class_lost++; + return 0; +} + +/** + Register a condition instrumentation metadata. + @param name the instrumented name + @param name_length length in bytes of name + @param flags the instrumentation flags + @return a condition instrumentation key +*/ +PFS_sync_key register_cond_class(const char *name, uint name_length, + int flags) +{ + /* See comments in register_mutex_class */ + uint32 index; + PFS_cond_class *entry; + + REGISTER_CLASS_BODY_PART(index, cond_class_array, cond_class_max, + name, name_length) + + index= PFS_atomic::add_u32(&cond_class_dirty_count, 1); + + if (index < cond_class_max) + { + entry= &cond_class_array[index]; + init_instr_class(entry, name, name_length, flags, PFS_CLASS_COND); + entry->m_event_name_index= cond_class_start + index; + entry->m_singleton= NULL; + entry->m_enabled= false; /* disabled by default */ + entry->m_timed= false; + /* Set user-defined configuration options for this instrument */ + configure_instr_class(entry); + PFS_atomic::add_u32(&cond_class_allocated_count, 1); + return (index + 1); + } + + if (pfs_enabled) + cond_class_lost++; + return 0; +} + +#define FIND_CLASS_BODY(KEY, COUNT, ARRAY) \ + if ((KEY == 0) || (KEY > COUNT)) \ + return NULL; \ + return &ARRAY[KEY - 1] + +/** + Find a mutex instrumentation class by key. + @param key the instrument key + @return the instrument class, or NULL +*/ +PFS_mutex_class *find_mutex_class(PFS_sync_key key) +{ + FIND_CLASS_BODY(key, mutex_class_allocated_count, mutex_class_array); +} + +PFS_mutex_class *sanitize_mutex_class(PFS_mutex_class *unsafe) +{ + SANITIZE_ARRAY_BODY(PFS_mutex_class, mutex_class_array, mutex_class_max, unsafe); +} + +/** + Find a rwlock instrumentation class by key. + @param key the instrument key + @return the instrument class, or NULL +*/ +PFS_rwlock_class *find_rwlock_class(PFS_sync_key key) +{ + FIND_CLASS_BODY(key, rwlock_class_allocated_count, rwlock_class_array); +} + +PFS_rwlock_class *sanitize_rwlock_class(PFS_rwlock_class *unsafe) +{ + SANITIZE_ARRAY_BODY(PFS_rwlock_class, rwlock_class_array, rwlock_class_max, unsafe); +} + +/** + Find a condition instrumentation class by key. + @param key the instrument key + @return the instrument class, or NULL +*/ +PFS_cond_class *find_cond_class(PFS_sync_key key) +{ + FIND_CLASS_BODY(key, cond_class_allocated_count, cond_class_array); +} + +PFS_cond_class *sanitize_cond_class(PFS_cond_class *unsafe) +{ + SANITIZE_ARRAY_BODY(PFS_cond_class, cond_class_array, cond_class_max, unsafe); +} + +/** + Register a thread instrumentation metadata. + @param name the instrumented name + @param name_length length in bytes of name + @param flags the instrumentation flags + @return a thread instrumentation key +*/ +PFS_thread_key register_thread_class(const char *name, uint name_length, + int flags) +{ + /* See comments in register_mutex_class */ + uint32 index; + PFS_thread_class *entry; + + for (index= 0; index < thread_class_max; index++) + { + entry= &thread_class_array[index]; + + if ((entry->m_name_length == name_length) && + (strncmp(entry->m_name, name, name_length) == 0)) + return (index + 1); + } + + index= PFS_atomic::add_u32(&thread_class_dirty_count, 1); + + if (index < thread_class_max) + { + entry= &thread_class_array[index]; + assert(name_length <= PFS_MAX_INFO_NAME_LENGTH); + strncpy(entry->m_name, name, name_length); + entry->m_name_length= name_length; + entry->m_enabled= true; + PFS_atomic::add_u32(&thread_class_allocated_count, 1); + return (index + 1); + } + + if (pfs_enabled) + thread_class_lost++; + return 0; +} + +/** + Find a thread instrumentation class by key. + @param key the instrument key + @return the instrument class, or NULL +*/ +PFS_thread_class *find_thread_class(PFS_sync_key key) +{ + FIND_CLASS_BODY(key, thread_class_allocated_count, thread_class_array); +} + +PFS_thread_class *sanitize_thread_class(PFS_thread_class *unsafe) +{ + SANITIZE_ARRAY_BODY(PFS_thread_class, thread_class_array, thread_class_max, unsafe); +} + +/** + Register a file instrumentation metadata. + @param name the instrumented name + @param name_length length in bytes of name + @param flags the instrumentation flags + @return a file instrumentation key +*/ +PFS_file_key register_file_class(const char *name, uint name_length, + int flags) +{ + /* See comments in register_mutex_class */ + uint32 index; + PFS_file_class *entry; + + REGISTER_CLASS_BODY_PART(index, file_class_array, file_class_max, + name, name_length) + + index= PFS_atomic::add_u32(&file_class_dirty_count, 1); + + if (index < file_class_max) + { + entry= &file_class_array[index]; + init_instr_class(entry, name, name_length, flags, PFS_CLASS_FILE); + entry->m_event_name_index= file_class_start + index; + entry->m_singleton= NULL; + entry->m_enabled= true; /* enabled by default */ + entry->m_timed= true; + /* Set user-defined configuration options for this instrument */ + configure_instr_class(entry); + PFS_atomic::add_u32(&file_class_allocated_count, 1); + + return (index + 1); + } + + if (pfs_enabled) + file_class_lost++; + return 0; +} + +/** + Register a stage instrumentation metadata. + @param name the instrumented name + @param prefix_length length in bytes of the name prefix + @param name_length length in bytes of name + @param flags the instrumentation flags + @return a stage instrumentation key +*/ +PFS_stage_key register_stage_class(const char *name, + uint prefix_length, + uint name_length, + int flags) +{ + /* See comments in register_mutex_class */ + uint32 index; + PFS_stage_class *entry; + + REGISTER_CLASS_BODY_PART(index, stage_class_array, stage_class_max, + name, name_length) + + index= PFS_atomic::add_u32(&stage_class_dirty_count, 1); + + if (index < stage_class_max) + { + entry= &stage_class_array[index]; + init_instr_class(entry, name, name_length, flags, PFS_CLASS_STAGE); + entry->m_prefix_length= prefix_length; + entry->m_event_name_index= index; + + if (flags & PSI_FLAG_STAGE_PROGRESS) + { + /* Stages with progress information are enabled and timed by default */ + entry->m_enabled= true; + entry->m_timed= true; + } + else + { + /* Stages without progress information are disabled by default */ + entry->m_enabled= false; + entry->m_timed= false; + } + + /* Set user-defined configuration options for this instrument */ + configure_instr_class(entry); + PFS_atomic::add_u32(&stage_class_allocated_count, 1); + + return (index + 1); + } + + if (pfs_enabled) + stage_class_lost++; + return 0; +} + +/** + Register a statement instrumentation metadata. + @param name the instrumented name + @param name_length length in bytes of name + @param flags the instrumentation flags + @return a statement instrumentation key +*/ +PFS_statement_key register_statement_class(const char *name, uint name_length, + int flags) +{ + /* See comments in register_mutex_class */ + uint32 index; + PFS_statement_class *entry; + + REGISTER_CLASS_BODY_PART(index, statement_class_array, statement_class_max, + name, name_length) + + index= PFS_atomic::add_u32(&statement_class_dirty_count, 1); + + if (index < statement_class_max) + { + entry= &statement_class_array[index]; + init_instr_class(entry, name, name_length, flags, PFS_CLASS_STATEMENT); + entry->m_event_name_index= index; + entry->m_enabled= true; /* enabled by default */ + entry->m_timed= true; + /* Set user-defined configuration options for this instrument */ + configure_instr_class(entry); + PFS_atomic::add_u32(&statement_class_allocated_count, 1); + + return (index + 1); + } + + if (pfs_enabled) + statement_class_lost++; + return 0; +} + +/** + Find a file instrumentation class by key. + @param key the instrument key + @return the instrument class, or NULL +*/ +PFS_file_class *find_file_class(PFS_file_key key) +{ + FIND_CLASS_BODY(key, file_class_allocated_count, file_class_array); +} + +PFS_file_class *sanitize_file_class(PFS_file_class *unsafe) +{ + SANITIZE_ARRAY_BODY(PFS_file_class, file_class_array, file_class_max, unsafe); +} + +/** + Find a stage instrumentation class by key. + @param key the instrument key + @return the instrument class, or NULL +*/ +PFS_stage_class *find_stage_class(PFS_stage_key key) +{ + FIND_CLASS_BODY(key, stage_class_allocated_count, stage_class_array); +} + +PFS_stage_class *sanitize_stage_class(PFS_stage_class *unsafe) +{ + SANITIZE_ARRAY_BODY(PFS_stage_class, stage_class_array, stage_class_max, unsafe); +} + +/** + Find a statement instrumentation class by key. + @param key the instrument key + @return the instrument class, or NULL +*/ +PFS_statement_class *find_statement_class(PFS_stage_key key) +{ + FIND_CLASS_BODY(key, statement_class_allocated_count, statement_class_array); +} + +PFS_statement_class *sanitize_statement_class(PFS_statement_class *unsafe) +{ + SANITIZE_ARRAY_BODY(PFS_statement_class, statement_class_array, statement_class_max, unsafe); +} + +/** + Register a socket instrumentation metadata. + @param name the instrumented name + @param name_length length in bytes of name + @param flags the instrumentation flags + @return a socket instrumentation key +*/ +PFS_socket_key register_socket_class(const char *name, uint name_length, + int flags) +{ + /* See comments in register_mutex_class */ + uint32 index; + PFS_socket_class *entry; + + REGISTER_CLASS_BODY_PART(index, socket_class_array, socket_class_max, + name, name_length) + + index= PFS_atomic::add_u32(&socket_class_dirty_count, 1); + + if (index < socket_class_max) + { + entry= &socket_class_array[index]; + init_instr_class(entry, name, name_length, flags, PFS_CLASS_SOCKET); + entry->m_event_name_index= socket_class_start + index; + entry->m_singleton= NULL; + entry->m_enabled= false; /* disabled by default */ + entry->m_timed= false; + /* Set user-defined configuration options for this instrument */ + configure_instr_class(entry); + PFS_atomic::add_u32(&socket_class_allocated_count, 1); + return (index + 1); + } + + if (pfs_enabled) + socket_class_lost++; + return 0; +} + +/** + Find a socket instrumentation class by key. + @param key the instrument key + @return the instrument class, or NULL +*/ +PFS_socket_class *find_socket_class(PFS_socket_key key) +{ + FIND_CLASS_BODY(key, socket_class_allocated_count, socket_class_array); +} + +PFS_socket_class *sanitize_socket_class(PFS_socket_class *unsafe) +{ + SANITIZE_ARRAY_BODY(PFS_socket_class, socket_class_array, socket_class_max, unsafe); +} + +/** + Register a memory instrumentation metadata. + @param name the instrumented name + @param name_length length in bytes of name + @param flags the instrumentation flags + @return a memory instrumentation key +*/ +PFS_memory_key register_memory_class(const char *name, uint name_length, + int flags) +{ + /* See comments in register_mutex_class */ + uint32 index; + PFS_memory_class *entry; + + REGISTER_CLASS_BODY_PART(index, memory_class_array, memory_class_max, + name, name_length) + + index= PFS_atomic::add_u32(&memory_class_dirty_count, 1); + + if (index < memory_class_max) + { + entry= &memory_class_array[index]; + init_instr_class(entry, name, name_length, flags, PFS_CLASS_MEMORY); + entry->m_event_name_index= index; + entry->m_enabled= false; /* disabled by default */ + /* Set user-defined configuration options for this instrument */ + configure_instr_class(entry); + entry->m_timed= false; /* Immutable */ + PFS_atomic::add_u32(&memory_class_allocated_count, 1); + return (index + 1); + } + + if (pfs_enabled) + memory_class_lost++; + return 0; +} + +/** + Find a memory instrumentation class by key. + @param key the instrument key + @return the instrument class, or NULL +*/ +PFS_memory_class *find_memory_class(PFS_memory_key key) +{ + FIND_CLASS_BODY(key, memory_class_allocated_count, memory_class_array); +} + +PFS_memory_class *sanitize_memory_class(PFS_memory_class *unsafe) +{ + SANITIZE_ARRAY_BODY(PFS_memory_class, memory_class_array, memory_class_max, unsafe); +} + +PFS_instr_class *find_table_class(uint index) +{ + if (index == 1) + return & global_table_io_class; + if (index == 2) + return & global_table_lock_class; + return NULL; +} + +PFS_instr_class *sanitize_table_class(PFS_instr_class *unsafe) +{ + if (likely((& global_table_io_class == unsafe) || + (& global_table_lock_class == unsafe))) + return unsafe; + return NULL; +} + +PFS_instr_class *find_idle_class(uint index) +{ + if (index == 1) + return & global_idle_class; + return NULL; +} + +PFS_instr_class *sanitize_idle_class(PFS_instr_class *unsafe) +{ + if (likely(& global_idle_class == unsafe)) + return unsafe; + return NULL; +} + +PFS_instr_class *find_metadata_class(uint index) +{ + if (index == 1) + return & global_metadata_class; + return NULL; +} + +PFS_instr_class *sanitize_metadata_class(PFS_instr_class *unsafe) +{ + if (likely(& global_metadata_class == unsafe)) + return unsafe; + return NULL; +} + +PFS_transaction_class *find_transaction_class(uint index) +{ + if (index == 1) + return &global_transaction_class; + return NULL; +} + +PFS_transaction_class *sanitize_transaction_class(PFS_transaction_class *unsafe) +{ + if (likely(&global_transaction_class == unsafe)) + return unsafe; + return NULL; +} + +static int compare_keys(PFS_table_share *pfs, const TABLE_SHARE *share) +{ + if (pfs->m_key_count != share->keys) + return 1; + + size_t len; + uint index= 0; + uint key_count= share->keys; + KEY *key_info= share->key_info; + PFS_table_share_index *index_stat; + + for ( ; index < key_count; key_info++, index++) + { + index_stat= pfs->find_index_stat(index); + if (index_stat != NULL) + { + len= key_info->name.length; + + if (len != index_stat->m_key.m_name_length) + return 1; + + if (memcmp(index_stat->m_key.m_name, key_info->name.str, len) != 0) + return 1; + } + } + + return 0; +} + + +/** + Find or create a table share instrumentation. + @param thread the executing instrumented thread + @param temporary true for TEMPORARY TABLE + @param share table share + @return a table share, or NULL +*/ +PFS_table_share* find_or_create_table_share(PFS_thread *thread, + bool temporary, + const TABLE_SHARE *share) +{ + /* See comments in register_mutex_class */ + PFS_table_share_key key; + + LF_PINS *pins= get_table_share_hash_pins(thread); + if (unlikely(pins == NULL)) + { + global_table_share_container.m_lost++; + return NULL; + } + + const char *schema_name= share->db.str; + size_t schema_name_length= share->db.length; + const char *table_name= share->table_name.str; + size_t table_name_length= share->table_name.length; + + set_table_share_key(&key, temporary, + schema_name, schema_name_length, + table_name, table_name_length); + + PFS_table_share **entry; + uint retry_count= 0; + const uint retry_max= 3; + bool enabled= true; + bool timed= true; + PFS_table_share *pfs; + pfs_dirty_state dirty_state; + +search: + entry= reinterpret_cast<PFS_table_share**> + (lf_hash_search(&table_share_hash, pins, + key.m_hash_key, key.m_key_length)); + if (entry && (entry != MY_ERRPTR)) + { + pfs= *entry; + pfs->inc_refcount() ; + if (compare_keys(pfs, share) != 0) + { + /* + Some DDL was detected. + - keep the lock stats, they are unaffected + - destroy the index stats, indexes changed. + - adjust the expected key count + - recreate index stats + */ + pfs->destroy_index_stats(); + pfs->m_key_count= share->keys; + for (uint index= 0; index < pfs->m_key_count; index++) + { + (void)pfs->find_or_create_index_stat(share, index); + } + } + lf_hash_search_unpin(pins); + return pfs; + } + + lf_hash_search_unpin(pins); + + if (retry_count == 0) + { + lookup_setup_object(thread, + OBJECT_TYPE_TABLE, + schema_name, static_cast<uint>(schema_name_length), + table_name, static_cast<uint>(table_name_length), + &enabled, &timed); + /* + Even when enabled is false, a record is added in the dictionary: + - It makes enabling a table already in the table cache possible, + - It improves performances for the next time a TABLE_SHARE is reloaded + in the table cache. + */ + } + + pfs= global_table_share_container.allocate(& dirty_state); + if (pfs != NULL) + { + pfs->m_key= key; + pfs->m_schema_name= &pfs->m_key.m_hash_key[1]; + pfs->m_schema_name_length= static_cast<uint>(schema_name_length); + pfs->m_table_name= &pfs->m_key.m_hash_key[schema_name_length + 2]; + pfs->m_table_name_length= static_cast<uint>(table_name_length); + pfs->m_enabled= enabled; + pfs->m_timed= timed; + pfs->init_refcount(); + pfs->destroy_lock_stat(); + pfs->destroy_index_stats(); + pfs->m_key_count= share->keys; + + int res; + pfs->m_lock.dirty_to_allocated(& dirty_state); + res= lf_hash_insert(&table_share_hash, pins, &pfs); + + if (likely(res == 0)) + { + /* Create table share index stats. */ + for (uint index= 0; index < pfs->m_key_count; index++) + { + (void)pfs->find_or_create_index_stat(share, index); + } + return pfs; + } + + global_table_share_container.deallocate(pfs); + + if (res > 0) + { + /* Duplicate insert by another thread */ + if (++retry_count > retry_max) + { + /* Avoid infinite loops */ + global_table_share_container.m_lost++; + return NULL; + } + goto search; + } + + /* OOM in lf_hash_insert */ + global_table_share_container.m_lost++; + return NULL; + } + + return NULL; +} + +void PFS_table_share::aggregate_io(void) +{ + uint index; + uint safe_key_count= sanitize_index_count(m_key_count); + PFS_table_share_index *from_stat; + PFS_table_io_stat sum_io; + + /* Aggregate stats for each index, if any */ + for (index= 0; index < safe_key_count; index++) + { + from_stat= find_index_stat(index); + if (from_stat != NULL) + { + sum_io.aggregate(& from_stat->m_stat); + from_stat->m_stat.reset(); + } + } + + /* Aggregate stats for the table */ + from_stat= find_index_stat(MAX_INDEXES); + if (from_stat != NULL) + { + sum_io.aggregate(& from_stat->m_stat); + from_stat->m_stat.reset(); + } + + /* Add this table stats to the global sink. */ + global_table_io_stat.aggregate(& sum_io); +} + +void PFS_table_share::sum_io(PFS_single_stat *result, uint key_count) +{ + uint index; + PFS_table_share_index *stat; + + assert(key_count <= MAX_INDEXES); + + /* Sum stats for each index, if any */ + for (index= 0; index < key_count; index++) + { + stat= find_index_stat(index); + if (stat != NULL) + { + stat->m_stat.sum(result); + } + } + + /* Sum stats for the table */ + stat= find_index_stat(MAX_INDEXES); + if (stat != NULL) + { + stat->m_stat.sum(result); + } +} + +void PFS_table_share::sum_lock(PFS_single_stat *result) +{ + PFS_table_share_lock *lock_stat; + lock_stat= find_lock_stat(); + if (lock_stat != NULL) + lock_stat->m_stat.sum(result); +} + +void PFS_table_share::sum(PFS_single_stat *result, uint key_count) +{ + sum_io(result, key_count); + sum_lock(result); +} + +void PFS_table_share::aggregate_lock(void) +{ + PFS_table_share_lock *lock_stat; + lock_stat= find_lock_stat(); + if (lock_stat != NULL) + { + global_table_lock_stat.aggregate(& lock_stat->m_stat); + /* Reset lock stat. */ + lock_stat->m_stat.reset(); + } +} + +void release_table_share(PFS_table_share *pfs) +{ + assert(pfs->get_refcount() > 0); + pfs->dec_refcount(); +} + +/** + Drop the instrumented table share associated with a table. + @param thread The running thread + @param temporary True for TEMPORARY TABLE + @param schema_name The table schema name + @param schema_name_length The table schema name length + @param table_name The table name + @param table_name_length The table name length +*/ +void drop_table_share(PFS_thread *thread, + bool temporary, + const char *schema_name, uint schema_name_length, + const char *table_name, uint table_name_length) +{ + PFS_table_share_key key; + LF_PINS* pins= get_table_share_hash_pins(thread); + if (unlikely(pins == NULL)) + return; + set_table_share_key(&key, temporary, schema_name, schema_name_length, + table_name, table_name_length); + PFS_table_share **entry; + entry= reinterpret_cast<PFS_table_share**> + (lf_hash_search(&table_share_hash, pins, + key.m_hash_key, key.m_key_length)); + if (entry && (entry != MY_ERRPTR)) + { + PFS_table_share *pfs= *entry; + lf_hash_delete(&table_share_hash, pins, + pfs->m_key.m_hash_key, pfs->m_key.m_key_length); + pfs->destroy_lock_stat(); + pfs->destroy_index_stats(); + + pfs->m_lock.allocated_to_free(); + } + + lf_hash_search_unpin(pins); +} + +/** + Sanitize an unsafe table_share pointer. + @param unsafe The possibly corrupt pointer. + @return A valid table_safe_pointer, or NULL. +*/ +PFS_table_share *sanitize_table_share(PFS_table_share *unsafe) +{ + return global_table_share_container.sanitize(unsafe); +} + +/** Reset the wait statistics per instrument class. */ +void reset_events_waits_by_class() +{ + reset_file_class_io(); + reset_socket_class_io(); + global_idle_stat.reset(); + global_table_io_stat.reset(); + global_table_lock_stat.reset(); + global_metadata_stat.reset(); +} + +/** Reset the io statistics per file class. */ +void reset_file_class_io(void) +{ + PFS_file_class *pfs= file_class_array; + PFS_file_class *pfs_last= file_class_array + file_class_max; + + for ( ; pfs < pfs_last; pfs++) + pfs->m_file_stat.m_io_stat.reset(); +} + +/** Reset the io statistics per socket class. */ +void reset_socket_class_io(void) +{ + PFS_socket_class *pfs= socket_class_array; + PFS_socket_class *pfs_last= socket_class_array + socket_class_max; + + for ( ; pfs < pfs_last; pfs++) + pfs->m_socket_stat.m_io_stat.reset(); +} + +class Proc_table_share_derived_flags + : public PFS_buffer_processor<PFS_table_share> +{ +public: + Proc_table_share_derived_flags(PFS_thread *thread) + : m_thread(thread) + {} + + virtual void operator()(PFS_table_share *pfs) + { + pfs->refresh_setup_object_flags(m_thread); + } + +private: + PFS_thread* m_thread; +}; + +void update_table_share_derived_flags(PFS_thread *thread) +{ + Proc_table_share_derived_flags proc(thread); + global_table_share_container.apply(proc); +} + +class Proc_program_share_derived_flags + : public PFS_buffer_processor<PFS_program> +{ +public: + Proc_program_share_derived_flags(PFS_thread *thread) + : m_thread(thread) + {} + + virtual void operator()(PFS_program *pfs) + { + pfs->refresh_setup_object_flags(m_thread); + } + +private: + PFS_thread* m_thread; +}; + +void update_program_share_derived_flags(PFS_thread *thread) +{ + Proc_program_share_derived_flags proc(thread); + global_program_container.apply(proc); +} + +/** @} */ + diff --git a/storage/perfschema/pfs_instr_class.h b/storage/perfschema/pfs_instr_class.h new file mode 100644 index 00000000..f353c410 --- /dev/null +++ b/storage/perfschema/pfs_instr_class.h @@ -0,0 +1,649 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_INSTR_CLASS_H +#define PFS_INSTR_CLASS_H + +#include "my_global.h" +#include "mysql_com.h" /* NAME_LEN */ +#include "lf.h" +#include "pfs_global.h" +#include "pfs_atomic.h" +#include "sql_array.h" + +/** + @file storage/perfschema/pfs_instr_class.h + Performance schema instruments meta data (declarations). +*/ + +/** + Maximum length of an instrument name. + For example, 'wait/sync/mutex/sql/LOCK_open' is an instrument name. +*/ +#define PFS_MAX_INFO_NAME_LENGTH 128 + +/** + Maximum length of the 'full' prefix of an instrument name. + For example, for the instrument name 'wait/sync/mutex/sql/LOCK_open', + the full prefix is 'wait/sync/mutex/sql/', which in turn derives from + a prefix 'wait/sync/mutex' for mutexes, and a category of 'sql' for mutexes + of the sql layer in the server. +*/ +#define PFS_MAX_FULL_PREFIX_NAME_LENGTH 32 + +#include <my_sys.h> +#include <mysql/psi/psi.h> +#include "pfs_lock.h" +#include "pfs_stat.h" +#include "pfs_column_types.h" + +struct PFS_global_param; +struct PFS_table_share; +class PFS_opaque_container_page; + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +extern my_bool pfs_enabled; +extern enum_timer_name *class_timers[]; + +/** Key, naming a synch instrument (mutex, rwlock, cond). */ +typedef unsigned int PFS_sync_key; +/** Key, naming a thread instrument. */ +typedef unsigned int PFS_thread_key; +/** Key, naming a file instrument. */ +typedef unsigned int PFS_file_key; +/** Key, naming a stage instrument. */ +typedef unsigned int PFS_stage_key; +/** Key, naming a statement instrument. */ +typedef unsigned int PFS_statement_key; +/** Key, naming a transaction instrument. */ +typedef unsigned int PFS_transaction_key; +/** Key, naming a socket instrument. */ +typedef unsigned int PFS_socket_key; +/** Key, naming a memory instrument. */ +typedef unsigned int PFS_memory_key; + +enum PFS_class_type +{ + PFS_CLASS_NONE= 0, + PFS_CLASS_MUTEX= 1, + PFS_CLASS_RWLOCK= 2, + PFS_CLASS_COND= 3, + PFS_CLASS_FILE= 4, + PFS_CLASS_TABLE= 5, + PFS_CLASS_STAGE= 6, + PFS_CLASS_STATEMENT= 7, + PFS_CLASS_TRANSACTION= 8, + PFS_CLASS_SOCKET= 9, + PFS_CLASS_TABLE_IO= 10, + PFS_CLASS_TABLE_LOCK= 11, + PFS_CLASS_IDLE= 12, + PFS_CLASS_MEMORY= 13, + PFS_CLASS_METADATA= 14, + PFS_CLASS_LAST= PFS_CLASS_METADATA, + PFS_CLASS_MAX= PFS_CLASS_LAST + 1 +}; + +/** User-defined instrument configuration. */ +struct PFS_instr_config +{ + /* Instrument name. */ + char *m_name; + /* Name length. */ + uint m_name_length; + /** Enabled flag. */ + bool m_enabled; + /** Timed flag. */ + bool m_timed; +}; + +typedef Dynamic_array<PFS_instr_config*> Pfs_instr_config_array; +extern Pfs_instr_config_array *pfs_instr_config_array; + +struct PFS_thread; + +extern uint mutex_class_start; +extern uint rwlock_class_start; +extern uint cond_class_start; +extern uint file_class_start; +extern uint socket_class_start; +extern uint wait_class_max; + +/** Information for all instrumentation. */ +struct PFS_instr_class +{ + /** Class type */ + PFS_class_type m_type; + /** True if this instrument is enabled. */ + bool m_enabled; + /** True if this instrument is timed. */ + bool m_timed; + /** Instrument flags. */ + int m_flags; + /** Volatility index. */ + int m_volatility; + /** + Instrument name index. + Self index in: + - EVENTS_WAITS_SUMMARY_*_BY_EVENT_NAME for waits + - EVENTS_STAGES_SUMMARY_*_BY_EVENT_NAME for stages + - EVENTS_STATEMENTS_SUMMARY_*_BY_EVENT_NAME for statements + - EVENTS_TRANSACTIONS_SUMMARY_*_BY_EVENT_NAME for transactions + */ + uint m_event_name_index; + /** Instrument name. */ + char m_name[PFS_MAX_INFO_NAME_LENGTH]; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** Timer associated with this class. */ + enum_timer_name *m_timer; + + bool is_singleton() const + { + return m_flags & PSI_FLAG_GLOBAL; + } + + bool is_mutable() const + { + return m_flags & PSI_FLAG_MUTABLE; + } + + bool is_progress() const + { + assert(m_type == PFS_CLASS_STAGE); + return m_flags & PSI_FLAG_STAGE_PROGRESS; + } + + bool is_shared_exclusive() const + { + assert(m_type == PFS_CLASS_RWLOCK); + return m_flags & PSI_RWLOCK_FLAG_SX; + } + + static void set_enabled(PFS_instr_class *pfs, bool enabled); + static void set_timed(PFS_instr_class *pfs, bool timed); + + bool is_deferred() const + { + switch(m_type) + { + case PFS_CLASS_SOCKET: + return true; + break; + default: + return false; + break; + }; + } +}; + +struct PFS_mutex; + +/** Instrumentation metadata for a MUTEX. */ +struct PFS_ALIGNED PFS_mutex_class : public PFS_instr_class +{ + /** Mutex usage statistics. */ + PFS_mutex_stat m_mutex_stat; + /** Singleton instance. */ + PFS_mutex *m_singleton; +}; + +struct PFS_rwlock; + +/** Instrumentation metadata for a RWLOCK. */ +struct PFS_ALIGNED PFS_rwlock_class : public PFS_instr_class +{ + /** Rwlock usage statistics. */ + PFS_rwlock_stat m_rwlock_stat; + /** Singleton instance. */ + PFS_rwlock *m_singleton; +}; + +struct PFS_cond; + +/** Instrumentation metadata for a COND. */ +struct PFS_ALIGNED PFS_cond_class : public PFS_instr_class +{ + /** + Condition usage statistics. + This statistic is not exposed in user visible tables yet. + */ + PFS_cond_stat m_cond_stat; + /** Singleton instance. */ + PFS_cond *m_singleton; +}; + +/** Instrumentation metadata of a thread. */ +struct PFS_ALIGNED PFS_thread_class +{ + /** True if this thread instrument is enabled. */ + bool m_enabled; + /** Singleton instance. */ + PFS_thread *m_singleton; + /** Thread instrument name. */ + char m_name[PFS_MAX_INFO_NAME_LENGTH]; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** Instrument flags. */ + int m_flags; + bool is_system_thread() const { return m_flags & PSI_FLAG_THREAD_SYSTEM; } +}; + +#define PFS_TABLESHARE_HASHKEY_SIZE (NAME_LEN + 1 + NAME_LEN + 1) + +/** Key identifying a table share. */ +struct PFS_table_share_key +{ + /** + Hash search key. + This has to be a string for LF_HASH, + the format is "<enum_object_type><schema_name><0x00><object_name><0x00>" + @see create_table_def_key + */ + char m_hash_key[PFS_TABLESHARE_HASHKEY_SIZE]; + /** Length in bytes of @c m_hash_key. */ + uint m_key_length; +}; + +/** Table index or 'key' */ +struct PFS_table_key +{ + /** Index name */ + char m_name[NAME_LEN]; + /** Length in bytes of @c m_name. */ + uint m_name_length; +}; + +/** Index statistics of a table.*/ +struct PFS_table_share_index +{ + pfs_lock m_lock; + /** The index name */ + PFS_table_key m_key; + /** The index stat */ + PFS_table_io_stat m_stat; + /** Owner table share. To be used later. */ + PFS_table_share* m_owner; + /** Container page. */ + PFS_opaque_container_page *m_page; +}; + +/** Lock statistics of a table.*/ +struct PFS_table_share_lock +{ + pfs_lock m_lock; + /** Lock stats. */ + PFS_table_lock_stat m_stat; + /** Owner table share. To be used later. */ + PFS_table_share* m_owner; + /** Container page. */ + PFS_opaque_container_page *m_page; +}; + +/** Instrumentation metadata for a table share. */ +struct PFS_ALIGNED PFS_table_share +{ +public: + uint32 get_version() + { return m_lock.get_version(); } + + enum_object_type get_object_type() + { + return (enum_object_type) m_key.m_hash_key[0]; + } + + void aggregate_io(void); + void aggregate_lock(void); + + void sum_io(PFS_single_stat *result, uint key_count); + void sum_lock(PFS_single_stat *result); + void sum(PFS_single_stat *result, uint key_count); + + inline void aggregate(void) + { + aggregate_io(); + aggregate_lock(); + } + + inline void init_refcount(void) + { + PFS_atomic::store_32(& m_refcount, 1); + } + + inline int get_refcount(void) + { + return PFS_atomic::load_32(& m_refcount); + } + + inline void inc_refcount(void) + { + PFS_atomic::add_32(& m_refcount, 1); + } + + inline void dec_refcount(void) + { + PFS_atomic::add_32(& m_refcount, -1); + } + + void refresh_setup_object_flags(PFS_thread *thread); + + /** Internal lock. */ + pfs_lock m_lock; + /** + True if table instrumentation is enabled. + This flag is computed from the content of table setup_objects. + */ + bool m_enabled; + /** + True if table instrumentation is timed. + This flag is computed from the content of table setup_objects. + */ + bool m_timed; + + /** Search key. */ + PFS_table_share_key m_key; + /** Schema name. */ + const char *m_schema_name; + /** Length in bytes of @c m_schema_name. */ + uint m_schema_name_length; + /** Table name. */ + const char *m_table_name; + /** Length in bytes of @c m_table_name. */ + uint m_table_name_length; + /** Number of indexes. */ + uint m_key_count; + /** Container page. */ + PFS_opaque_container_page *m_page; + + PFS_table_share_lock *find_lock_stat() const; + PFS_table_share_lock *find_or_create_lock_stat(); + void destroy_lock_stat(); + + PFS_table_share_index *find_index_stat(uint index) const; + PFS_table_share_index *find_or_create_index_stat(const TABLE_SHARE *server_share, uint index); + void destroy_index_stats(); + +private: + /** Number of opened table handles. */ + int m_refcount; + /** Table locks statistics. */ + PFS_table_share_lock *m_race_lock_stat; + /** Table indexes' stats. */ + PFS_table_share_index *m_race_index_stat[MAX_INDEXES + 1]; +}; + +/** Statistics for the IDLE instrument. */ +extern PFS_single_stat global_idle_stat; +/** Statistics for dropped table io. */ +extern PFS_table_io_stat global_table_io_stat; +/** Statistics for dropped table lock. */ +extern PFS_table_lock_stat global_table_lock_stat; +/** Statistics for the METADATA instrument. */ +extern PFS_single_stat global_metadata_stat; +/** Statistics for the transaction instrument. */ +extern PFS_transaction_stat global_transaction_stat; + +inline uint sanitize_index_count(uint count) +{ + if (likely(count <= MAX_INDEXES)) + return count; + return 0; +} + +#define GLOBAL_TABLE_IO_EVENT_INDEX 0 +#define GLOBAL_TABLE_LOCK_EVENT_INDEX 1 +#define GLOBAL_IDLE_EVENT_INDEX 2 +#define GLOBAL_METADATA_EVENT_INDEX 3 +/** Number of global wait events. */ +#define COUNT_GLOBAL_EVENT_INDEX 4 + +/** Transaction events are not wait events .*/ +#define GLOBAL_TRANSACTION_INDEX 0 + +/** + Instrument controlling all table io. + This instrument is used with table SETUP_OBJECTS. +*/ +extern PFS_instr_class global_table_io_class; + +/** + Instrument controlling all table lock. + This instrument is used with table SETUP_OBJECTS. +*/ +extern PFS_instr_class global_table_lock_class; + +/** + Instrument controlling all idle waits. +*/ +extern PFS_instr_class global_idle_class; + +extern PFS_instr_class global_metadata_class; + +struct PFS_file; + +/** Instrumentation metadata for a file. */ +struct PFS_ALIGNED PFS_file_class : public PFS_instr_class +{ + /** File usage statistics. */ + PFS_file_stat m_file_stat; + /** Singleton instance. */ + PFS_file *m_singleton; +}; + +/** Instrumentation metadata for a stage. */ +struct PFS_ALIGNED PFS_stage_class : public PFS_instr_class +{ + /** + Length of the 'stage/<component>/' prefix. + This is to extract 'foo' from 'stage/sql/foo'. + */ + uint m_prefix_length; + /** Stage usage statistics. */ + PFS_stage_stat m_stage_stat; +}; + +/** Instrumentation metadata for a statement. */ +struct PFS_ALIGNED PFS_statement_class : public PFS_instr_class +{ +}; + +/** Instrumentation metadata for a transaction. */ +struct PFS_ALIGNED PFS_transaction_class : public PFS_instr_class +{ +}; + +extern PFS_transaction_class global_transaction_class; + +struct PFS_socket; + +/** Instrumentation metadata for a socket. */ +struct PFS_ALIGNED PFS_socket_class : public PFS_instr_class +{ + /** Socket usage statistics. */ + PFS_socket_stat m_socket_stat; + /** Singleton instance. */ + PFS_socket *m_singleton; +}; + +/** Instrumentation metadata for a memory. */ +struct PFS_ALIGNED PFS_memory_class : public PFS_instr_class +{ + bool is_global() const + { + return m_flags & PSI_FLAG_GLOBAL; + } + + bool is_transferable() const + { + return m_flags & PSI_FLAG_TRANSFER; + } +}; + +void init_event_name_sizing(const PFS_global_param *param); + +void register_global_classes(); + +int init_sync_class(uint mutex_class_sizing, + uint rwlock_class_sizing, + uint cond_class_sizing); + +void cleanup_sync_class(); +int init_thread_class(uint thread_class_sizing); +void cleanup_thread_class(); +int init_table_share(uint table_share_sizing); +void cleanup_table_share(); + +int init_table_share_lock_stat(uint table_stat_sizing); +void cleanup_table_share_lock_stat(); +PFS_table_share_lock* create_table_share_lock_stat(); +void release_table_share_lock_stat(PFS_table_share_lock *pfs); + +int init_table_share_index_stat(uint index_stat_sizing); +void cleanup_table_share_index_stat(); +PFS_table_share_index* create_table_share_index_stat(const TABLE_SHARE *share, uint index); +void release_table_share_index_stat(PFS_table_share_index *pfs); + +int init_table_share_hash(const PFS_global_param *param); +void cleanup_table_share_hash(); +int init_file_class(uint file_class_sizing); +void cleanup_file_class(); +int init_stage_class(uint stage_class_sizing); +void cleanup_stage_class(); +int init_statement_class(uint statement_class_sizing); +void cleanup_statement_class(); +int init_socket_class(uint socket_class_sizing); +void cleanup_socket_class(); +int init_memory_class(uint memory_class_sizing); +void cleanup_memory_class(); + +PFS_sync_key register_mutex_class(const char *name, uint name_length, + int flags); + +PFS_sync_key register_rwlock_class(const char *name, uint name_length, + int flags); + +PFS_sync_key register_cond_class(const char *name, uint name_length, + int flags); + +PFS_thread_key register_thread_class(const char *name, uint name_length, + int flags); + +PFS_file_key register_file_class(const char *name, uint name_length, + int flags); + +PFS_stage_key register_stage_class(const char *name, + uint prefix_length, + uint name_length, + int flags); + +PFS_statement_key register_statement_class(const char *name, uint name_length, + int flags); + +PFS_socket_key register_socket_class(const char *name, uint name_length, + int flags); + +PFS_memory_key register_memory_class(const char *name, uint name_length, + int flags); + +PFS_mutex_class *find_mutex_class(PSI_mutex_key key); +PFS_mutex_class *sanitize_mutex_class(PFS_mutex_class *unsafe); +PFS_rwlock_class *find_rwlock_class(PSI_rwlock_key key); +PFS_rwlock_class *sanitize_rwlock_class(PFS_rwlock_class *unsafe); +PFS_cond_class *find_cond_class(PSI_cond_key key); +PFS_cond_class *sanitize_cond_class(PFS_cond_class *unsafe); +PFS_thread_class *find_thread_class(PSI_thread_key key); +PFS_thread_class *sanitize_thread_class(PFS_thread_class *unsafe); +PFS_file_class *find_file_class(PSI_file_key key); +PFS_file_class *sanitize_file_class(PFS_file_class *unsafe); +PFS_stage_class *find_stage_class(PSI_stage_key key); +PFS_stage_class *sanitize_stage_class(PFS_stage_class *unsafe); +PFS_statement_class *find_statement_class(PSI_statement_key key); +PFS_statement_class *sanitize_statement_class(PFS_statement_class *unsafe); +PFS_instr_class *find_table_class(uint index); +PFS_instr_class *sanitize_table_class(PFS_instr_class *unsafe); +PFS_socket_class *find_socket_class(PSI_socket_key key); +PFS_socket_class *sanitize_socket_class(PFS_socket_class *unsafe); +PFS_memory_class *find_memory_class(PSI_memory_key key); +PFS_memory_class *sanitize_memory_class(PFS_memory_class *unsafe); +PFS_instr_class *find_idle_class(uint index); +PFS_instr_class *sanitize_idle_class(PFS_instr_class *unsafe); +PFS_instr_class *find_metadata_class(uint index); +PFS_instr_class *sanitize_metadata_class(PFS_instr_class *unsafe); +PFS_transaction_class *find_transaction_class(uint index); +PFS_transaction_class *sanitize_transaction_class(PFS_transaction_class *unsafe); + +PFS_table_share *find_or_create_table_share(PFS_thread *thread, + bool temporary, + const TABLE_SHARE *share); +void release_table_share(PFS_table_share *pfs); +void drop_table_share(PFS_thread *thread, + bool temporary, + const char *schema_name, uint schema_name_length, + const char *table_name, uint table_name_length); + +PFS_table_share *sanitize_table_share(PFS_table_share *unsafe); + +extern ulong mutex_class_max; +extern ulong mutex_class_lost; +extern ulong rwlock_class_max; +extern ulong rwlock_class_lost; +extern ulong cond_class_max; +extern ulong cond_class_lost; +extern ulong thread_class_max; +extern ulong thread_class_lost; +extern ulong file_class_max; +extern ulong file_class_lost; +extern ulong stage_class_max; +extern ulong stage_class_lost; +extern ulong statement_class_max; +extern ulong statement_class_lost; +extern ulong transaction_class_max; +extern ulong socket_class_max; +extern ulong socket_class_lost; +extern ulong memory_class_max; +extern ulong memory_class_lost; + +/* Exposing the data directly, for iterators. */ + +extern PFS_mutex_class *mutex_class_array; +extern PFS_rwlock_class *rwlock_class_array; +extern PFS_cond_class *cond_class_array; +extern PFS_file_class *file_class_array; + +void reset_events_waits_by_class(); +void reset_file_class_io(); +void reset_socket_class_io(); + +/** Update derived flags for all table shares. */ +void update_table_share_derived_flags(PFS_thread *thread); + +/** Update derived flags for all stored procedure shares. */ +void update_program_share_derived_flags(PFS_thread *thread); + +extern LF_HASH table_share_hash; + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_lock.h b/storage/perfschema/pfs_lock.h new file mode 100644 index 00000000..0f4bcb6d --- /dev/null +++ b/storage/perfschema/pfs_lock.h @@ -0,0 +1,310 @@ +/* Copyright (c) 2009, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_LOCK_H +#define PFS_LOCK_H + +/** + @file storage/perfschema/pfs_lock.h + Performance schema internal locks (declarations). +*/ + +#include "my_global.h" + +#include "pfs_atomic.h" + +/* to cause bugs, testing */ +// #define MEM(X) std::memory_order_relaxed +/* correct code */ +#define MEM(X) X + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +/** + State of a free record. + Values of a free record should not be read by a reader. + Writers can concurrently attempt to allocate a free record. +*/ +#define PFS_LOCK_FREE 0x00 +/** + State of a dirty record. + Values of a dirty record should not be read by a reader, + as the record is currently being modified. + Only one writer, the writer which owns the record, should + modify the record content. +*/ +#define PFS_LOCK_DIRTY 0x01 +/** + State of an allocated record. + Values of an allocated record are safe to read by a reader. + A writer may modify some but not all properties of the record: + only modifying values that can never cause the reader to crash is allowed. +*/ +#define PFS_LOCK_ALLOCATED 0x02 + +#define VERSION_MASK 0xFFFFFFFC +#define STATE_MASK 0x00000003 +#define VERSION_INC 4 + +struct pfs_optimistic_state +{ + uint32 m_version_state; +}; + +struct pfs_dirty_state +{ + uint32 m_version_state; +}; + +/** + A 'lock' protecting performance schema internal buffers. + This lock is used to mark the state of a record. + Access to the record is not enforced here, + it's up to the readers and writers to look at the record state + before making an actual read or write operation. +*/ +struct pfs_lock +{ + /** + The record internal version and state + @sa PFS_LOCK_FREE + @sa PFS_LOCK_DIRTY + @sa PFS_LOCK_ALLOCATED + The version number is to transform the 'ABA' problem + (see http://en.wikipedia.org/wiki/ABA_problem) + into an 'A(n)BA(n + 1)' problem, where 'n' is the m_version number. + When the performance schema instrumentation deletes a record, + then create a different record reusing the same memory allocation, + the version number is incremented, so that a reader can detect that + the record was changed. Note that the version number is never + reset to zero when a new record is created. + The version number is stored in the high 30 bits. + The state is stored in the low 2 bits. + */ + uint32 m_version_state; + + uint32 copy_version_state() + { + uint32 copy; + + copy= m_version_state; /* dirty read */ + + return copy; + } + + /** Returns true if the record is free. */ + bool is_free(void) + { + uint32 copy; + + copy= PFS_atomic::load_u32(&m_version_state); + + return ((copy & STATE_MASK) == PFS_LOCK_FREE); + } + + /** Returns true if the record contains values that can be read. */ + bool is_populated(void) + { + uint32 copy; + + copy= PFS_atomic::load_u32(&m_version_state); + + return ((copy & STATE_MASK) == PFS_LOCK_ALLOCATED); + } + + /** + Execute a free to dirty transition. + This transition is safe to execute concurrently by multiple writers. + Only one writer will succeed to acquire the record. + @return true if the operation succeed + */ + bool free_to_dirty(pfs_dirty_state *copy_ptr) + { + uint32 old_val; + + old_val= PFS_atomic::load_u32(&m_version_state); + + if ((old_val & STATE_MASK) != PFS_LOCK_FREE) + { + return false; + } + + uint32 new_val= (old_val & VERSION_MASK) + PFS_LOCK_DIRTY; + bool pass; + + pass= PFS_atomic::cas_u32(&m_version_state, &old_val, new_val); + + if (pass) + { + copy_ptr->m_version_state= new_val; + } + + return pass; + } + + /** + Execute an allocated to dirty transition. + This transition should be executed by the writer that owns the record, + before the record is modified. + */ + void allocated_to_dirty(pfs_dirty_state *copy_ptr) + { + uint32 copy= copy_version_state(); + /* Make sure the record was ALLOCATED. */ + assert((copy & STATE_MASK) == PFS_LOCK_ALLOCATED); + /* Keep the same version, set the DIRTY state */ + uint32 new_val= (copy & VERSION_MASK) + PFS_LOCK_DIRTY; + /* We own the record, no need to use compare and swap. */ + + PFS_atomic::store_u32(&m_version_state, new_val); + + copy_ptr->m_version_state= new_val; + } + + /** + Execute a dirty to allocated transition. + This transition should be executed by the writer that owns the record, + after the record is in a state ready to be read. + */ + void dirty_to_allocated(const pfs_dirty_state *copy) + { + /* Make sure the record was DIRTY. */ + assert((copy->m_version_state & STATE_MASK) == PFS_LOCK_DIRTY); + /* Increment the version, set the ALLOCATED state */ + uint32 new_val= (copy->m_version_state & VERSION_MASK) + VERSION_INC + PFS_LOCK_ALLOCATED; + + PFS_atomic::store_u32(&m_version_state, new_val); + } + + /** + Initialize a lock to allocated. + This transition should be executed by the writer that owns the record and the lock, + after the record is in a state ready to be read. + */ + void set_allocated(void) + { + /* Do not set the version to 0, read the previous value. */ + uint32 copy= copy_version_state(); + /* Increment the version, set the ALLOCATED state */ + uint32 new_val= (copy & VERSION_MASK) + VERSION_INC + PFS_LOCK_ALLOCATED; + + PFS_atomic::store_u32(&m_version_state, new_val); + } + + /** + Initialize a lock to dirty. + */ + void set_dirty(pfs_dirty_state *copy_ptr) + { + /* Do not set the version to 0, read the previous value. */ + uint32 copy= PFS_atomic::load_u32(&m_version_state); + /* Increment the version, set the DIRTY state */ + uint32 new_val= (copy & VERSION_MASK) + VERSION_INC + PFS_LOCK_DIRTY; + PFS_atomic::store_u32(&m_version_state, new_val); + + copy_ptr->m_version_state= new_val; + } + + /** + Execute a dirty to free transition. + This transition should be executed by the writer that owns the record. + */ + void dirty_to_free(const pfs_dirty_state *copy) + { + /* Make sure the record was DIRTY. */ + assert((copy->m_version_state & STATE_MASK) == PFS_LOCK_DIRTY); + /* Keep the same version, set the FREE state */ + uint32 new_val= (copy->m_version_state & VERSION_MASK) + PFS_LOCK_FREE; + + PFS_atomic::store_u32(&m_version_state, new_val); + } + + /** + Execute an allocated to free transition. + This transition should be executed by the writer that owns the record. + */ + void allocated_to_free(void) + { + /* + If this record is not in the ALLOCATED state and the caller is trying + to free it, this is a bug: the caller is confused, + and potentially damaging data owned by another thread or object. + */ + uint32 copy= copy_version_state(); + /* Make sure the record was ALLOCATED. */ + assert(((copy & STATE_MASK) == PFS_LOCK_ALLOCATED)); + /* Keep the same version, set the FREE state */ + uint32 new_val= (copy & VERSION_MASK) + PFS_LOCK_FREE; + + PFS_atomic::store_u32(&m_version_state, new_val); + } + + /** + Start an optimistic read operation. + @param [out] copy Saved lock state + @sa end_optimist_lock. + */ + void begin_optimistic_lock(struct pfs_optimistic_state *copy) + { + copy->m_version_state= PFS_atomic::load_u32(&m_version_state); + } + + /** + End an optimistic read operation. + @sa begin_optimist_lock. + @param copy Saved lock state + @return true if the data read is safe to use. + */ + bool end_optimistic_lock(const struct pfs_optimistic_state *copy) + { + uint32 version_state; + + /* Check there was valid data to look at. */ + if ((copy->m_version_state & STATE_MASK) != PFS_LOCK_ALLOCATED) + return false; + + version_state= PFS_atomic::load_u32(&m_version_state); + + /* Check the version + state has not changed. */ + if (copy->m_version_state != version_state) + return false; + + return true; + } + + uint32 get_version() + { + uint32 version_state; + + version_state= PFS_atomic::load_u32(&m_version_state); + + return (version_state & VERSION_MASK); + } +}; + + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_memory.cc b/storage/perfschema/pfs_memory.cc new file mode 100644 index 00000000..0ee8e3dd --- /dev/null +++ b/storage/perfschema/pfs_memory.cc @@ -0,0 +1,98 @@ +/* Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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/pfs_memory.cc + Memory statistics aggregation (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "pfs_global.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_account.h" +#include "pfs_host.h" +#include "pfs_user.h" +#include "pfs_atomic.h" +#include "pfs_buffer_container.h" +#include "m_string.h" + +static void fct_reset_memory_by_thread(PFS_thread *pfs) +{ + PFS_account *account= sanitize_account(pfs->m_account); + PFS_user *user= sanitize_user(pfs->m_user); + PFS_host *host= sanitize_host(pfs->m_host); + aggregate_thread_memory(true, pfs, account, user, host); +} + +/** Reset table MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME data. */ +void reset_memory_by_thread() +{ + global_thread_container.apply(fct_reset_memory_by_thread); +} + +static void fct_reset_memory_by_account(PFS_account *pfs) +{ + PFS_user *user= sanitize_user(pfs->m_user); + PFS_host *host= sanitize_host(pfs->m_host); + pfs->aggregate_memory(true, user, host); +} + +/** Reset table MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME data. */ +void reset_memory_by_account() +{ + global_account_container.apply(fct_reset_memory_by_account); +} + +static void fct_reset_memory_by_user(PFS_user *pfs) +{ + pfs->aggregate_memory(true); +} + +/** Reset table MEMORY_SUMMARY_BY_USER_BY_EVENT_NAME data. */ +void reset_memory_by_user() +{ + global_user_container.apply(fct_reset_memory_by_user); +} + +static void fct_reset_memory_by_host(PFS_host *pfs) +{ + pfs->aggregate_memory(true); +} + +/** Reset table MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME data. */ +void reset_memory_by_host() +{ + global_host_container.apply(fct_reset_memory_by_host); +} + +/** Reset table MEMORY_GLOBAL_BY_EVENT_NAME data. */ +void reset_memory_global() +{ + PFS_memory_stat *stat= global_instr_class_memory_array; + PFS_memory_stat *stat_last= global_instr_class_memory_array + memory_class_max; + + for ( ; stat < stat_last; stat++) + stat->rebase(); +} + diff --git a/storage/perfschema/pfs_memory.h b/storage/perfschema/pfs_memory.h new file mode 100644 index 00000000..ee90b7d9 --- /dev/null +++ b/storage/perfschema/pfs_memory.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef PFS_MEMORY_H +#define PFS_MEMORY_H + +/** + @file storage/perfschema/pfs_memory.h + Memory statistics aggregation (declarations). +*/ + +void reset_memory_by_thread(); +void reset_memory_by_account(); +void reset_memory_by_user(); +void reset_memory_by_host(); +void reset_memory_global(); + +#endif + diff --git a/storage/perfschema/pfs_prepared_stmt.cc b/storage/perfschema/pfs_prepared_stmt.cc new file mode 100644 index 00000000..50e4e27b --- /dev/null +++ b/storage/perfschema/pfs_prepared_stmt.cc @@ -0,0 +1,145 @@ +/* Copyright (c) 2014, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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/pfs_prepared_stmt.cc + Prepared Statement data structures (implementation). +*/ + +/* + This code needs extra visibility in the lexer structures +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "pfs_instr.h" +#include "pfs_prepared_stmt.h" +#include "pfs_global.h" +#include "sql_string.h" +#include "pfs_buffer_container.h" +#include <string.h> + +/** + Initialize table PREPARED_STATEMENTS_INSTANCE. + @param param performance schema sizing +*/ +int init_prepared_stmt(const PFS_global_param *param) +{ + if (global_prepared_stmt_container.init(param->m_prepared_stmt_sizing)) + return 1; + + reset_prepared_stmt_instances(); + return 0; +} + +/** Cleanup table PREPARED_STATEMENTS_INSTANCE. */ +void cleanup_prepared_stmt(void) +{ + global_prepared_stmt_container.cleanup(); +} + +void PFS_prepared_stmt::reset_data() +{ + m_prepare_stat.reset(); + m_reprepare_stat.reset(); + m_execute_stat.reset(); +} + +static void fct_reset_prepared_stmt_instances(PFS_prepared_stmt *pfs) +{ + pfs->reset_data(); +} + +void reset_prepared_stmt_instances() +{ + global_prepared_stmt_container.apply_all(fct_reset_prepared_stmt_instances); +} + +PFS_prepared_stmt* +create_prepared_stmt(void *identity, + PFS_thread *thread, PFS_program *pfs_program, + PFS_events_statements *pfs_stmt, uint stmt_id, + const char* stmt_name, uint stmt_name_length) +{ + PFS_prepared_stmt *pfs= NULL; + pfs_dirty_state dirty_state; + + /* Create a new record in prepared stmt stat array. */ + pfs= global_prepared_stmt_container.allocate(& dirty_state); + if (pfs != NULL) + { + /* Reset the stats. */ + pfs->reset_data(); + /* Do the assignments. */ + pfs->m_identity= identity; + + pfs->m_sqltext_length= 0; + + if (stmt_name != NULL) + { + pfs->m_stmt_name_length= stmt_name_length; + if (pfs->m_stmt_name_length > PS_NAME_LENGTH) + pfs->m_stmt_name_length= PS_NAME_LENGTH; + strncpy(pfs->m_stmt_name, stmt_name, pfs->m_stmt_name_length); + } + else + pfs->m_stmt_name_length= 0; + + pfs->m_stmt_id= stmt_id; + pfs->m_owner_thread_id= thread->m_thread_internal_id; + + /* If this statement prepare is called from a SP. */ + if (pfs_program) + { + pfs->m_owner_object_type= pfs_program->m_type; + strncpy(pfs->m_owner_object_schema, pfs_program->m_schema_name, pfs_program->m_schema_name_length); + pfs->m_owner_object_schema_length= pfs_program->m_schema_name_length; + strncpy(pfs->m_owner_object_name, pfs_program->m_object_name, pfs_program->m_object_name_length); + pfs->m_owner_object_name_length= pfs_program->m_object_name_length; + } + else + { + pfs->m_owner_object_type= NO_OBJECT_TYPE; + pfs->m_owner_object_schema_length= 0; + pfs->m_owner_object_name_length= 0; + } + + if (pfs_stmt) + { + if (pfs_program) + pfs->m_owner_event_id= pfs_stmt->m_event.m_nesting_event_id; + else + pfs->m_owner_event_id= pfs_stmt->m_event.m_event_id; + } + + /* Insert this record. */ + pfs->m_lock.dirty_to_allocated(& dirty_state); + } + + return pfs; +} + +void delete_prepared_stmt(PFS_prepared_stmt *pfs) +{ + global_prepared_stmt_container.deallocate(pfs); + return; +} diff --git a/storage/perfschema/pfs_prepared_stmt.h b/storage/perfschema/pfs_prepared_stmt.h new file mode 100644 index 00000000..1a061223 --- /dev/null +++ b/storage/perfschema/pfs_prepared_stmt.h @@ -0,0 +1,95 @@ +/* Copyright (c) 2014, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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_PS_H +#define PFS_PS_H + +/** + @file storage/perfschema/pfs_prepared_statement.h + Stored Program data structures (declarations). +*/ + +#include "pfs_stat.h" +#include "mysql/psi/psi.h" +#include "mysql/psi/mysql_ps.h" +#include "pfs_program.h" + +#define PS_NAME_LENGTH NAME_LEN + +struct PFS_ALIGNED PFS_prepared_stmt : public PFS_instr +{ + /** Column OBJECT_INSTANCE_BEGIN */ + const void *m_identity; + + /** STATEMENT_ID */ + ulonglong m_stmt_id; + + /** STATEMENT_NAME */ + char m_stmt_name[PS_NAME_LENGTH]; + uint m_stmt_name_length; + + /** SQL_TEXT */ + char m_sqltext[COL_INFO_SIZE]; + uint m_sqltext_length; + + /** Column OWNER_THREAD_ID */ + ulonglong m_owner_thread_id; + + /** Column OWNER_EVENT_ID. */ + ulonglong m_owner_event_id; + + /** Column OBJECT_OWNER_TYPE. */ + enum_object_type m_owner_object_type; + + /** Column OBJECT_OWNER_SCHEMA. */ + char m_owner_object_schema[COL_OBJECT_SCHEMA_SIZE]; + uint m_owner_object_schema_length; + + /** Column OBJECT_OWNER_NAME. */ + char m_owner_object_name[COL_OBJECT_NAME_SIZE]; + uint m_owner_object_name_length; + + /** COLUMN TIMER_PREPARE. Prepared stmt prepare stat. */ + PFS_single_stat m_prepare_stat; + + /** COLUMN COUNT_REPREPARE. Prepared stmt reprepare stat. */ + PFS_single_stat m_reprepare_stat; + + /** Prepared stmt execution stat. */ + PFS_statement_stat m_execute_stat; + + /** Reset data for this record. */ + void reset_data(); +}; + +int init_prepared_stmt(const PFS_global_param *param); +void cleanup_prepared_stmt(void); + +void reset_prepared_stmt_instances(); + +PFS_prepared_stmt* +create_prepared_stmt(void *identity, + PFS_thread *thread, PFS_program *pfs_program, + PFS_events_statements *pfs_stmt, uint stmt_id, + const char* stmt_name, uint stmt_name_length); +void delete_prepared_stmt(PFS_prepared_stmt *pfs_ps); +#endif diff --git a/storage/perfschema/pfs_program.cc b/storage/perfschema/pfs_program.cc new file mode 100644 index 00000000..de456610 --- /dev/null +++ b/storage/perfschema/pfs_program.cc @@ -0,0 +1,322 @@ +/* Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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/pfs_program.cc + Statement Digest data structures (implementation). +*/ + +/* + This code needs extra visibility in the lexer structures +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "pfs_instr.h" +#include "pfs_program.h" +#include "pfs_global.h" +#include "sql_string.h" +#include "pfs_setup_object.h" +#include "pfs_buffer_container.h" +#include "mysqld.h" //system_charset_info +#include <string.h> + +LF_HASH program_hash; +static bool program_hash_inited= false; + +/** + Initialize table EVENTS_STATEMENTS_SUMMARY_BY_PROGRAM. + @param param performance schema sizing +*/ +int init_program(const PFS_global_param *param) +{ + if (global_program_container.init(param->m_program_sizing)) + return 1; + + reset_esms_by_program(); + return 0; +} + +/** Cleanup table EVENTS_STATEMENTS_SUMMARY_BY_PROGRAM. */ +void cleanup_program(void) +{ + global_program_container.cleanup(); +} + +C_MODE_START +static uchar *program_hash_get_key(const uchar *entry, size_t *length, + my_bool) +{ + const PFS_program * const *typed_entry; + const PFS_program *program; + const void *result; + typed_entry= reinterpret_cast<const PFS_program* const *> (entry); + assert(typed_entry != NULL); + program= *typed_entry; + assert(program != NULL); + *length= program->m_key.m_key_length; + result= program->m_key.m_hash_key; + return const_cast<uchar*> (reinterpret_cast<const uchar*> (result)); +} +C_MODE_END + +/** + Initialize the program hash. + @return 0 on success +*/ +int init_program_hash(const PFS_global_param *param) +{ + if ((! program_hash_inited) && (param->m_program_sizing != 0)) + { + lf_hash_init(&program_hash, sizeof(PFS_program*), LF_HASH_UNIQUE, + 0, 0, program_hash_get_key, &my_charset_bin); + program_hash_inited= true; + } + return 0; +} + +/** Cleanup the program hash. */ +void cleanup_program_hash(void) +{ + if (program_hash_inited) + { + lf_hash_destroy(&program_hash); + program_hash_inited= false; + } +} + +static void set_program_key(PFS_program_key *key, + enum_object_type object_type, + const char *object_name, uint object_name_length, + const char *schema_name, uint schema_name_length) +{ + assert(object_name_length <= COL_OBJECT_NAME_SIZE); + assert(schema_name_length <= COL_OBJECT_SCHEMA_SIZE); + + /* + To make sure generated key is case insensitive, + convert object_name/schema_name to lowercase. + */ + + char *ptr= &key->m_hash_key[0]; + + ptr[0]= object_type; + ptr++; + + if (object_name_length > 0) + { + char tmp_object_name[COL_OBJECT_NAME_SIZE + 1]; + memcpy(tmp_object_name, object_name, object_name_length); + tmp_object_name[object_name_length]= '\0'; + my_casedn_str(system_charset_info, tmp_object_name); + memcpy(ptr, tmp_object_name, object_name_length); + ptr+= object_name_length; + } + ptr[0]= 0; + ptr++; + + if (schema_name_length > 0) + { + char tmp_schema_name[COL_OBJECT_SCHEMA_SIZE + 1]; + memcpy(tmp_schema_name, schema_name, schema_name_length); + tmp_schema_name[schema_name_length]='\0'; + my_casedn_str(system_charset_info, tmp_schema_name); + memcpy(ptr, tmp_schema_name, schema_name_length); + ptr+= schema_name_length; + } + ptr[0]= 0; + ptr++; + + key->m_key_length= static_cast<uint>(ptr - &key->m_hash_key[0]); +} + + + +void PFS_program::reset_data() +{ + m_sp_stat.reset(); + m_stmt_stat.reset(); +} + +static void fct_reset_esms_by_program(PFS_program *pfs) +{ + pfs->reset_data(); +} + +void reset_esms_by_program() +{ + global_program_container.apply_all(fct_reset_esms_by_program); +} + +static LF_PINS* get_program_hash_pins(PFS_thread *thread) +{ + if (unlikely(thread->m_program_hash_pins == NULL)) + { + if (! program_hash_inited) + return NULL; + thread->m_program_hash_pins= lf_hash_get_pins(&program_hash); + } + return thread->m_program_hash_pins; +} + +PFS_program* +find_or_create_program(PFS_thread *thread, + enum_object_type object_type, + const char *object_name, + uint object_name_length, + const char *schema_name, + uint schema_name_length) +{ + bool is_enabled, is_timed; + + LF_PINS *pins= get_program_hash_pins(thread); + if (unlikely(pins == NULL)) + { + global_program_container.m_lost++; + return NULL; + } + + /* Prepare program key */ + PFS_program_key key; + set_program_key(&key, object_type, + object_name, object_name_length, + schema_name, schema_name_length); + + PFS_program **entry; + PFS_program *pfs= NULL; + uint retry_count= 0; + const uint retry_max= 3; + pfs_dirty_state dirty_state; + +search: + entry= reinterpret_cast<PFS_program**> + (lf_hash_search(&program_hash, pins, + key.m_hash_key, key.m_key_length)); + + if (entry && (entry != MY_ERRPTR)) + { + /* If record already exists then return its pointer. */ + pfs= *entry; + lf_hash_search_unpin(pins); + return pfs; + } + + lf_hash_search_unpin(pins); + + /* + First time while inserting this record to program array we need to + find out if it is enabled and timed. + */ + lookup_setup_object(thread, object_type, + schema_name, schema_name_length, + object_name, object_name_length, + &is_enabled, &is_timed); + + /* Else create a new record in program stat array. */ + pfs= global_program_container.allocate(& dirty_state); + if (pfs != NULL) + { + /* Do the assignments. */ + memcpy(pfs->m_key.m_hash_key, key.m_hash_key, key.m_key_length); + pfs->m_key.m_key_length= key.m_key_length; + pfs->m_type= object_type; + + pfs->m_object_name= pfs->m_key.m_hash_key + 1; + pfs->m_object_name_length= object_name_length; + pfs->m_schema_name= pfs->m_object_name + object_name_length + 1; + pfs->m_schema_name_length= schema_name_length; + pfs->m_enabled= is_enabled; + pfs->m_timed= is_timed; + + /* Insert this record. */ + pfs->m_lock.dirty_to_allocated(& dirty_state); + int res= lf_hash_insert(&program_hash, pins, &pfs); + + if (likely(res == 0)) + { + return pfs; + } + + global_program_container.deallocate(pfs); + + if (res > 0) + { + /* Duplicate insert by another thread */ + if (++retry_count > retry_max) + { + /* Avoid infinite loops */ + global_program_container.m_lost++; + return NULL; + } + goto search; + } + /* OOM in lf_hash_insert */ + global_program_container.m_lost++; + return NULL; + } + + return NULL; +} + +void drop_program(PFS_thread *thread, + enum_object_type object_type, + const char *object_name, + uint object_name_length, + const char *schema_name, + uint schema_name_length) +{ + LF_PINS *pins= get_program_hash_pins(thread); + if (unlikely(pins == NULL)) + return; + + /* Prepare program key */ + PFS_program_key key; + set_program_key(&key, object_type, + object_name, object_name_length, + schema_name, schema_name_length); + + PFS_program **entry; + entry= reinterpret_cast<PFS_program**> + (lf_hash_search(&program_hash, pins, + key.m_hash_key, key.m_key_length)); + + if (entry && (entry != MY_ERRPTR)) + { + PFS_program *pfs= NULL; + pfs= *entry; + + lf_hash_delete(&program_hash, pins, + key.m_hash_key, key.m_key_length); + global_program_container.deallocate(pfs); + } + + lf_hash_search_unpin(pins); + return; +} + +void PFS_program::refresh_setup_object_flags(PFS_thread *thread) +{ + lookup_setup_object(thread, m_type, + m_schema_name, m_schema_name_length, + m_object_name, m_object_name_length, + &m_enabled, &m_timed); +} diff --git a/storage/perfschema/pfs_program.h b/storage/perfschema/pfs_program.h new file mode 100644 index 00000000..a5a6245c --- /dev/null +++ b/storage/perfschema/pfs_program.h @@ -0,0 +1,103 @@ +/* Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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_PROGRAM_H +#define PFS_PROGRAM_H + +/** + @file storage/perfschema/pfs_program.h + Stored Program data structures (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_stat.h" + +#define PROGRAM_HASH_KEY_LENGTH sizeof(enum_object_type) + COL_OBJECT_NAME_SIZE + 1 + COL_OBJECT_SCHEMA_SIZE + 1 + +extern LF_HASH program_hash; + +/** + Hash key for a program. +*/ +struct PFS_program_key +{ + /** + Hash search key. + This has to be a string for LF_HASH, + the format is "<object_type><0x00><object_name><0x00><schema_name><0x00>" + */ + char m_hash_key[PROGRAM_HASH_KEY_LENGTH]; + uint m_key_length; +}; + +struct PFS_ALIGNED PFS_program : public PFS_instr +{ + /** Object type. */ + enum_object_type m_type; + + /** Object name. */ + const char *m_object_name; + int m_object_name_length; + + /** Object Schema name. */ + const char *m_schema_name; + int m_schema_name_length; + + /** Hash key */ + PFS_program_key m_key; + + /** Sub statement stat. */ + PFS_statement_stat m_stmt_stat; + + /** Stored program stat. */ + PFS_sp_stat m_sp_stat; + + /** Referesh setup object flags. */ + void refresh_setup_object_flags(PFS_thread* thread); + + /** Reset data for this record. */ + void reset_data(); +}; + +int init_program(const PFS_global_param *param); +void cleanup_program(void); +int init_program_hash(const PFS_global_param *param); +void cleanup_program_hash(void); + +void reset_esms_by_program(); + +PFS_program* +find_or_create_program(PFS_thread *thread, + enum_object_type object_type, + const char *object_name, + uint object_name_length, + const char *schema, + uint schema_length); + +void +drop_program(PFS_thread *thread, + enum_object_type object_type, + const char *object_name, + uint object_name_length, + const char *schema_name, + uint schema_name_length); +#endif diff --git a/storage/perfschema/pfs_server.cc b/storage/perfschema/pfs_server.cc new file mode 100644 index 00000000..258c2153 --- /dev/null +++ b/storage/perfschema/pfs_server.cc @@ -0,0 +1,448 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_server.cc + Private interface for the server (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "mysys_err.h" +#include "pfs_server.h" +#include "pfs.h" +#include "pfs_global.h" +#include "pfs_instr_class.h" +#include "pfs_builtin_memory.h" +#include "pfs_instr.h" +#include "pfs_events_waits.h" +#include "pfs_events_stages.h" +#include "pfs_events_statements.h" +#include "pfs_events_transactions.h" +#include "pfs_timer.h" +#include "pfs_setup_actor.h" +#include "pfs_setup_object.h" +#include "pfs_host.h" +#include "pfs_user.h" +#include "pfs_account.h" +#include "pfs_defaults.h" +#include "pfs_digest.h" +#include "pfs_program.h" +//#include "template_utils.h" +#include "pfs_prepared_stmt.h" + +PFS_global_param pfs_param; + +PFS_table_stat PFS_table_stat::g_reset_template; + +C_MODE_START +static void destroy_pfs_thread(void *key); +C_MODE_END + +static void cleanup_performance_schema(void); +void cleanup_instrument_config(void); + +void pre_initialize_performance_schema() +{ + pfs_initialized= false; + + init_all_builtin_memory_class(); + + PFS_table_stat::g_reset_template.reset(); + global_idle_stat.reset(); + global_table_io_stat.reset(); + global_table_lock_stat.reset(); + + if (my_create_thread_local_key(&THR_PFS, destroy_pfs_thread)) + return; + if (my_create_thread_local_key(&THR_PFS_VG, NULL)) // global_variables + return; + if (my_create_thread_local_key(&THR_PFS_SV, NULL)) // session_variables + return; + if (my_create_thread_local_key(&THR_PFS_VBT, NULL)) // variables_by_thread + return; + if (my_create_thread_local_key(&THR_PFS_SG, NULL)) // global_status + return; + if (my_create_thread_local_key(&THR_PFS_SS, NULL)) // session_status + return; + if (my_create_thread_local_key(&THR_PFS_SBT, NULL)) // status_by_thread + return; + if (my_create_thread_local_key(&THR_PFS_SBU, NULL)) // status_by_user + return; + if (my_create_thread_local_key(&THR_PFS_SBH, NULL)) // status_by_host + return; + if (my_create_thread_local_key(&THR_PFS_SBA, NULL)) // status_by_account + return; + + THR_PFS_initialized= true; +} + +struct PSI_bootstrap* +initialize_performance_schema(PFS_global_param *param) +{ + if (!THR_PFS_initialized) + { + /* Pre-initialization failed. */ + return NULL; + } + + pfs_enabled= param->m_enabled; + + pfs_automated_sizing(param); + init_timers(); + init_event_name_sizing(param); + register_global_classes(); + + if (init_sync_class(param->m_mutex_class_sizing, + param->m_rwlock_class_sizing, + param->m_cond_class_sizing) || + init_thread_class(param->m_thread_class_sizing) || + init_table_share(param->m_table_share_sizing) || + init_table_share_lock_stat(param->m_table_lock_stat_sizing) || + init_table_share_index_stat(param->m_index_stat_sizing) || + init_file_class(param->m_file_class_sizing) || + init_stage_class(param->m_stage_class_sizing) || + init_statement_class(param->m_statement_class_sizing) || + init_socket_class(param->m_socket_class_sizing) || + init_memory_class(param->m_memory_class_sizing) || + init_instruments(param) || + init_events_waits_history_long( + param->m_events_waits_history_long_sizing) || + init_events_stages_history_long( + param->m_events_stages_history_long_sizing) || + init_events_statements_history_long( + param->m_events_statements_history_long_sizing) || + init_events_transactions_history_long( + param->m_events_transactions_history_long_sizing) || + init_file_hash(param) || + init_table_share_hash(param) || + init_setup_actor(param) || + init_setup_actor_hash(param) || + init_setup_object(param) || + init_setup_object_hash(param) || + init_host(param) || + init_host_hash(param) || + init_user(param) || + init_user_hash(param) || + init_account(param) || + init_account_hash(param) || + init_digest(param) || + init_digest_hash(param) || + init_program(param) || + init_program_hash(param) || + init_prepared_stmt(param)) + { + /* + The performance schema initialization failed. + Free the memory used, and disable the instrumentation. + */ + cleanup_performance_schema(); + return NULL; + } + + if (param->m_enabled) + { + /** Default values for SETUP_CONSUMERS */ + flag_events_stages_current= param->m_consumer_events_stages_current_enabled; + flag_events_stages_history= param->m_consumer_events_stages_history_enabled; + flag_events_stages_history_long= param->m_consumer_events_stages_history_long_enabled; + flag_events_statements_current= param->m_consumer_events_statements_current_enabled; + flag_events_statements_history= param->m_consumer_events_statements_history_enabled; + flag_events_statements_history_long= param->m_consumer_events_statements_history_long_enabled; + flag_events_transactions_current= param->m_consumer_events_transactions_current_enabled; + flag_events_transactions_history= param->m_consumer_events_transactions_history_enabled; + flag_events_transactions_history_long= param->m_consumer_events_transactions_history_long_enabled; + flag_events_waits_current= param->m_consumer_events_waits_current_enabled; + flag_events_waits_history= param->m_consumer_events_waits_history_enabled; + flag_events_waits_history_long= param->m_consumer_events_waits_history_long_enabled; + flag_global_instrumentation= param->m_consumer_global_instrumentation_enabled; + flag_thread_instrumentation= param->m_consumer_thread_instrumentation_enabled; + flag_statements_digest= param->m_consumer_statement_digest_enabled; + } + else + { + flag_events_stages_current= false; + flag_events_stages_history= false; + flag_events_stages_history_long= false; + flag_events_statements_current= false; + flag_events_statements_history= false; + flag_events_statements_history_long= false; + flag_events_transactions_current= false; + flag_events_transactions_history= false; + flag_events_transactions_history_long= false; + flag_events_waits_current= false; + flag_events_waits_history= false; + flag_events_waits_history_long= false; + flag_global_instrumentation= false; + flag_thread_instrumentation= false; + flag_statements_digest= false; + } + + pfs_initialized= true; + + if (param->m_enabled) + { + install_default_setup(&PFS_bootstrap); + return &PFS_bootstrap; + } + + return NULL; +} + +static void destroy_pfs_thread(void *key) +{ + PFS_thread* pfs= reinterpret_cast<PFS_thread*> (key); + assert(pfs); + /* + This automatic cleanup is a last resort and best effort to avoid leaks, + and may not work on windows due to the implementation of pthread_key_create(). + Please either use: + - my_thread_end() + - or PSI_server->delete_current_thread() + in the instrumented code, to explicitly cleanup the instrumentation. + + Avoid invalid writes when the main() thread completes after shutdown: + the memory pointed by pfs is already released. + */ + if (pfs_initialized) + destroy_thread(pfs); +} + +static void cleanup_performance_schema(void) +{ + /* + my.cnf options + */ + + cleanup_instrument_config(); + + /* + All the LF_HASH + */ + + cleanup_setup_actor_hash(); + cleanup_setup_object_hash(); + cleanup_account_hash(); + cleanup_host_hash(); + cleanup_user_hash(); + cleanup_program_hash(); + cleanup_table_share_hash(); + cleanup_file_hash(); + cleanup_digest_hash(); + + /* + Then the lookup tables + */ + + cleanup_setup_actor(); + cleanup_setup_object(); + + /* + Then the history tables + */ + + cleanup_events_waits_history_long(); + cleanup_events_stages_history_long(); + cleanup_events_statements_history_long(); + cleanup_events_transactions_history_long(); + + /* + Then the various aggregations + */ + + cleanup_digest(); + cleanup_account(); + cleanup_host(); + cleanup_user(); + + /* + Then the instrument classes. + Once a class is cleaned up, + find_XXX_class(key) + will return PSI_NOT_INSTRUMENTED + */ + cleanup_program(); + cleanup_prepared_stmt(); + cleanup_sync_class(); + cleanup_thread_class(); + cleanup_table_share(); + cleanup_table_share_lock_stat(); + cleanup_table_share_index_stat(); + cleanup_file_class(); + cleanup_stage_class(); + cleanup_statement_class(); + cleanup_socket_class(); + cleanup_memory_class(); + + cleanup_instruments(); +} + +void shutdown_performance_schema(void) +{ + pfs_initialized= false; + + /* disable everything, especially for this thread. */ + flag_events_stages_current= false; + flag_events_stages_history= false; + flag_events_stages_history_long= false; + flag_events_statements_current= false; + flag_events_statements_history= false; + flag_events_statements_history_long= false; + flag_events_transactions_current= false; + flag_events_transactions_history= false; + flag_events_transactions_history_long= false; + flag_events_waits_current= false; + flag_events_waits_history= false; + flag_events_waits_history_long= false; + flag_global_instrumentation= false; + flag_thread_instrumentation= false; + flag_statements_digest= false; + + global_table_io_class.m_enabled= false; + global_table_lock_class.m_enabled= false; + global_idle_class.m_enabled= false; + global_metadata_class.m_enabled= false; + global_transaction_class.m_enabled= false; + + cleanup_performance_schema(); + /* + Be careful to not delete un-initialized keys, + this would affect key 0, which is THR_KEY_mysys, + */ + if (THR_PFS_initialized) + { + my_set_thread_local(THR_PFS, NULL); + my_set_thread_local(THR_PFS_VG, NULL); // global_variables + my_set_thread_local(THR_PFS_SV, NULL); // session_variables + my_set_thread_local(THR_PFS_VBT, NULL); // variables_by_thread + my_set_thread_local(THR_PFS_SG, NULL); // global_status + my_set_thread_local(THR_PFS_SS, NULL); // session_status + my_set_thread_local(THR_PFS_SBT, NULL); // status_by_thread + my_set_thread_local(THR_PFS_SBU, NULL); // status_by_user + my_set_thread_local(THR_PFS_SBH, NULL); // status_by_host + my_set_thread_local(THR_PFS_SBA, NULL); // status_by_account + + my_delete_thread_local_key(THR_PFS); + my_delete_thread_local_key(THR_PFS_VG); + my_delete_thread_local_key(THR_PFS_SV); + my_delete_thread_local_key(THR_PFS_VBT); + my_delete_thread_local_key(THR_PFS_SG); + my_delete_thread_local_key(THR_PFS_SS); + my_delete_thread_local_key(THR_PFS_SBT); + my_delete_thread_local_key(THR_PFS_SBU); + my_delete_thread_local_key(THR_PFS_SBH); + my_delete_thread_local_key(THR_PFS_SBA); + + THR_PFS_initialized= false; + } +} + +/** + Initialize the dynamic array used to hold PFS_INSTRUMENT configuration + options. +*/ +void init_pfs_instrument_array() +{ + pfs_instr_config_array= new Pfs_instr_config_array((PSI_memory_key)PSI_NOT_INSTRUMENTED); +} + +/** + Deallocate the PFS_INSTRUMENT array. +*/ +void cleanup_instrument_config() +{ + if (pfs_instr_config_array != NULL) + { + PFS_instr_config **it= pfs_instr_config_array->front(); + for ( ; it != pfs_instr_config_array->end(); it++) + my_free(*it); + } + delete pfs_instr_config_array; + pfs_instr_config_array= NULL; +} + +/** + Process one performance_schema_instrument configuration string. Isolate the + instrument name, evaluate the option value, and store them in a dynamic array. + Return 'false' for success, 'true' for error. + + @param name Instrument name + @param value Configuration option: 'on', 'off', etc. + @return 0 for success, non zero for errors +*/ + +int add_pfs_instr_to_array(const char* name, const char* value) +{ + size_t name_length= strlen(name); + size_t value_length= strlen(value); + + /* Allocate structure plus string buffers plus null terminators */ + PFS_instr_config* e = (PFS_instr_config*)my_malloc(PSI_NOT_INSTRUMENTED, + sizeof(PFS_instr_config) + + name_length + 1 + value_length + 1, MYF(MY_WME)); + if (!e) return 1; + + /* Copy the instrument name */ + e->m_name= (char*)e + sizeof(PFS_instr_config); + memcpy(e->m_name, name, name_length); + e->m_name_length= (uint)name_length; + e->m_name[name_length]= '\0'; + + /* Set flags accordingly */ + if (!my_strcasecmp(&my_charset_latin1, value, "counted")) + { + e->m_enabled= true; + e->m_timed= false; + } + else + if (!my_strcasecmp(&my_charset_latin1, value, "true") || + !my_strcasecmp(&my_charset_latin1, value, "on") || + !my_strcasecmp(&my_charset_latin1, value, "1") || + !my_strcasecmp(&my_charset_latin1, value, "yes")) + { + e->m_enabled= true; + e->m_timed= true; + } + else + if (!my_strcasecmp(&my_charset_latin1, value, "false") || + !my_strcasecmp(&my_charset_latin1, value, "off") || + !my_strcasecmp(&my_charset_latin1, value, "0") || + !my_strcasecmp(&my_charset_latin1, value, "no")) + { + e->m_enabled= false; + e->m_timed= false; + } + else + { + my_free(e); + return 1; + } + + /* Add to the array of default startup options */ + if (pfs_instr_config_array->push(e)) + { + my_free(e); + return 1; + } + + return 0; +} diff --git a/storage/perfschema/pfs_server.h b/storage/perfschema/pfs_server.h new file mode 100644 index 00000000..7c22812f --- /dev/null +++ b/storage/perfschema/pfs_server.h @@ -0,0 +1,309 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_SERVER_H +#define PFS_SERVER_H + +/** + @file storage/perfschema/pfs_server.h + Private interface for the server (declarations). +*/ + +#define PFS_AUTOSCALE_VALUE (-1) +#define PFS_AUTOSIZE_VALUE (-1) + +#ifndef PFS_MAX_MUTEX_CLASS + #define PFS_MAX_MUTEX_CLASS 210 +#endif +#ifndef PFS_MAX_RWLOCK_CLASS + #define PFS_MAX_RWLOCK_CLASS 50 +#endif +#ifndef PFS_MAX_COND_CLASS + #define PFS_MAX_COND_CLASS 90 +#endif +#ifndef PFS_MAX_THREAD_CLASS + #define PFS_MAX_THREAD_CLASS 50 +#endif +#ifndef PFS_MAX_FILE_CLASS + #define PFS_MAX_FILE_CLASS 80 +#endif +#ifndef PFS_MAX_FILE_HANDLE + #define PFS_MAX_FILE_HANDLE 32768 +#endif +#ifndef PFS_MAX_SOCKET_CLASS + #define PFS_MAX_SOCKET_CLASS 10 +#endif +#ifndef PFS_MAX_STAGE_CLASS + #define PFS_MAX_STAGE_CLASS 160 +#endif +#ifndef PFS_STATEMENTS_STACK_SIZE + #define PFS_STATEMENTS_STACK_SIZE 10 +#endif +#ifndef PFS_MAX_MEMORY_CLASS + #define PFS_MAX_MEMORY_CLASS 320 +#endif + +/** Sizing hints, from the server configuration. */ +struct PFS_sizing_hints +{ + /** Value of @c Sys_table_def_size */ + long m_table_definition_cache; + /** Value of @c Sys_table_cache_size */ + long m_table_open_cache; + /** Value of @c Sys_max_connections */ + long m_max_connections; + /** Value of @c Sys_open_files_limit */ + long m_open_files_limit; + /** Value of @c Sys_max_prepared_stmt_count */ + long m_max_prepared_stmt_count; +}; + +/** Performance schema global sizing parameters. */ +struct PFS_global_param +{ + /** True if the performance schema is enabled. */ + bool m_enabled; + /** Default values for SETUP_CONSUMERS. */ + bool m_consumer_events_stages_current_enabled; + bool m_consumer_events_stages_history_enabled; + bool m_consumer_events_stages_history_long_enabled; + bool m_consumer_events_statements_current_enabled; + bool m_consumer_events_statements_history_enabled; + bool m_consumer_events_statements_history_long_enabled; + bool m_consumer_events_transactions_current_enabled; + bool m_consumer_events_transactions_history_enabled; + bool m_consumer_events_transactions_history_long_enabled; + bool m_consumer_events_waits_current_enabled; + bool m_consumer_events_waits_history_enabled; + bool m_consumer_events_waits_history_long_enabled; + bool m_consumer_global_instrumentation_enabled; + bool m_consumer_thread_instrumentation_enabled; + bool m_consumer_statement_digest_enabled; + + /** Default instrument configuration option. */ + char *m_pfs_instrument; + + /** + Maximum number of instrumented mutex classes. + @sa mutex_class_lost. + */ + ulong m_mutex_class_sizing; + /** + Maximum number of instrumented rwlock classes. + @sa rwlock_class_lost. + */ + ulong m_rwlock_class_sizing; + /** + Maximum number of instrumented cond classes. + @sa cond_class_lost. + */ + ulong m_cond_class_sizing; + /** + Maximum number of instrumented thread classes. + @sa thread_class_lost. + */ + ulong m_thread_class_sizing; + /** + Maximum number of instrumented table share. + @sa table_share_lost. + */ + long m_table_share_sizing; + /** + Maximum number of lock statistics collected for tables. + @sa table_lock_stat_lost. + */ + long m_table_lock_stat_sizing; + /** + Maximum number of index statistics collected for tables. + @sa table_index_lost. + */ + long m_index_stat_sizing; + /** + Maximum number of instrumented file classes. + @sa file_class_lost. + */ + ulong m_file_class_sizing; + /** + Maximum number of instrumented mutex instances. + @sa mutex_lost. + */ + long m_mutex_sizing; + /** + Maximum number of instrumented rwlock instances. + @sa rwlock_lost. + */ + long m_rwlock_sizing; + /** + Maximum number of instrumented cond instances. + @sa cond_lost. + */ + long m_cond_sizing; + /** + Maximum number of instrumented thread instances. + @sa thread_lost. + */ + long m_thread_sizing; + /** + Maximum number of instrumented table handles. + @sa table_lost. + */ + long m_table_sizing; + /** + Maximum number of instrumented file instances. + @sa file_lost. + */ + long m_file_sizing; + /** + Maximum number of instrumented file handles. + @sa file_handle_lost. + */ + long m_file_handle_sizing; + /** + Maxium number of instrumented socket instances + @sa socket_lost + */ + long m_socket_sizing; + /** + Maximum number of instrumented socket classes. + @sa socket_class_lost. + */ + ulong m_socket_class_sizing; + /** Maximum number of rows per thread in table EVENTS_WAITS_HISTORY. */ + long m_events_waits_history_sizing; + /** Maximum number of rows in table EVENTS_WAITS_HISTORY_LONG. */ + long m_events_waits_history_long_sizing; + /** Maximum number of rows in table SETUP_ACTORS. */ + long m_setup_actor_sizing; + /** Maximum number of rows in table SETUP_OBJECTS. */ + long m_setup_object_sizing; + /** Maximum number of rows in table HOSTS. */ + long m_host_sizing; + /** Maximum number of rows in table USERS. */ + long m_user_sizing; + /** Maximum number of rows in table ACCOUNTS. */ + long m_account_sizing; + /** + Maximum number of instrumented stage classes. + @sa stage_class_lost. + */ + ulong m_stage_class_sizing; + /** Maximum number of rows per thread in table EVENTS_STAGES_HISTORY. */ + long m_events_stages_history_sizing; + /** Maximum number of rows in table EVENTS_STAGES_HISTORY_LONG. */ + long m_events_stages_history_long_sizing; + /** + Maximum number of instrumented statement classes. + @sa statement_class_lost. + */ + ulong m_statement_class_sizing; + /** Maximum number of rows per thread in table EVENTS_STATEMENTS_HISTORY. */ + long m_events_statements_history_sizing; + /** Maximum number of rows in table EVENTS_STATEMENTS_HISTORY_LONG. */ + long m_events_statements_history_long_sizing; + /** Maximum number of digests to be captured */ + long m_digest_sizing; + /** Maximum number of programs to be captured */ + long m_program_sizing; + /** Maximum number of prepared statements to be captured */ + long m_prepared_stmt_sizing; + /** Maximum number of rows per thread in table EVENTS_TRANSACTIONS_HISTORY. */ + long m_events_transactions_history_sizing; + /** Maximum number of rows in table EVENTS_TRANSACTIONS_HISTORY_LONG. */ + long m_events_transactions_history_long_sizing; + + /** Maximum number of session attribute strings per thread */ + long m_session_connect_attrs_sizing; + /** Maximum size of statement stack */ + ulong m_statement_stack_sizing; + + /** + Maximum number of instrumented memory classes. + @sa memory_class_lost. + */ + ulong m_memory_class_sizing; + + long m_metadata_lock_sizing; + + long m_max_digest_length; + ulong m_max_sql_text_length; + + /** Sizing hints, for auto tuning. */ + PFS_sizing_hints m_hints; +}; + +/** + Performance schema sizing values for the server. + This global variable is set when parsing server startup options. +*/ +extern PFS_global_param pfs_param; + +/** + Null initialization. + Disable all instrumentation, size all internal buffers to 0. + This pre initialization step is needed to ensure that events can be collected + and discarded, until such time @c initialize_performance_schema() is called. +*/ +void pre_initialize_performance_schema(); + +/** + Initialize the performance schema. + @param param Size parameters to use. + @return A bootstrap handle, or NULL. +*/ +struct PSI_bootstrap* +initialize_performance_schema(PFS_global_param *param); + +void pfs_automated_sizing(PFS_global_param *param); + +/** + Initialize the performance schema ACL. + ACL is strictly enforced when the server is running in normal mode, + to enforce that only legal operations are allowed. + When running in bootstrap mode, ACL restrictions are relaxed, + to allow the bootstrap scripts to DROP / CREATE performance schema tables. + @sa ACL_internal_schema_registry + @param bootstrap True if the server is starting in bootstrap mode. +*/ +void initialize_performance_schema_acl(bool bootstrap); + +/** + Reset the aggregated status counter stats. +*/ +void reset_pfs_status_stats(); + +/** + Initialize the dynamic array holding individual instrument settings collected + from the server configuration options. +*/ +void init_pfs_instrument_array(); + +/** + Process one PFS_INSTRUMENT configuration string. +*/ +int add_pfs_instr_to_array(const char* name, const char* value); + +/** + Shutdown the performance schema. +*/ +void shutdown_performance_schema(); + +#endif diff --git a/storage/perfschema/pfs_setup_actor.cc b/storage/perfschema/pfs_setup_actor.cc new file mode 100644 index 00000000..efe19b3c --- /dev/null +++ b/storage/perfschema/pfs_setup_actor.cc @@ -0,0 +1,342 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_setup_actor.cc + Performance schema setup actor (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "my_base.h" +#include "pfs.h" +#include "pfs_stat.h" +#include "pfs_instr.h" +#include "pfs_setup_actor.h" +#include "pfs_account.h" +#include "pfs_global.h" +#include "pfs_buffer_container.h" + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +/** Hash table for setup_actor records. */ +LF_HASH setup_actor_hash; +/** True if @c setup_actor_hash is initialized. */ +static bool setup_actor_hash_inited= false; + +/** + Initialize the setup actor buffers. + @param param sizing parameters + @return 0 on success +*/ +int init_setup_actor(const PFS_global_param *param) +{ + return global_setup_actor_container.init(param->m_setup_actor_sizing); +} + +/** Cleanup all the setup actor buffers. */ +void cleanup_setup_actor(void) +{ + global_setup_actor_container.cleanup(); +} + +C_MODE_START +static uchar *setup_actor_hash_get_key(const uchar *entry, size_t *length, + my_bool) +{ + const PFS_setup_actor * const *typed_entry; + const PFS_setup_actor *setup_actor; + const void *result; + typed_entry= reinterpret_cast<const PFS_setup_actor* const *> (entry); + assert(typed_entry != NULL); + setup_actor= *typed_entry; + assert(setup_actor != NULL); + *length= setup_actor->m_key.m_key_length; + result= setup_actor->m_key.m_hash_key; + return const_cast<uchar*> (reinterpret_cast<const uchar*> (result)); +} +C_MODE_END + +/** + Initialize the setup actor hash. + @return 0 on success +*/ +int init_setup_actor_hash(const PFS_global_param *param) +{ + if ((! setup_actor_hash_inited) && (param->m_setup_actor_sizing != 0)) + { + lf_hash_init(&setup_actor_hash, sizeof(PFS_setup_actor*), LF_HASH_UNIQUE, + 0, 0, setup_actor_hash_get_key, &my_charset_bin); + /* setup_actor_hash.size= param->m_setup_actor_sizing; */ + setup_actor_hash_inited= true; + } + return 0; +} + +/** Cleanup the setup actor hash. */ +void cleanup_setup_actor_hash(void) +{ + if (setup_actor_hash_inited) + { + lf_hash_destroy(&setup_actor_hash); + setup_actor_hash_inited= false; + } +} + +static LF_PINS* get_setup_actor_hash_pins(PFS_thread *thread) +{ + if (unlikely(thread->m_setup_actor_hash_pins == NULL)) + { + if (! setup_actor_hash_inited) + return NULL; + thread->m_setup_actor_hash_pins= lf_hash_get_pins(&setup_actor_hash); + } + return thread->m_setup_actor_hash_pins; +} + +static void set_setup_actor_key(PFS_setup_actor_key *key, + const char *user, uint user_length, + const char *host, uint host_length, + const char *role, uint role_length) +{ + assert(user_length <= USERNAME_LENGTH); + assert(host_length <= HOSTNAME_LENGTH); + + char *ptr= &key->m_hash_key[0]; + memcpy(ptr, user, user_length); + ptr+= user_length; + ptr[0]= 0; + ptr++; + memcpy(ptr, host, host_length); + ptr+= host_length; + ptr[0]= 0; + ptr++; + memcpy(ptr, role, role_length); + ptr+= role_length; + ptr[0]= 0; + ptr++; + key->m_key_length= (uint)(ptr - &key->m_hash_key[0]); +} + +int insert_setup_actor(const String *user, const String *host, const String *role, + bool enabled, bool history) +{ + PFS_thread *thread= PFS_thread::get_current_thread(); + if (unlikely(thread == NULL)) + return HA_ERR_OUT_OF_MEM; + + LF_PINS *pins= get_setup_actor_hash_pins(thread); + if (unlikely(pins == NULL)) + return HA_ERR_OUT_OF_MEM; + + PFS_setup_actor *pfs; + pfs_dirty_state dirty_state; + + pfs= global_setup_actor_container.allocate(& dirty_state); + if (pfs != NULL) + { + set_setup_actor_key(&pfs->m_key, + user->ptr(), user->length(), + host->ptr(), host->length(), + role->ptr(), role->length()); + pfs->m_username= &pfs->m_key.m_hash_key[0]; + pfs->m_username_length= user->length(); + pfs->m_hostname= pfs->m_username + pfs->m_username_length + 1; + pfs->m_hostname_length= host->length(); + pfs->m_rolename= pfs->m_hostname + pfs->m_hostname_length + 1; + pfs->m_rolename_length= role->length(); + pfs->m_enabled= enabled; + pfs->m_history= history; + + int res; + pfs->m_lock.dirty_to_allocated(& dirty_state); + res= lf_hash_insert(&setup_actor_hash, pins, &pfs); + if (likely(res == 0)) + { + update_setup_actors_derived_flags(); + return 0; + } + + global_setup_actor_container.deallocate(pfs); + + if (res > 0) + return HA_ERR_FOUND_DUPP_KEY; + return HA_ERR_OUT_OF_MEM; + } + + return HA_ERR_RECORD_FILE_FULL; +} + +int delete_setup_actor(const String *user, const String *host, const String *role) +{ + PFS_thread *thread= PFS_thread::get_current_thread(); + if (unlikely(thread == NULL)) + return HA_ERR_OUT_OF_MEM; + + LF_PINS* pins= get_setup_actor_hash_pins(thread); + if (unlikely(pins == NULL)) + return HA_ERR_OUT_OF_MEM; + + PFS_setup_actor_key key; + set_setup_actor_key(&key, + user->ptr(), user->length(), + host->ptr(), host->length(), + role->ptr(), role->length()); + + PFS_setup_actor **entry; + entry= reinterpret_cast<PFS_setup_actor**> + (lf_hash_search(&setup_actor_hash, pins, key.m_hash_key, key.m_key_length)); + + if (entry && (entry != MY_ERRPTR)) + { + PFS_setup_actor *pfs= *entry; + lf_hash_delete(&setup_actor_hash, pins, key.m_hash_key, key.m_key_length); + global_setup_actor_container.deallocate(pfs); + } + + lf_hash_search_unpin(pins); + + update_setup_actors_derived_flags(); + + return 0; +} + +class Proc_reset_setup_actor + : public PFS_buffer_processor<PFS_setup_actor> +{ +public: + Proc_reset_setup_actor(LF_PINS* pins) + : m_pins(pins) + {} + + virtual void operator()(PFS_setup_actor *pfs) + { + lf_hash_delete(&setup_actor_hash, m_pins, + pfs->m_key.m_hash_key, pfs->m_key.m_key_length); + + global_setup_actor_container.deallocate(pfs); + } + +private: + LF_PINS* m_pins; +}; + +int reset_setup_actor() +{ + PFS_thread *thread= PFS_thread::get_current_thread(); + if (unlikely(thread == NULL)) + return HA_ERR_OUT_OF_MEM; + + LF_PINS* pins= get_setup_actor_hash_pins(thread); + if (unlikely(pins == NULL)) + return HA_ERR_OUT_OF_MEM; + + Proc_reset_setup_actor proc(pins); + // FIXME: delete helper instead + global_setup_actor_container.apply(proc); + + update_setup_actors_derived_flags(); + + return 0; +} + +long setup_actor_count() +{ + return setup_actor_hash.count; +} + +/* + - '%' should be replaced by NULL in table SETUP_ACTOR + - add an ENABLED column to include/exclude patterns, more flexible + - the principle is similar to SETUP_OBJECTS +*/ +void lookup_setup_actor(PFS_thread *thread, + const char *user, uint user_length, + const char *host, uint host_length, + bool *enabled, bool *history) +{ + PFS_setup_actor_key key; + PFS_setup_actor **entry; + int i; + + LF_PINS* pins= get_setup_actor_hash_pins(thread); + if (unlikely(pins == NULL)) + { + *enabled= false; + *history= false; + return; + } + + for (i= 1; i<=4; i++) + { + /* + WL#988 Roles is not implemented, so we do not have a role name. + Looking up "%" in SETUP_ACTORS.ROLE. + */ + switch(i) + { + case 1: + set_setup_actor_key(&key, user, user_length, host, host_length, "%", 1); + break; + case 2: + set_setup_actor_key(&key, user, user_length, "%", 1, "%", 1); + break; + case 3: + set_setup_actor_key(&key, "%", 1, host, host_length, "%", 1); + break; + case 4: + set_setup_actor_key(&key, "%", 1, "%", 1, "%", 1); + break; + } + entry= reinterpret_cast<PFS_setup_actor**> + (lf_hash_search(&setup_actor_hash, pins, key.m_hash_key, key.m_key_length)); + + if (entry && (entry != MY_ERRPTR)) + { + PFS_setup_actor *pfs= *entry; + lf_hash_search_unpin(pins); + *enabled= pfs->m_enabled; + *history= pfs->m_history; + return; + } + + lf_hash_search_unpin(pins); + } + *enabled= false; + *history= false; + return; +} + +int update_setup_actors_derived_flags() +{ + PFS_thread *thread= PFS_thread::get_current_thread(); + if (unlikely(thread == NULL)) + return HA_ERR_OUT_OF_MEM; + + update_accounts_derived_flags(thread); + return 0; +} + +/** @} */ diff --git a/storage/perfschema/pfs_setup_actor.h b/storage/perfschema/pfs_setup_actor.h new file mode 100644 index 00000000..8e401dcd --- /dev/null +++ b/storage/perfschema/pfs_setup_actor.h @@ -0,0 +1,112 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_SETUP_ACTOR_H +#define PFS_SETUP_ACTOR_H + +/** + @file storage/perfschema/pfs_setup_actor.h + Performance schema setup actors (declarations). +*/ + +#include "sql_string.h" +#include "pfs_lock.h" +#include "lf.h" + +struct PFS_global_param; +class PFS_opaque_container_page; + +/* WL#988 Roles Not implemented yet */ +#define ROLENAME_LENGTH 64 + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +/** Hash key for @sa PFS_setup_actor. */ +struct PFS_setup_actor_key +{ + /** + Hash search key. + This has to be a string for LF_HASH, + the format is "<username><0x00><hostname><0x00><rolename><0x00>" + */ + char m_hash_key[USERNAME_LENGTH + 1 + HOSTNAME_LENGTH + 1 + ROLENAME_LENGTH + 1]; + /** Length of @c m_hash_key. */ + uint m_key_length; +}; + +/** A setup_actor record. */ +struct PFS_ALIGNED PFS_setup_actor +{ + /** Internal lock. */ + pfs_lock m_lock; + /** Hash key. */ + PFS_setup_actor_key m_key; + /** User name. This points inside the hash key. */ + const char *m_username; + /** Length of @c m_username. */ + uint m_username_length; + /** Host name. This points inside the hash key. */ + const char *m_hostname; + /** Length of @c m_hostname. */ + uint m_hostname_length; + /** Role name. This points inside the hash key. */ + const char *m_rolename; + /** Length of @c m_rolename. */ + uint m_rolename_length; + /** ENABLED flag. */ + bool m_enabled; + /** HISTORY flag. */ + bool m_history; + /** Container page. */ + PFS_opaque_container_page *m_page; +}; + +int init_setup_actor(const PFS_global_param *param); +void cleanup_setup_actor(void); +int init_setup_actor_hash(const PFS_global_param *param); +void cleanup_setup_actor_hash(void); + +int insert_setup_actor(const String *user, const String *host, + const String *role, bool enabled, bool history); +int delete_setup_actor(const String *user, const String *host, + const String *role); +int reset_setup_actor(void); +long setup_actor_count(void); + +void lookup_setup_actor(PFS_thread *thread, + const char *user, uint user_length, + const char *host, uint host_length, + bool *enabled, bool *history); + +/** Update derived flags for all setup_actors. */ +int update_setup_actors_derived_flags(); + +/* For show status. */ + +extern LF_HASH setup_actor_hash; + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_setup_object.cc b/storage/perfschema/pfs_setup_object.cc new file mode 100644 index 00000000..78617d1b --- /dev/null +++ b/storage/perfschema/pfs_setup_object.cc @@ -0,0 +1,338 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_setup_object.cc + Performance schema setup object (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "my_base.h" +#include "sql_string.h" +#include "pfs.h" +#include "pfs_stat.h" +#include "pfs_instr.h" +#include "pfs_setup_object.h" +#include "pfs_global.h" +#include "pfs_buffer_container.h" + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +uint setup_objects_version= 0; + +LF_HASH setup_object_hash; +static bool setup_object_hash_inited= false; + +/** + Initialize the setup object buffers. + @param param sizing parameters + @return 0 on success +*/ +int init_setup_object(const PFS_global_param *param) +{ + return global_setup_object_container.init(param->m_setup_object_sizing); +} + +/** Cleanup all the setup object buffers. */ +void cleanup_setup_object(void) +{ + global_setup_object_container.cleanup(); +} + +C_MODE_START +static uchar *setup_object_hash_get_key(const uchar *entry, size_t *length, + my_bool) +{ + const PFS_setup_object * const *typed_entry; + const PFS_setup_object *setup_object; + const void *result; + typed_entry= reinterpret_cast<const PFS_setup_object* const *> (entry); + assert(typed_entry != NULL); + setup_object= *typed_entry; + assert(setup_object != NULL); + *length= setup_object->m_key.m_key_length; + result= setup_object->m_key.m_hash_key; + return const_cast<uchar*> (reinterpret_cast<const uchar*> (result)); +} +C_MODE_END + +/** + Initialize the setup objects hash. + @return 0 on success +*/ +int init_setup_object_hash(const PFS_global_param *param) +{ + if ((! setup_object_hash_inited) && (param->m_setup_object_sizing != 0)) + { + lf_hash_init(&setup_object_hash, sizeof(PFS_setup_object*), LF_HASH_UNIQUE, + 0, 0, setup_object_hash_get_key, &my_charset_bin); + setup_object_hash_inited= true; + } + return 0; +} + +/** Cleanup the setup objects hash. */ +void cleanup_setup_object_hash(void) +{ + if (setup_object_hash_inited) + { + setup_object_hash_inited= false; + lf_hash_destroy(&setup_object_hash); + } +} + +static LF_PINS* get_setup_object_hash_pins(PFS_thread *thread) +{ + if (unlikely(thread->m_setup_object_hash_pins == NULL)) + { + if (! setup_object_hash_inited) + return NULL; + thread->m_setup_object_hash_pins= lf_hash_get_pins(&setup_object_hash); + } + return thread->m_setup_object_hash_pins; +} + +static void set_setup_object_key(PFS_setup_object_key *key, + enum_object_type object_type, + const char *schema, uint schema_length, + const char *object, uint object_length) +{ + assert(schema_length <= NAME_LEN); + assert(object_length <= NAME_LEN); + + char *ptr= &key->m_hash_key[0]; + ptr[0]= (char) object_type; + ptr++; + if (schema_length) + { + memcpy(ptr, schema, schema_length); + ptr+= schema_length; + } + ptr[0]= 0; + ptr++; + if (object_length) + { + memcpy(ptr, object, object_length); + ptr+= object_length; + } + ptr[0]= 0; + ptr++; + key->m_key_length= (uint)(ptr - &key->m_hash_key[0]); +} + +int insert_setup_object(enum_object_type object_type, const String *schema, + const String *object, bool enabled, bool timed) +{ + PFS_thread *thread= PFS_thread::get_current_thread(); + if (unlikely(thread == NULL)) + return HA_ERR_OUT_OF_MEM; + + LF_PINS* pins= get_setup_object_hash_pins(thread); + if (unlikely(pins == NULL)) + return HA_ERR_OUT_OF_MEM; + + PFS_setup_object *pfs; + pfs_dirty_state dirty_state; + + pfs= global_setup_object_container.allocate(& dirty_state); + if (pfs != NULL) + { + set_setup_object_key(&pfs->m_key, object_type, + schema->ptr(), schema->length(), + object->ptr(), object->length()); + pfs->m_schema_name= &pfs->m_key.m_hash_key[1]; + pfs->m_schema_name_length= schema->length(); + pfs->m_object_name= pfs->m_schema_name + pfs->m_schema_name_length + 1; + pfs->m_object_name_length= object->length(); + pfs->m_enabled= enabled; + pfs->m_timed= timed; + + int res; + pfs->m_lock.dirty_to_allocated(& dirty_state); + res= lf_hash_insert(&setup_object_hash, pins, &pfs); + if (likely(res == 0)) + { + setup_objects_version++; + return 0; + } + + global_setup_object_container.deallocate(pfs); + + if (res > 0) + return HA_ERR_FOUND_DUPP_KEY; + /* OOM in lf_hash_insert */ + return HA_ERR_OUT_OF_MEM; + } + + return HA_ERR_RECORD_FILE_FULL; +} + +int delete_setup_object(enum_object_type object_type, const String *schema, + const String *object) +{ + PFS_thread *thread= PFS_thread::get_current_thread(); + if (unlikely(thread == NULL)) + return HA_ERR_OUT_OF_MEM; + + LF_PINS* pins= get_setup_object_hash_pins(thread); + if (unlikely(pins == NULL)) + return HA_ERR_OUT_OF_MEM; + + PFS_setup_object_key key; + set_setup_object_key(&key, object_type, + schema->ptr(), schema->length(), + object->ptr(), object->length()); + + PFS_setup_object **entry; + entry= reinterpret_cast<PFS_setup_object**> + (lf_hash_search(&setup_object_hash, pins, key.m_hash_key, key.m_key_length)); + + if (entry && (entry != MY_ERRPTR)) + { + PFS_setup_object *pfs= *entry; + lf_hash_delete(&setup_object_hash, pins, key.m_hash_key, key.m_key_length); + global_setup_object_container.deallocate(pfs); + } + + lf_hash_search_unpin(pins); + + setup_objects_version++; + return 0; +} + +class Proc_reset_setup_object + : public PFS_buffer_processor<PFS_setup_object> +{ +public: + Proc_reset_setup_object(LF_PINS* pins) + : m_pins(pins) + {} + + virtual void operator()(PFS_setup_object *pfs) + { + lf_hash_delete(&setup_object_hash, m_pins, + pfs->m_key.m_hash_key, pfs->m_key.m_key_length); + + global_setup_object_container.deallocate(pfs); + } + +private: + LF_PINS* m_pins; +}; + +int reset_setup_object() +{ + PFS_thread *thread= PFS_thread::get_current_thread(); + if (unlikely(thread == NULL)) + return HA_ERR_OUT_OF_MEM; + + LF_PINS* pins= get_setup_object_hash_pins(thread); + if (unlikely(pins == NULL)) + return HA_ERR_OUT_OF_MEM; + + Proc_reset_setup_object proc(pins); + // FIXME: delete helper instead + global_setup_object_container.apply(proc); + + setup_objects_version++; + return 0; +} + +long setup_object_count() +{ + return setup_object_hash.count; +} + +void lookup_setup_object(PFS_thread *thread, + enum_object_type object_type, + const char *schema_name, int schema_name_length, + const char *object_name, int object_name_length, + bool *enabled, bool *timed) +{ + PFS_setup_object_key key; + PFS_setup_object **entry; + PFS_setup_object *pfs; + int i; + + /* + The table io instrumentation uses "TABLE" and "TEMPORARY TABLE". + SETUP_OBJECT uses "TABLE" for both concepts. + There is no way to provide a different setup for: + - TABLE foo.bar + - TEMPORARY TABLE foo.bar + */ + assert(object_type != OBJECT_TYPE_TEMPORARY_TABLE); + + LF_PINS* pins= get_setup_object_hash_pins(thread); + if (unlikely(pins == NULL)) + { + *enabled= false; + *timed= false; + return; + } + + for (i= 1; i<=3; i++) + { + switch(i) + { + case 1: + /* Lookup OBJECT_TYPE + OBJECT_SCHEMA + OBJECT_NAME in SETUP_OBJECTS */ + set_setup_object_key(&key, + object_type, + schema_name, schema_name_length, + object_name, object_name_length); + break; + case 2: + /* Lookup OBJECT_TYPE + OBJECT_SCHEMA + "%" in SETUP_OBJECTS */ + set_setup_object_key(&key, + object_type, + schema_name, schema_name_length, "%", 1); + break; + case 3: + /* Lookup OBJECT_TYPE + "%" + "%" in SETUP_OBJECTS */ + set_setup_object_key(&key, object_type, "%", 1, "%", 1); + break; + } + entry= reinterpret_cast<PFS_setup_object**> + (lf_hash_search(&setup_object_hash, pins, key.m_hash_key, key.m_key_length)); + + if (entry && (entry != MY_ERRPTR)) + { + pfs= *entry; + *enabled= pfs->m_enabled; + *timed= pfs->m_timed; + lf_hash_search_unpin(pins); + return; + } + + lf_hash_search_unpin(pins); + } + *enabled= false; + *timed= false; + return; +} + +/** @} */ diff --git a/storage/perfschema/pfs_setup_object.h b/storage/perfschema/pfs_setup_object.h new file mode 100644 index 00000000..e934e01e --- /dev/null +++ b/storage/perfschema/pfs_setup_object.h @@ -0,0 +1,107 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_SETUP_OBJECT_H +#define PFS_SETUP_OBJECT_H + +/** + @file storage/perfschema/pfs_setup_object.h + Performance schema setup object (declarations). +*/ + +#include "pfs_lock.h" +#include "lf.h" + +class String; +struct PFS_global_param; +class PFS_opaque_container_page; + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +/** Hash key for @sa PFS_setup_object. */ +struct PFS_setup_object_key +{ + /** + Hash search key. + This has to be a string for LF_HASH, + the format is "<enum_object_type><schema_name><0x00><object_name><0x00>" + */ + char m_hash_key[1 + NAME_LEN + 1 + NAME_LEN + 1]; + uint m_key_length; +}; + +/** A setup_object record. */ +struct PFS_ALIGNED PFS_setup_object +{ + enum_object_type get_object_type() + { + return (enum_object_type) m_key.m_hash_key[0]; + } + + /** Internal lock. */ + pfs_lock m_lock; + /** Hash key. */ + PFS_setup_object_key m_key; + /** Schema name. Points inside m_key. */ + const char *m_schema_name; + /** Length of @c m_schema_name. */ + uint m_schema_name_length; + /** Object name. Points inside m_key. */ + const char *m_object_name; + /** Length of @c m_object_name. */ + uint m_object_name_length; + /** ENABLED flag. */ + bool m_enabled; + /** TIMED flag. */ + bool m_timed; + /** Container page. */ + PFS_opaque_container_page *m_page; +}; + +int init_setup_object(const PFS_global_param *param); +void cleanup_setup_object(void); +int init_setup_object_hash(const PFS_global_param *param); +void cleanup_setup_object_hash(void); + +int insert_setup_object(enum_object_type object_type, const String *schema, + const String *object, bool enabled, bool timed); +int delete_setup_object(enum_object_type object_type, const String *schema, + const String *object); +int reset_setup_object(void); +long setup_object_count(void); + +void lookup_setup_object(PFS_thread *thread, + enum_object_type object_type, + const char *schema_name, int schema_name_length, + const char *object_name, int object_name_length, + bool *enabled, bool *timed); + +/* For show status. */ + +extern LF_HASH setup_object_hash; + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_stat.h b/storage/perfschema/pfs_stat.h new file mode 100644 index 00000000..af0be085 --- /dev/null +++ b/storage/perfschema/pfs_stat.h @@ -0,0 +1,1332 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_STAT_H +#define PFS_STAT_H + +#include <algorithm> +#include "sql_const.h" +/* memcpy */ +#include "string.h" + +/** + @file storage/perfschema/pfs_stat.h + Statistics (declarations). +*/ + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +/** Single statistic. */ +struct PFS_single_stat +{ + /** Count of values. */ + ulonglong m_count; + /** Sum of values. */ + ulonglong m_sum; + /** Minimum value. */ + ulonglong m_min; + /** Maximum value. */ + ulonglong m_max; + + PFS_single_stat() + { + m_count= 0; + m_sum= 0; + m_min= ULLONG_MAX; + m_max= 0; + } + + inline void reset(void) + { + m_count= 0; + m_sum= 0; + m_min= ULLONG_MAX; + m_max= 0; + } + + inline bool has_timed_stats() const + { + return (m_min <= m_max); + } + + inline void aggregate(const PFS_single_stat *stat) + { + if (stat->m_count != 0) + { + m_count+= stat->m_count; + m_sum+= stat->m_sum; + if (unlikely(m_min > stat->m_min)) + m_min= stat->m_min; + if (unlikely(m_max < stat->m_max)) + m_max= stat->m_max; + } + } + + inline void aggregate_no_check(const PFS_single_stat *stat) + { + m_count+= stat->m_count; + m_sum+= stat->m_sum; + if (unlikely(m_min > stat->m_min)) + m_min= stat->m_min; + if (unlikely(m_max < stat->m_max)) + m_max= stat->m_max; + } + + inline void aggregate_counted() + { + m_count++; + } + + inline void aggregate_counted(ulonglong count) + { + m_count+= count; + } + + inline void aggregate_value(ulonglong value) + { + m_count++; + m_sum+= value; + if (unlikely(m_min > value)) + m_min= value; + if (unlikely(m_max < value)) + m_max= value; + } + + inline void aggregate_many_value(ulonglong value, ulonglong count) + { + m_count+= count; + m_sum+= value; + if (unlikely(m_min > value)) + m_min= value; + if (unlikely(m_max < value)) + m_max= value; + } +}; + +/** Combined statistic. */ +struct PFS_byte_stat : public PFS_single_stat +{ + /** Byte count statistics */ + ulonglong m_bytes; + + /** Aggregate wait stats, event count and byte count */ + inline void aggregate(const PFS_byte_stat *stat) + { + if (stat->m_count != 0) + { + PFS_single_stat::aggregate_no_check(stat); + m_bytes+= stat->m_bytes; + } + } + + /** Aggregate wait stats, event count and byte count */ + inline void aggregate_no_check(const PFS_byte_stat *stat) + { + PFS_single_stat::aggregate_no_check(stat); + m_bytes+= stat->m_bytes; + } + + /** Aggregate individual wait time, event count and byte count */ + inline void aggregate(ulonglong wait, ulonglong bytes) + { + aggregate_value(wait); + m_bytes+= bytes; + } + + /** Aggregate wait stats and event count */ + inline void aggregate_waits(const PFS_byte_stat *stat) + { + PFS_single_stat::aggregate(stat); + } + + /** Aggregate event count and byte count */ + inline void aggregate_counted() + { + PFS_single_stat::aggregate_counted(); + } + + /** Aggregate event count and byte count */ + inline void aggregate_counted(ulonglong bytes) + { + PFS_single_stat::aggregate_counted(); + m_bytes+= bytes; + } + + PFS_byte_stat() + { + reset(); + } + + inline void reset(void) + { + PFS_single_stat::reset(); + m_bytes= 0; + } +}; + +/** Statistics for mutex usage. */ +struct PFS_mutex_stat +{ + /** Wait statistics. */ + PFS_single_stat m_wait_stat; +#ifdef PFS_LATER + /** + Lock statistics. + This statistic is not exposed in user visible tables yet. + */ + PFS_single_stat m_lock_stat; +#endif + + inline void aggregate(const PFS_mutex_stat *stat) + { + m_wait_stat.aggregate(&stat->m_wait_stat); +#ifdef PFS_LATER + m_lock_stat.aggregate(&stat->m_lock_stat); +#endif + } + + inline void reset(void) + { + m_wait_stat.reset(); +#ifdef PFS_LATER + m_lock_stat.reset(); +#endif + } +}; + +/** Statistics for rwlock usage. */ +struct PFS_rwlock_stat +{ + /** Wait statistics. */ + PFS_single_stat m_wait_stat; +#ifdef PFS_LATER + /** + RWLock read lock usage statistics. + This statistic is not exposed in user visible tables yet. + */ + PFS_single_stat m_read_lock_stat; + /** + RWLock write lock usage statistics. + This statistic is not exposed in user visible tables yet. + */ + PFS_single_stat m_write_lock_stat; +#endif + + inline void aggregate(const PFS_rwlock_stat *stat) + { + m_wait_stat.aggregate(&stat->m_wait_stat); +#ifdef PFS_LATER + m_read_lock_stat.aggregate(&stat->m_read_lock_stat); + m_write_lock_stat.aggregate(&stat->m_write_lock_stat); +#endif + } + + inline void reset(void) + { + m_wait_stat.reset(); +#ifdef PFS_LATER + m_read_lock_stat.reset(); + m_write_lock_stat.reset(); +#endif + } +}; + +/** Statistics for COND usage. */ +struct PFS_cond_stat +{ + /** Wait statistics. */ + PFS_single_stat m_wait_stat; +#ifdef PFS_LATER + /** + Number of times a condition was signalled. + This statistic is not exposed in user visible tables yet. + */ + ulonglong m_signal_count; + /** + Number of times a condition was broadcast. + This statistic is not exposed in user visible tables yet. + */ + ulonglong m_broadcast_count; +#endif + + inline void aggregate(const PFS_cond_stat *stat) + { + m_wait_stat.aggregate(&stat->m_wait_stat); +#ifdef PFS_LATER + m_signal_count+= stat->m_signal_count; + m_broadcast_count+= stat->m_broadcast_count; +#endif + } + + inline void reset(void) + { + m_wait_stat.reset(); +#ifdef PFS_LATER + m_signal_count= 0; + m_broadcast_count= 0; +#endif + } +}; + +/** Statistics for FILE IO. Used for both waits and byte counts. */ +struct PFS_file_io_stat +{ + /** READ statistics */ + PFS_byte_stat m_read; + /** WRITE statistics */ + PFS_byte_stat m_write; + /** Miscellaneous statistics */ + PFS_byte_stat m_misc; + + inline void reset(void) + { + m_read.reset(); + m_write.reset(); + m_misc.reset(); + } + + inline void aggregate(const PFS_file_io_stat *stat) + { + m_read.aggregate(&stat->m_read); + m_write.aggregate(&stat->m_write); + m_misc.aggregate(&stat->m_misc); + } + + /* Sum waits and byte counts */ + inline void sum(PFS_byte_stat *stat) + { + stat->aggregate(&m_read); + stat->aggregate(&m_write); + stat->aggregate(&m_misc); + } + + /* Sum waits only */ + inline void sum_waits(PFS_single_stat *stat) + { + stat->aggregate(&m_read); + stat->aggregate(&m_write); + stat->aggregate(&m_misc); + } +}; + +/** Statistics for FILE usage. */ +struct PFS_file_stat +{ + /** Number of current open handles. */ + ulong m_open_count; + /** File IO statistics. */ + PFS_file_io_stat m_io_stat; + + inline void aggregate(const PFS_file_stat *stat) + { + m_io_stat.aggregate(&stat->m_io_stat); + } + + /** Reset file statistics. */ + inline void reset(void) + { + m_io_stat.reset(); + } +}; + +/** Statistics for stage usage. */ +struct PFS_stage_stat +{ + PFS_single_stat m_timer1_stat; + + inline void reset(void) + { m_timer1_stat.reset(); } + + inline void aggregate_counted() + { m_timer1_stat.aggregate_counted(); } + + inline void aggregate_value(ulonglong value) + { m_timer1_stat.aggregate_value(value); } + + inline void aggregate(const PFS_stage_stat *stat) + { m_timer1_stat.aggregate(& stat->m_timer1_stat); } +}; + +/** Statistics for stored program usage. */ +struct PFS_sp_stat +{ + PFS_single_stat m_timer1_stat; + + inline void reset(void) + { m_timer1_stat.reset(); } + + inline void aggregate_counted() + { m_timer1_stat.aggregate_counted(); } + + inline void aggregate_value(ulonglong value) + { m_timer1_stat.aggregate_value(value); } + + inline void aggregate(const PFS_stage_stat *stat) + { m_timer1_stat.aggregate(& stat->m_timer1_stat); } +}; + +/** Statistics for prepared statement usage. */ +struct PFS_prepared_stmt_stat +{ + PFS_single_stat m_timer1_stat; + + inline void reset(void) + { m_timer1_stat.reset(); } + + inline void aggregate_counted() + { m_timer1_stat.aggregate_counted(); } + + inline void aggregate_value(ulonglong value) + { m_timer1_stat.aggregate_value(value); } + + inline void aggregate(PFS_stage_stat *stat) + { m_timer1_stat.aggregate(& stat->m_timer1_stat); } +}; + +/** + Statistics for statement usage. + This structure uses lazy initialization, + controlled by member @c m_timer1_stat.m_count. +*/ +struct PFS_statement_stat +{ + PFS_single_stat m_timer1_stat; + ulonglong m_error_count; + ulonglong m_warning_count; + ulonglong m_rows_affected; + ulonglong m_lock_time; + ulonglong m_rows_sent; + ulonglong m_rows_examined; + ulonglong m_created_tmp_disk_tables; + ulonglong m_created_tmp_tables; + ulonglong m_select_full_join; + ulonglong m_select_full_range_join; + ulonglong m_select_range; + ulonglong m_select_range_check; + ulonglong m_select_scan; + ulonglong m_sort_merge_passes; + ulonglong m_sort_range; + ulonglong m_sort_rows; + ulonglong m_sort_scan; + ulonglong m_no_index_used; + ulonglong m_no_good_index_used; + + PFS_statement_stat() + { + reset(); + } + + inline void reset() + { + m_timer1_stat.m_count= 0; + } + + inline void mark_used() + { + delayed_reset(); + } + +private: + inline void delayed_reset(void) + { + if (m_timer1_stat.m_count == 0) + { + m_timer1_stat.reset(); + m_error_count= 0; + m_warning_count= 0; + m_rows_affected= 0; + m_lock_time= 0; + m_rows_sent= 0; + m_rows_examined= 0; + m_created_tmp_disk_tables= 0; + m_created_tmp_tables= 0; + m_select_full_join= 0; + m_select_full_range_join= 0; + m_select_range= 0; + m_select_range_check= 0; + m_select_scan= 0; + m_sort_merge_passes= 0; + m_sort_range= 0; + m_sort_rows= 0; + m_sort_scan= 0; + m_no_index_used= 0; + m_no_good_index_used= 0; + } + } + +public: + inline void aggregate_counted() + { + delayed_reset(); + m_timer1_stat.aggregate_counted(); + } + + inline void aggregate_value(ulonglong value) + { + delayed_reset(); + m_timer1_stat.aggregate_value(value); + } + + inline void aggregate(const PFS_statement_stat *stat) + { + if (stat->m_timer1_stat.m_count != 0) + { + delayed_reset(); + m_timer1_stat.aggregate_no_check(& stat->m_timer1_stat); + + m_error_count+= stat->m_error_count; + m_warning_count+= stat->m_warning_count; + m_rows_affected+= stat->m_rows_affected; + m_lock_time+= stat->m_lock_time; + m_rows_sent+= stat->m_rows_sent; + m_rows_examined+= stat->m_rows_examined; + m_created_tmp_disk_tables+= stat->m_created_tmp_disk_tables; + m_created_tmp_tables+= stat->m_created_tmp_tables; + m_select_full_join+= stat->m_select_full_join; + m_select_full_range_join+= stat->m_select_full_range_join; + m_select_range+= stat->m_select_range; + m_select_range_check+= stat->m_select_range_check; + m_select_scan+= stat->m_select_scan; + m_sort_merge_passes+= stat->m_sort_merge_passes; + m_sort_range+= stat->m_sort_range; + m_sort_rows+= stat->m_sort_rows; + m_sort_scan+= stat->m_sort_scan; + m_no_index_used+= stat->m_no_index_used; + m_no_good_index_used+= stat->m_no_good_index_used; + } + } +}; + +/** Statistics for transaction usage. */ +struct PFS_transaction_stat +{ + PFS_single_stat m_read_write_stat; + PFS_single_stat m_read_only_stat; + + ulonglong m_savepoint_count; + ulonglong m_rollback_to_savepoint_count; + ulonglong m_release_savepoint_count; + + PFS_transaction_stat() + { + m_savepoint_count= 0; + m_rollback_to_savepoint_count= 0; + m_release_savepoint_count= 0; + } + + ulonglong count(void) + { + return (m_read_write_stat.m_count + m_read_only_stat.m_count); + } + + inline void reset(void) + { + m_read_write_stat.reset(); + m_read_only_stat.reset(); + m_savepoint_count= 0; + m_rollback_to_savepoint_count= 0; + m_release_savepoint_count= 0; + } + + inline void aggregate(const PFS_transaction_stat *stat) + { + m_read_write_stat.aggregate(&stat->m_read_write_stat); + m_read_only_stat.aggregate(&stat->m_read_only_stat); + m_savepoint_count+= stat->m_savepoint_count; + m_rollback_to_savepoint_count+= stat->m_rollback_to_savepoint_count; + m_release_savepoint_count+= stat->m_release_savepoint_count; + } +}; + +/** Single table io statistic. */ +struct PFS_table_io_stat +{ + bool m_has_data; + /** FETCH statistics */ + PFS_single_stat m_fetch; + /** INSERT statistics */ + PFS_single_stat m_insert; + /** UPDATE statistics */ + PFS_single_stat m_update; + /** DELETE statistics */ + PFS_single_stat m_delete; + + PFS_table_io_stat() + { + m_has_data= false; + } + + inline void reset(void) + { + m_has_data= false; + m_fetch.reset(); + m_insert.reset(); + m_update.reset(); + m_delete.reset(); + } + + inline void aggregate(const PFS_table_io_stat *stat) + { + if (stat->m_has_data) + { + m_has_data= true; + m_fetch.aggregate(&stat->m_fetch); + m_insert.aggregate(&stat->m_insert); + m_update.aggregate(&stat->m_update); + m_delete.aggregate(&stat->m_delete); + } + } + + inline void sum(PFS_single_stat *result) + { + if (m_has_data) + { + result->aggregate(& m_fetch); + result->aggregate(& m_insert); + result->aggregate(& m_update); + result->aggregate(& m_delete); + } + } +}; + +enum PFS_TL_LOCK_TYPE +{ + /* Locks from enum thr_lock */ + PFS_TL_READ= 0, + PFS_TL_READ_WITH_SHARED_LOCKS= 1, + PFS_TL_READ_HIGH_PRIORITY= 2, + PFS_TL_READ_NO_INSERT= 3, + PFS_TL_WRITE_ALLOW_WRITE= 4, + PFS_TL_WRITE_CONCURRENT_INSERT= 5, + PFS_TL_WRITE_DELAYED= 6, + PFS_TL_WRITE_LOW_PRIORITY= 7, + PFS_TL_WRITE= 8, + + /* Locks for handler::ha_external_lock() */ + PFS_TL_READ_EXTERNAL= 9, + PFS_TL_WRITE_EXTERNAL= 10, + + PFS_TL_NONE= 99 +}; + +#define COUNT_PFS_TL_LOCK_TYPE 11 + +/** Statistics for table locks. */ +struct PFS_table_lock_stat +{ + PFS_single_stat m_stat[COUNT_PFS_TL_LOCK_TYPE]; + + inline void reset(void) + { + PFS_single_stat *pfs= & m_stat[0]; + PFS_single_stat *pfs_last= & m_stat[COUNT_PFS_TL_LOCK_TYPE]; + for ( ; pfs < pfs_last ; pfs++) + pfs->reset(); + } + + inline void aggregate(const PFS_table_lock_stat *stat) + { + PFS_single_stat *pfs= & m_stat[0]; + PFS_single_stat *pfs_last= & m_stat[COUNT_PFS_TL_LOCK_TYPE]; + const PFS_single_stat *pfs_from= & stat->m_stat[0]; + for ( ; pfs < pfs_last ; pfs++, pfs_from++) + pfs->aggregate(pfs_from); + } + + inline void sum(PFS_single_stat *result) + { + PFS_single_stat *pfs= & m_stat[0]; + PFS_single_stat *pfs_last= & m_stat[COUNT_PFS_TL_LOCK_TYPE]; + for ( ; pfs < pfs_last ; pfs++) + result->aggregate(pfs); + } +}; + +/** Statistics for TABLE usage. */ +struct PFS_table_stat +{ + /** + Statistics, per index. + Each index stat is in [0, MAX_INDEXES-1], + stats when using no index are in [MAX_INDEXES]. + */ + PFS_table_io_stat m_index_stat[MAX_INDEXES + 1]; + + /** + Statistics, per lock type. + */ + PFS_table_lock_stat m_lock_stat; + + /** Reset table io statistic. */ + inline void reset_io(void) + { + PFS_table_io_stat *stat= & m_index_stat[0]; + PFS_table_io_stat *stat_last= & m_index_stat[MAX_INDEXES + 1]; + for ( ; stat < stat_last ; stat++) + stat->reset(); + } + + /** Reset table lock statistic. */ + inline void reset_lock(void) + { + m_lock_stat.reset(); + } + + /** Reset table statistic. */ + inline void reset(void) + { + reset_io(); + reset_lock(); + } + + inline void fast_reset_io(void) + { + memcpy(& m_index_stat, & g_reset_template.m_index_stat, sizeof(m_index_stat)); + } + + inline void fast_reset_lock(void) + { + memcpy(& m_lock_stat, & g_reset_template.m_lock_stat, sizeof(m_lock_stat)); + } + + inline void fast_reset(void) + { + memcpy(this, & g_reset_template, sizeof(*this)); + } + + inline void aggregate_io(const PFS_table_stat *stat, uint key_count) + { + PFS_table_io_stat *to_stat; + PFS_table_io_stat *to_stat_last; + const PFS_table_io_stat *from_stat; + + assert(key_count <= MAX_INDEXES); + + /* Aggregate stats for each index, if any */ + to_stat= & m_index_stat[0]; + to_stat_last= to_stat + key_count; + from_stat= & stat->m_index_stat[0]; + for ( ; to_stat < to_stat_last ; from_stat++, to_stat++) + to_stat->aggregate(from_stat); + + /* Aggregate stats for the table */ + to_stat= & m_index_stat[MAX_INDEXES]; + from_stat= & stat->m_index_stat[MAX_INDEXES]; + to_stat->aggregate(from_stat); + } + + inline void aggregate_lock(const PFS_table_stat *stat) + { + m_lock_stat.aggregate(& stat->m_lock_stat); + } + + inline void aggregate(const PFS_table_stat *stat, uint key_count) + { + aggregate_io(stat, key_count); + aggregate_lock(stat); + } + + inline void sum_io(PFS_single_stat *result, uint key_count) + { + PFS_table_io_stat *stat; + PFS_table_io_stat *stat_last; + + assert(key_count <= MAX_INDEXES); + + /* Sum stats for each index, if any */ + stat= & m_index_stat[0]; + stat_last= stat + key_count; + for ( ; stat < stat_last ; stat++) + stat->sum(result); + + /* Sum stats for the table */ + m_index_stat[MAX_INDEXES].sum(result); + } + + inline void sum_lock(PFS_single_stat *result) + { + m_lock_stat.sum(result); + } + + inline void sum(PFS_single_stat *result, uint key_count) + { + sum_io(result, key_count); + sum_lock(result); + } + + static struct PFS_table_stat g_reset_template; +}; + +/** Statistics for SOCKET IO. Used for both waits and byte counts. */ +struct PFS_socket_io_stat +{ + /** READ statistics */ + PFS_byte_stat m_read; + /** WRITE statistics */ + PFS_byte_stat m_write; + /** Miscellaneous statistics */ + PFS_byte_stat m_misc; + + inline void reset(void) + { + m_read.reset(); + m_write.reset(); + m_misc.reset(); + } + + inline void aggregate(const PFS_socket_io_stat *stat) + { + m_read.aggregate(&stat->m_read); + m_write.aggregate(&stat->m_write); + m_misc.aggregate(&stat->m_misc); + } + + /* Sum waits and byte counts */ + inline void sum(PFS_byte_stat *stat) + { + stat->aggregate(&m_read); + stat->aggregate(&m_write); + stat->aggregate(&m_misc); + } + + /* Sum waits only */ + inline void sum_waits(PFS_single_stat *stat) + { + stat->aggregate(&m_read); + stat->aggregate(&m_write); + stat->aggregate(&m_misc); + } +}; + +/** Statistics for SOCKET usage. */ +struct PFS_socket_stat +{ + /** Socket timing and byte count statistics per operation */ + PFS_socket_io_stat m_io_stat; + + /** Reset socket statistics. */ + inline void reset(void) + { + m_io_stat.reset(); + } +}; + +struct PFS_memory_stat_delta +{ + size_t m_alloc_count_delta; + size_t m_free_count_delta; + size_t m_alloc_size_delta; + size_t m_free_size_delta; + + void reset() + { + m_alloc_count_delta= 0; + m_free_count_delta= 0; + m_alloc_size_delta= 0; + m_free_size_delta= 0; + } +}; + +/** + Memory statistics. + Conceptually, the following statistics are maintained: + - CURRENT_COUNT_USED, + - LOW_COUNT_USED, + - HIGH_COUNT_USED + - CURRENT_SIZE_USED, + - LOW_SIZE_USED, + - HIGH_SIZE_USED + Now, the implementation keeps different counters, + which are easier (less overhead) to maintain while + collecting statistics. + Invariants are as follows: + CURRENT_COUNT_USED = @c m_alloc_count - @c m_free_count + LOW_COUNT_USED + @c m_free_count_capacity = CURRENT_COUNT_USED + CURRENT_COUNT_USED + @c m_alloc_count_capacity = HIGH_COUNT_USED + CURRENT_SIZE_USED = @c m_alloc_size - @c m_free_size + LOW_SIZE_USED + @c m_free_size_capacity = CURRENT_SIZE_USED + CURRENT_SIZE_USED + @c m_alloc_size_capacity = HIGH_SIZE_USED + +*/ +struct PFS_memory_stat +{ + bool m_used; + size_t m_alloc_count; + size_t m_free_count; + size_t m_alloc_size; + size_t m_free_size; + + size_t m_alloc_count_capacity; + size_t m_free_count_capacity; + size_t m_alloc_size_capacity; + size_t m_free_size_capacity; + + inline void reset(void) + { + m_used= false; + m_alloc_count= 0; + m_free_count= 0; + m_alloc_size= 0; + m_free_size= 0; + + m_alloc_count_capacity= 0; + m_free_count_capacity= 0; + m_alloc_size_capacity= 0; + m_free_size_capacity= 0; + } + + inline void rebase(void) + { + if (! m_used) + return; + + size_t base; + + base= std::min<size_t>(m_alloc_count, m_free_count); + m_alloc_count-= base; + m_free_count-= base; + + base= std::min<size_t>(m_alloc_size, m_free_size); + m_alloc_size-= base; + m_free_size-= base; + + m_alloc_count_capacity= 0; + m_free_count_capacity= 0; + m_alloc_size_capacity= 0; + m_free_size_capacity= 0; + } + + inline void partial_aggregate_to(PFS_memory_stat *stat) + { + if (! m_used) + return; + + size_t base; + + stat->m_used= true; + + base= std::min<size_t>(m_alloc_count, m_free_count); + if (base != 0) + { + stat->m_alloc_count+= base; + stat->m_free_count+= base; + m_alloc_count-= base; + m_free_count-= base; + } + + base= std::min<size_t>(m_alloc_size, m_free_size); + if (base != 0) + { + stat->m_alloc_size+= base; + stat->m_free_size+= base; + m_alloc_size-= base; + m_free_size-= base; + } + + stat->m_alloc_count_capacity+= m_alloc_count_capacity; + stat->m_free_count_capacity+= m_free_count_capacity; + stat->m_alloc_size_capacity+= m_alloc_size_capacity; + stat->m_free_size_capacity+= m_free_size_capacity; + + m_alloc_count_capacity= 0; + m_free_count_capacity= 0; + m_alloc_size_capacity= 0; + m_free_size_capacity= 0; + } + + inline void full_aggregate_to(PFS_memory_stat *stat) const + { + if (! m_used) + return; + + stat->m_used= true; + + stat->m_alloc_count+= m_alloc_count; + stat->m_free_count+= m_free_count; + stat->m_alloc_size+= m_alloc_size; + stat->m_free_size+= m_free_size; + + stat->m_alloc_count_capacity+= m_alloc_count_capacity; + stat->m_free_count_capacity+= m_free_count_capacity; + stat->m_alloc_size_capacity+= m_alloc_size_capacity; + stat->m_free_size_capacity+= m_free_size_capacity; + } + + inline void partial_aggregate_to(PFS_memory_stat *stat1, PFS_memory_stat *stat2) + { + if (! m_used) + return; + + size_t base; + + stat1->m_used= true; + stat2->m_used= true; + + base= std::min<size_t>(m_alloc_count, m_free_count); + if (base != 0) + { + stat1->m_alloc_count+= base; + stat2->m_alloc_count+= base; + stat1->m_free_count+= base; + stat2->m_free_count+= base; + m_alloc_count-= base; + m_free_count-= base; + } + + base= std::min<size_t>(m_alloc_size, m_free_size); + if (base != 0) + { + stat1->m_alloc_size+= base; + stat2->m_alloc_size+= base; + stat1->m_free_size+= base; + stat2->m_free_size+= base; + m_alloc_size-= base; + m_free_size-= base; + } + + stat1->m_alloc_count_capacity+= m_alloc_count_capacity; + stat2->m_alloc_count_capacity+= m_alloc_count_capacity; + stat1->m_free_count_capacity+= m_free_count_capacity; + stat2->m_free_count_capacity+= m_free_count_capacity; + stat1->m_alloc_size_capacity+= m_alloc_size_capacity; + stat2->m_alloc_size_capacity+= m_alloc_size_capacity; + stat1->m_free_size_capacity+= m_free_size_capacity; + stat2->m_free_size_capacity+= m_free_size_capacity; + + m_alloc_count_capacity= 0; + m_free_count_capacity= 0; + m_alloc_size_capacity= 0; + m_free_size_capacity= 0; + } + + inline void full_aggregate_to(PFS_memory_stat *stat1, PFS_memory_stat *stat2) const + { + if (! m_used) + return; + + stat1->m_used= true; + stat2->m_used= true; + + stat1->m_alloc_count+= m_alloc_count; + stat2->m_alloc_count+= m_alloc_count; + stat1->m_free_count+= m_free_count; + stat2->m_free_count+= m_free_count; + stat1->m_alloc_size+= m_alloc_size; + stat2->m_alloc_size+= m_alloc_size; + stat1->m_free_size+= m_free_size; + stat2->m_free_size+= m_free_size; + + stat1->m_alloc_count_capacity+= m_alloc_count_capacity; + stat2->m_alloc_count_capacity+= m_alloc_count_capacity; + stat1->m_free_count_capacity+= m_free_count_capacity; + stat2->m_free_count_capacity+= m_free_count_capacity; + stat1->m_alloc_size_capacity+= m_alloc_size_capacity; + stat2->m_alloc_size_capacity+= m_alloc_size_capacity; + stat1->m_free_size_capacity+= m_free_size_capacity; + stat2->m_free_size_capacity+= m_free_size_capacity; + } + + void count_builtin_alloc(size_t size) + { + m_used= true; + + m_alloc_count++; + m_free_count_capacity++; + m_alloc_size+= size; + m_free_size_capacity+= size; + + if (m_alloc_count_capacity >= 1) + { + m_alloc_count_capacity--; + } + + if (m_alloc_size_capacity >= size) + { + m_alloc_size_capacity-= size; + } + + return; + } + + void count_builtin_free(size_t size) + { + m_used= true; + + m_free_count++; + m_alloc_count_capacity++; + m_free_size+= size; + m_alloc_size_capacity+= size; + + if (m_free_count_capacity >= 1) + { + m_free_count_capacity--; + } + + if (m_free_size_capacity >= size) + { + m_free_size_capacity-= size; + } + + return; + } + + inline PFS_memory_stat_delta *count_alloc(size_t size, + PFS_memory_stat_delta *delta) + { + m_used= true; + + m_alloc_count++; + m_free_count_capacity++; + m_alloc_size+= size; + m_free_size_capacity+= size; + + if ((m_alloc_count_capacity >= 1) && + (m_alloc_size_capacity >= size)) + { + m_alloc_count_capacity--; + m_alloc_size_capacity-= size; + return NULL; + } + + delta->reset(); + + if (m_alloc_count_capacity >= 1) + { + m_alloc_count_capacity--; + } + else + { + delta->m_alloc_count_delta= 1; + } + + if (m_alloc_size_capacity >= size) + { + m_alloc_size_capacity-= size; + } + else + { + delta->m_alloc_size_delta= size - m_alloc_size_capacity; + m_alloc_size_capacity= 0; + } + + return delta; + } + + inline PFS_memory_stat_delta *count_realloc(size_t old_size, size_t new_size, + PFS_memory_stat_delta *delta) + { + m_used= true; + + size_t size_delta= new_size - old_size; + m_alloc_count++; + m_alloc_size+= new_size; + m_free_count++; + m_free_size+= old_size; + + if (new_size == old_size) + { + return NULL; + } + + if (new_size > old_size) + { + /* Growing */ + size_delta= new_size - old_size; + m_free_size_capacity+= size_delta; + + if (m_alloc_size_capacity >= size_delta) + { + m_alloc_size_capacity-= size_delta; + return NULL; + } + + delta->reset(); + delta->m_alloc_size_delta= size_delta - m_alloc_size_capacity; + m_alloc_size_capacity= 0; + } + else + { + /* Shrinking */ + size_delta= old_size - new_size; + m_alloc_size_capacity+= size_delta; + + if (m_free_size_capacity >= size_delta) + { + m_free_size_capacity-= size_delta; + return NULL; + } + + delta->reset(); + delta->m_free_size_delta= size_delta - m_free_size_capacity; + m_free_size_capacity= 0; + } + + return delta; + } + + inline PFS_memory_stat_delta *count_free(size_t size, PFS_memory_stat_delta *delta) + { + m_used= true; + + m_free_count++; + m_alloc_count_capacity++; + m_free_size+= size; + m_alloc_size_capacity+= size; + + if ((m_free_count_capacity >= 1) && + (m_free_size_capacity >= size)) + { + m_free_count_capacity--; + m_free_size_capacity-= size; + return NULL; + } + + delta->reset(); + + if (m_free_count_capacity >= 1) + { + m_free_count_capacity--; + } + else + { + delta->m_free_count_delta= 1; + } + + if (m_free_size_capacity >= size) + { + m_free_size_capacity-= size; + } + else + { + delta->m_free_size_delta= size - m_free_size_capacity; + m_free_size_capacity= 0; + } + + return delta; + } + + inline PFS_memory_stat_delta *apply_delta(const PFS_memory_stat_delta *delta, + PFS_memory_stat_delta *delta_buffer) + { + size_t val; + size_t remaining_alloc_count; + size_t remaining_alloc_size; + size_t remaining_free_count; + size_t remaining_free_size; + bool has_remaining= false; + + m_used= true; + + val= delta->m_alloc_count_delta; + if (val <= m_alloc_count_capacity) + { + m_alloc_count_capacity-= val; + remaining_alloc_count= 0; + } + else + { + remaining_alloc_count= val - m_alloc_count_capacity; + m_alloc_count_capacity= 0; + has_remaining= true; + } + + val= delta->m_alloc_size_delta; + if (val <= m_alloc_size_capacity) + { + m_alloc_size_capacity-= val; + remaining_alloc_size= 0; + } + else + { + remaining_alloc_size= val - m_alloc_size_capacity; + m_alloc_size_capacity= 0; + has_remaining= true; + } + + val= delta->m_free_count_delta; + if (val <= m_free_count_capacity) + { + m_free_count_capacity-= val; + remaining_free_count= 0; + } + else + { + remaining_free_count= val - m_free_count_capacity; + m_free_count_capacity= 0; + has_remaining= true; + } + + val= delta->m_free_size_delta; + if (val <= m_free_size_capacity) + { + m_free_size_capacity-= val; + remaining_free_size= 0; + } + else + { + remaining_free_size= val - m_free_size_capacity; + m_free_size_capacity= 0; + has_remaining= true; + } + + if (! has_remaining) + return NULL; + + delta_buffer->m_alloc_count_delta= remaining_alloc_count; + delta_buffer->m_alloc_size_delta= remaining_alloc_size; + delta_buffer->m_free_count_delta= remaining_free_count; + delta_buffer->m_free_size_delta= remaining_free_size; + return delta_buffer; + } +}; + +#define PFS_MEMORY_STAT_INITIALIZER { false, 0, 0, 0, 0, 0, 0, 0, 0} + +/** Connections statistics. */ +struct PFS_connection_stat +{ + PFS_connection_stat() + : m_current_connections(0), + m_total_connections(0) + {} + + ulonglong m_current_connections; + ulonglong m_total_connections; + + inline void aggregate_active(ulonglong active) + { + m_current_connections+= active; + m_total_connections+= active; + } + + inline void aggregate_disconnected(ulonglong disconnected) + { + m_total_connections+= disconnected; + } +}; + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_status.cc b/storage/perfschema/pfs_status.cc new file mode 100644 index 00000000..2596c53c --- /dev/null +++ b/storage/perfschema/pfs_status.cc @@ -0,0 +1,166 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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/pfs_status.cc + Status variables statistics (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "pfs_global.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_account.h" +#include "pfs_host.h" +#include "pfs_user.h" +#include "pfs_status.h" +#include "pfs_atomic.h" +#include "pfs_buffer_container.h" + +#include "sql_show.h" /* reset_status_vars */ + +PFS_status_stats::PFS_status_stats() +{ + reset(); +} + +void PFS_status_stats::reset() +{ + m_has_stats= false; + memset(&m_stats, 0, sizeof(m_stats)); +} + +void PFS_status_stats::aggregate(const PFS_status_stats *from) +{ + if (from->m_has_stats) + { + m_has_stats= true; + for (int i= 0; i < COUNT_GLOBAL_STATUS_VARS; i++) + { + m_stats[i] += from->m_stats[i]; + } + } +} + +void PFS_status_stats::aggregate_from(const STATUS_VAR *from) +{ + ulong *from_var= (ulong*) from; + + m_has_stats= true; + for (int i= 0; + i < COUNT_GLOBAL_STATUS_VARS; + i++, from_var++) + { + m_stats[i] += *from_var; + } +} + +void PFS_status_stats::aggregate_to(STATUS_VAR *to) +{ + if (m_has_stats) + { + ulong *to_var= (ulong*) to; + + for (int i= 0; + i < COUNT_GLOBAL_STATUS_VARS; + i++, to_var++) + { + *to_var += m_stats[i]; + } + } +} + +static void fct_reset_status_by_thread(PFS_thread *thread) +{ + PFS_account *account; + PFS_user *user; + PFS_host *host; + + if (thread->m_lock.is_populated()) + { + account= sanitize_account(thread->m_account); + user= sanitize_user(thread->m_user); + host= sanitize_host(thread->m_host); + aggregate_thread_status(thread, account, user, host); + } +} + +/** Reset table STATUS_BY_THREAD data. */ +void reset_status_by_thread() +{ + global_thread_container.apply_all(fct_reset_status_by_thread); +} + +static void fct_reset_status_by_account(PFS_account *account) +{ + PFS_user *user; + PFS_host *host; + + if (account->m_lock.is_populated()) + { + user= sanitize_user(account->m_user); + host= sanitize_host(account->m_host); + account->aggregate_status(user, host); + } +} + +/** Reset table STATUS_BY_ACCOUNT data. */ +void reset_status_by_account() +{ + global_account_container.apply_all(fct_reset_status_by_account); +} + +static void fct_reset_status_by_user(PFS_user *user) +{ + if (user->m_lock.is_populated()) + user->aggregate_status(); +} + +/** Reset table STATUS_BY_USER data. */ +void reset_status_by_user() +{ + global_user_container.apply_all(fct_reset_status_by_user); +} + +static void fct_reset_status_by_host(PFS_host *host) +{ + if (host->m_lock.is_populated()) + host->aggregate_status(); +} + +/** Reset table STATUS_BY_HOST data. */ +void reset_status_by_host() +{ + global_host_container.apply_all(fct_reset_status_by_host); +} + +/** Reset table GLOBAL_STATUS data. */ +void reset_global_status() +{ + /* + Do not memset global_status_var, + NO_FLUSH counters need to be preserved + */ + reset_status_vars(); +} + diff --git a/storage/perfschema/pfs_status.h b/storage/perfschema/pfs_status.h new file mode 100644 index 00000000..42329ffb --- /dev/null +++ b/storage/perfschema/pfs_status.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef PFS_STATUS_H +#define PFS_STATUS_H + +/** + @file storage/perfschema/pfs_status.h + Status variables statistics (declarations). +*/ + +struct PFS_status_stats +{ + PFS_status_stats(); + + void reset(); + void aggregate(const PFS_status_stats *from); + void aggregate_from(const STATUS_VAR *from); + void aggregate_to(STATUS_VAR *to); + + bool m_has_stats; + ulong m_stats[COUNT_GLOBAL_STATUS_VARS]; +}; + +void reset_status_by_thread(); +void reset_status_by_account(); +void reset_status_by_user(); +void reset_status_by_host(); +void reset_global_status(); + +#endif + diff --git a/storage/perfschema/pfs_timer.cc b/storage/perfschema/pfs_timer.cc new file mode 100644 index 00000000..15e1a188 --- /dev/null +++ b/storage/perfschema/pfs_timer.cc @@ -0,0 +1,345 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_timer.cc + Performance schema timers (implementation). +*/ + +#include "my_global.h" +#include "pfs_timer.h" +#include "my_rdtsc.h" + +enum_timer_name idle_timer= TIMER_NAME_MICROSEC; +enum_timer_name wait_timer= TIMER_NAME_CYCLE; +enum_timer_name stage_timer= TIMER_NAME_NANOSEC; +enum_timer_name statement_timer= TIMER_NAME_NANOSEC; +enum_timer_name transaction_timer= TIMER_NAME_NANOSEC; + +static ulonglong cycle_v0; +static ulonglong nanosec_v0; +static ulonglong microsec_v0; +static ulonglong millisec_v0; +static ulonglong tick_v0; + +static ulong cycle_to_pico; /* 1000 at 1 GHz, 333 at 3GHz, 250 at 4GHz */ +static ulong nanosec_to_pico; /* In theory, 1 000 */ +static ulong microsec_to_pico; /* In theory, 1 000 000 */ +static ulong millisec_to_pico; /* In theory, 1 000 000 000, fits in uint32 */ +static ulonglong tick_to_pico; /* 1e10 at 100 Hz, 1.666e10 at 60 Hz */ + +/* Indexed by enum enum_timer_name */ +static struct time_normalizer to_pico_data[FIRST_TIMER_NAME + COUNT_TIMER_NAME]= +{ + { 0, 0}, /* unused */ + { 0, 0}, /* cycle */ + { 0, 0}, /* nanosec */ + { 0, 0}, /* microsec */ + { 0, 0}, /* millisec */ + { 0, 0} /* tick */ +}; + +static inline ulong round_to_ulong(double value) +{ + return (ulong) (value + 0.5); +} + +static inline ulonglong round_to_ulonglong(double value) +{ + return (ulonglong) (value + 0.5); +} + +void init_timers(void) +{ + double pico_frequency= 1.0e12; + + cycle_v0= my_timer_cycles(); + nanosec_v0= my_timer_nanoseconds(); + microsec_v0= my_timer_microseconds(); + millisec_v0= my_timer_milliseconds(); + tick_v0= my_timer_ticks(); + + if (sys_timer_info.cycles.frequency > 0) + cycle_to_pico= round_to_ulong(pico_frequency/ + (double)sys_timer_info.cycles.frequency); + else + cycle_to_pico= 0; + + if (sys_timer_info.nanoseconds.frequency > 0) + nanosec_to_pico= round_to_ulong(pico_frequency/ + (double)sys_timer_info.nanoseconds.frequency); + else + nanosec_to_pico= 0; + + if (sys_timer_info.microseconds.frequency > 0) + microsec_to_pico= round_to_ulong(pico_frequency/ + (double)sys_timer_info.microseconds.frequency); + else + microsec_to_pico= 0; + + if (sys_timer_info.milliseconds.frequency > 0) + millisec_to_pico= round_to_ulong(pico_frequency/ + (double)sys_timer_info.milliseconds.frequency); + else + millisec_to_pico= 0; + + if (sys_timer_info.ticks.frequency > 0) + tick_to_pico= round_to_ulonglong(pico_frequency/ + (double)sys_timer_info.ticks.frequency); + else + tick_to_pico= 0; + + to_pico_data[TIMER_NAME_CYCLE].m_v0= cycle_v0; + to_pico_data[TIMER_NAME_CYCLE].m_factor= cycle_to_pico; + + to_pico_data[TIMER_NAME_NANOSEC].m_v0= nanosec_v0; + to_pico_data[TIMER_NAME_NANOSEC].m_factor= nanosec_to_pico; + + to_pico_data[TIMER_NAME_MICROSEC].m_v0= microsec_v0; + to_pico_data[TIMER_NAME_MICROSEC].m_factor= microsec_to_pico; + + to_pico_data[TIMER_NAME_MILLISEC].m_v0= millisec_v0; + to_pico_data[TIMER_NAME_MILLISEC].m_factor= millisec_to_pico; + + to_pico_data[TIMER_NAME_TICK].m_v0= tick_v0; + to_pico_data[TIMER_NAME_TICK].m_factor= tick_to_pico; + + /* + Depending on the platform and build options, + some timers may not be available. + Pick best replacements. + */ + + /* + For WAIT, the cycle timer is used by default. However, it is not available + on all architectures. Fall back to the nanosecond timer in this case. It is + unlikely that neither cycle nor nanosecond are available, but we continue + probing less resolution timers anyway for consistency with other events. + */ + + if (cycle_to_pico != 0) + { + /* Normal case. */ + wait_timer= TIMER_NAME_CYCLE; + } + else if (nanosec_to_pico != 0) + { + /* Robustness, no known cases. */ + wait_timer= TIMER_NAME_NANOSEC; + } + else if (microsec_to_pico != 0) + { + /* Robustness, no known cases. */ + wait_timer= TIMER_NAME_MICROSEC; + } + else if (millisec_to_pico != 0) + { + /* Robustness, no known cases. */ + wait_timer= TIMER_NAME_MILLISEC; + } + else + { + /* + Will never be reached on any architecture, but must provide a default if + no other timers are available. + */ + wait_timer= TIMER_NAME_TICK; + } + + /* + For STAGE and STATEMENT, a timer with a fixed frequency is better. + The preferred timer is nanosecond, or lower resolutions. + */ + + if (nanosec_to_pico != 0) + { + /* Normal case. */ + stage_timer= TIMER_NAME_NANOSEC; + statement_timer= TIMER_NAME_NANOSEC; + transaction_timer= TIMER_NAME_NANOSEC; + } + else if (microsec_to_pico != 0) + { + /* Windows. */ + stage_timer= TIMER_NAME_MICROSEC; + statement_timer= TIMER_NAME_MICROSEC; + transaction_timer= TIMER_NAME_MICROSEC; + } + else if (millisec_to_pico != 0) + { + /* Robustness, no known cases. */ + stage_timer= TIMER_NAME_MILLISEC; + statement_timer= TIMER_NAME_MILLISEC; + transaction_timer= TIMER_NAME_MILLISEC; + } + else if (tick_to_pico != 0) + { + /* Robustness, no known cases. */ + stage_timer= TIMER_NAME_TICK; + statement_timer= TIMER_NAME_TICK; + transaction_timer= TIMER_NAME_TICK; + } + else + { + /* Robustness, no known cases. */ + stage_timer= TIMER_NAME_CYCLE; + statement_timer= TIMER_NAME_CYCLE; + transaction_timer= TIMER_NAME_CYCLE; + } + + /* + For IDLE, a timer with a fixed frequency is critical, + as the CPU clock may slow down a lot if the server is completely idle. + The preferred timer is microsecond, or lower resolutions. + */ + + if (microsec_to_pico != 0) + { + /* Normal case. */ + idle_timer= TIMER_NAME_MICROSEC; + } + else if (millisec_to_pico != 0) + { + /* Robustness, no known cases. */ + wait_timer= TIMER_NAME_MILLISEC; + } + else if (tick_to_pico != 0) + { + /* Robustness, no known cases. */ + idle_timer= TIMER_NAME_TICK; + } + else + { + /* Robustness, no known cases. */ + idle_timer= TIMER_NAME_CYCLE; + } +} + +ulonglong get_timer_raw_value(enum_timer_name timer_name) +{ + switch (timer_name) + { + case TIMER_NAME_CYCLE: + return my_timer_cycles(); + case TIMER_NAME_NANOSEC: + return my_timer_nanoseconds(); + case TIMER_NAME_MICROSEC: + return my_timer_microseconds(); + case TIMER_NAME_MILLISEC: + return my_timer_milliseconds(); + case TIMER_NAME_TICK: + return my_timer_ticks(); + default: + assert(false); + } + return 0; +} + +ulonglong get_timer_raw_value_and_function(enum_timer_name timer_name, timer_fct_t *fct) +{ + switch (timer_name) + { + case TIMER_NAME_CYCLE: + *fct= my_timer_cycles; + return my_timer_cycles(); + case TIMER_NAME_NANOSEC: + *fct= my_timer_nanoseconds; + return my_timer_nanoseconds(); + case TIMER_NAME_MICROSEC: + *fct= my_timer_microseconds; + return my_timer_microseconds(); + case TIMER_NAME_MILLISEC: + *fct= my_timer_milliseconds; + return my_timer_milliseconds(); + case TIMER_NAME_TICK: + *fct= my_timer_ticks; + return my_timer_ticks(); + default: + *fct= NULL; + assert(false); + } + return 0; +} + +ulonglong get_timer_pico_value(enum_timer_name timer_name) +{ + ulonglong result; + + switch (timer_name) + { + case TIMER_NAME_CYCLE: + result= (my_timer_cycles() - cycle_v0) * cycle_to_pico; + break; + case TIMER_NAME_NANOSEC: + result= (my_timer_nanoseconds() - nanosec_v0) * nanosec_to_pico; + break; + case TIMER_NAME_MICROSEC: + result= (my_timer_microseconds() - microsec_v0) * microsec_to_pico; + break; + case TIMER_NAME_MILLISEC: + result= (my_timer_milliseconds() - millisec_v0) * millisec_to_pico; + break; + case TIMER_NAME_TICK: + result= (my_timer_ticks() - tick_v0) * tick_to_pico; + break; + default: + result= 0; + assert(false); + } + return result; +} + +time_normalizer* time_normalizer::get(enum_timer_name timer_name) +{ + uint index= static_cast<uint> (timer_name); + + assert(index >= FIRST_TIMER_NAME); + assert(index <= LAST_TIMER_NAME); + + return & to_pico_data[index]; +} + +void time_normalizer::to_pico(ulonglong start, ulonglong end, + ulonglong *pico_start, ulonglong *pico_end, ulonglong *pico_wait) +{ + if (start == 0) + { + *pico_start= 0; + *pico_end= 0; + *pico_wait= 0; + } + else + { + *pico_start= (start - m_v0) * m_factor; + if (end == 0) + { + *pico_end= 0; + *pico_wait= 0; + } + else + { + *pico_end= (end - m_v0) * m_factor; + *pico_wait= (end - start) * m_factor; + } + } +} + diff --git a/storage/perfschema/pfs_timer.h b/storage/perfschema/pfs_timer.h new file mode 100644 index 00000000..e5393a4a --- /dev/null +++ b/storage/perfschema/pfs_timer.h @@ -0,0 +1,152 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_TIMER_H +#define PFS_TIMER_H + +/** + @file storage/perfschema/pfs_timer.h + Performance schema timers (declarations). +*/ +#include <my_rdtsc.h> +#include "pfs_column_types.h" + +/** Conversion factor, from micro seconds to pico seconds. */ +#define MICROSEC_TO_PICOSEC 1000000 + +/** + A time normalizer. + A time normalizer consist of a transformation that + converts raw timer values (expressed in the timer unit) + to normalized values, expressed in picoseconds. +*/ +struct time_normalizer +{ + /** + Get a time normalizer for a given timer. + @param timer_name the timer name + @return the normalizer for the timer + */ + static time_normalizer* get(enum_timer_name timer_name); + + /** Timer value at server statup. */ + ulonglong m_v0; + /** Conversion factor from timer values to pico seconds. */ + ulonglong m_factor; + + /** + Convert a wait from timer units to pico seconds. + @param wait a wait, expressed in timer units + @return the wait, expressed in pico seconds + */ + inline ulonglong wait_to_pico(ulonglong wait) + { + return wait * m_factor; + } + + /** + Convert a time from timer units to pico seconds. + @param t a time, expressed in timer units + @return the time, expressed in pico seconds + */ + inline ulonglong time_to_pico(ulonglong t) + { + return (t == 0 ? 0 : (t - m_v0) * m_factor); + } + + /** + Convert start / end times from timer units to pico seconds. + @param start start time, expressed in timer units + @param end end time, expressed in timer units + @param[out] pico_start start time, expressed in pico seconds + @param[out] pico_end end time, expressed in pico seconds + @param[out] pico_wait wait time, expressed in pico seconds + */ + void to_pico(ulonglong start, ulonglong end, + ulonglong *pico_start, ulonglong *pico_end, ulonglong *pico_wait); +}; + +/** + Idle timer. + The timer used to measure all idle events. +*/ +extern enum_timer_name idle_timer; +/** + Wait timer. + The timer used to measure all wait events. +*/ +extern enum_timer_name wait_timer; +/** + Stage timer. + The timer used to measure all stage events. +*/ +extern enum_timer_name stage_timer; +/** + Statement timer. + The timer used to measure all statement events. +*/ +extern enum_timer_name statement_timer; +/** + Transaction timer. + The timer used to measure all transaction events. +*/ +extern enum_timer_name transaction_timer; +/** + Timer information data. + Characteristics about each supported timer. +*/ +extern MYSQL_PLUGIN_IMPORT MY_TIMER_INFO sys_timer_info; + +/** Initialize the timer component. */ +void init_timers(); + +extern "C" +{ + /** A timer function. */ + typedef ulonglong (*timer_fct_t)(void); +} + +/** + Get a timer value, in pico seconds. + @param timer_name the timer to use + @return timer value, in pico seconds +*/ +ulonglong get_timer_pico_value(enum_timer_name timer_name); +/** + Get a timer value, in timer units. + @param timer_name the timer to use + @return timer value, in timer units +*/ +ulonglong get_timer_raw_value(enum_timer_name timer_name); +/** + Get a timer value and function, in timer units. + This function is useful when code needs to call the same timer several times. + The returned timer function can be invoked directly, which avoids having to + resolve the timer by name for each call. + @param timer_name the timer to use + @param[out] fct the timer function + @return timer value, in timer units +*/ +ulonglong get_timer_raw_value_and_function(enum_timer_name timer_name, timer_fct_t *fct); + +#endif + diff --git a/storage/perfschema/pfs_user.cc b/storage/perfschema/pfs_user.cc new file mode 100644 index 00000000..1f299d9b --- /dev/null +++ b/storage/perfschema/pfs_user.cc @@ -0,0 +1,339 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_user.cc + Performance schema user (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "pfs.h" +#include "pfs_stat.h" +#include "pfs_instr.h" +#include "pfs_setup_actor.h" +#include "pfs_user.h" +#include "pfs_global.h" +#include "pfs_instr_class.h" +#include "pfs_buffer_container.h" + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +LF_HASH user_hash; +static bool user_hash_inited= false; + +/** + Initialize the user buffers. + @param param sizing parameters + @return 0 on success +*/ +int init_user(const PFS_global_param *param) +{ + if (global_user_container.init(param->m_user_sizing)) + return 1; + + return 0; +} + +/** Cleanup all the user buffers. */ +void cleanup_user(void) +{ + global_user_container.cleanup(); +} + +C_MODE_START +static uchar *user_hash_get_key(const uchar *entry, size_t *length, + my_bool) +{ + const PFS_user * const *typed_entry; + const PFS_user *user; + const void *result; + typed_entry= reinterpret_cast<const PFS_user* const *> (entry); + assert(typed_entry != NULL); + user= *typed_entry; + assert(user != NULL); + *length= user->m_key.m_key_length; + result= user->m_key.m_hash_key; + return const_cast<uchar*> (reinterpret_cast<const uchar*> (result)); +} +C_MODE_END + +/** + Initialize the user hash. + @return 0 on success +*/ +int init_user_hash(const PFS_global_param *param) +{ + if ((! user_hash_inited) && (param->m_user_sizing != 0)) + { + lf_hash_init(&user_hash, sizeof(PFS_user*), LF_HASH_UNIQUE, + 0, 0, user_hash_get_key, &my_charset_bin); + user_hash_inited= true; + } + return 0; +} + +/** Cleanup the user hash. */ +void cleanup_user_hash(void) +{ + if (user_hash_inited) + { + lf_hash_destroy(&user_hash); + user_hash_inited= false; + } +} + +static LF_PINS* get_user_hash_pins(PFS_thread *thread) +{ + if (unlikely(thread->m_user_hash_pins == NULL)) + { + if (! user_hash_inited) + return NULL; + thread->m_user_hash_pins= lf_hash_get_pins(&user_hash); + } + return thread->m_user_hash_pins; +} + +static void set_user_key(PFS_user_key *key, + const char *user, uint user_length) +{ + assert(user_length <= USERNAME_LENGTH); + + char *ptr= &key->m_hash_key[0]; + if (user_length > 0) + { + memcpy(ptr, user, user_length); + ptr+= user_length; + } + ptr[0]= 0; + ptr++; + key->m_key_length= (uint)(ptr - &key->m_hash_key[0]); +} + +PFS_user * +find_or_create_user(PFS_thread *thread, + const char *username, uint username_length) +{ + LF_PINS *pins= get_user_hash_pins(thread); + if (unlikely(pins == NULL)) + { + global_user_container.m_lost++; + return NULL; + } + + PFS_user_key key; + set_user_key(&key, username, username_length); + + PFS_user **entry; + PFS_user *pfs; + uint retry_count= 0; + const uint retry_max= 3; + pfs_dirty_state dirty_state; + +search: + entry= reinterpret_cast<PFS_user**> + (lf_hash_search(&user_hash, pins, + key.m_hash_key, key.m_key_length)); + if (entry && (entry != MY_ERRPTR)) + { + pfs= *entry; + pfs->inc_refcount(); + lf_hash_search_unpin(pins); + return pfs; + } + + lf_hash_search_unpin(pins); + + pfs= global_user_container.allocate(& dirty_state); + if (pfs != NULL) + { + pfs->m_key= key; + if (username_length > 0) + pfs->m_username= &pfs->m_key.m_hash_key[0]; + else + pfs->m_username= NULL; + pfs->m_username_length= username_length; + + pfs->init_refcount(); + pfs->reset_stats(); + pfs->m_disconnected_count= 0; + + int res; + pfs->m_lock.dirty_to_allocated(& dirty_state); + res= lf_hash_insert(&user_hash, pins, &pfs); + if (likely(res == 0)) + { + return pfs; + } + + global_user_container.deallocate(pfs); + + if (res > 0) + { + if (++retry_count > retry_max) + { + global_user_container.m_lost++; + return NULL; + } + goto search; + } + + global_user_container.m_lost++; + return NULL; + } + + return NULL; +} + +void PFS_user::aggregate(bool alive) +{ + aggregate_waits(); + aggregate_stages(); + aggregate_statements(); + aggregate_transactions(); + aggregate_memory(alive); + aggregate_status(); + aggregate_stats(); +} + +void PFS_user::aggregate_waits() +{ + /* No parent to aggregate to, clean the stats */ + reset_waits_stats(); +} + +void PFS_user::aggregate_stages() +{ + /* No parent to aggregate to, clean the stats */ + reset_stages_stats(); +} + +void PFS_user::aggregate_statements() +{ + /* No parent to aggregate to, clean the stats */ + reset_statements_stats(); +} + +void PFS_user::aggregate_transactions() +{ + /* No parent to aggregate to, clean the stats */ + reset_transactions_stats(); +} + +void PFS_user::aggregate_memory(bool alive) +{ + /* No parent to aggregate to, clean the stats */ + rebase_memory_stats(); +} + +void PFS_user::aggregate_status() +{ + /* No parent to aggregate to, clean the stats */ + reset_status_stats(); +} + +void PFS_user::aggregate_stats() +{ + /* No parent to aggregate to, clean the stats */ + m_disconnected_count= 0; +} + +void PFS_user::release() +{ + dec_refcount(); +} + +void PFS_user::carry_memory_stat_delta(PFS_memory_stat_delta *delta, uint index) +{ + PFS_memory_stat *event_name_array; + PFS_memory_stat *stat; + PFS_memory_stat_delta delta_buffer; + + event_name_array= write_instr_class_memory_stats(); + stat= & event_name_array[index]; + (void) stat->apply_delta(delta, &delta_buffer); +} + +PFS_user *sanitize_user(PFS_user *unsafe) +{ + return global_user_container.sanitize(unsafe); +} + +void purge_user(PFS_thread *thread, PFS_user *user) +{ + LF_PINS *pins= get_user_hash_pins(thread); + if (unlikely(pins == NULL)) + return; + + PFS_user **entry; + entry= reinterpret_cast<PFS_user**> + (lf_hash_search(&user_hash, pins, + user->m_key.m_hash_key, user->m_key.m_key_length)); + if (entry && (entry != MY_ERRPTR)) + { + assert(*entry == user); + if (user->get_refcount() == 0) + { + lf_hash_delete(&user_hash, pins, + user->m_key.m_hash_key, user->m_key.m_key_length); + user->aggregate(false); + global_user_container.deallocate(user); + } + } + + lf_hash_search_unpin(pins); +} + +class Proc_purge_user + : public PFS_buffer_processor<PFS_user> +{ +public: + Proc_purge_user(PFS_thread *thread) + : m_thread(thread) + {} + + virtual void operator()(PFS_user *pfs) + { + pfs->aggregate(true); + if (pfs->get_refcount() == 0) + purge_user(m_thread, pfs); + } + +private: + PFS_thread *m_thread; +}; + +/** Purge non connected users, reset stats of connected users. */ +void purge_all_user(void) +{ + PFS_thread *thread= PFS_thread::get_current_thread(); + if (unlikely(thread == NULL)) + return; + + Proc_purge_user proc(thread); + global_user_container.apply(proc); +} + +/** @} */ diff --git a/storage/perfschema/pfs_user.h b/storage/perfschema/pfs_user.h new file mode 100644 index 00000000..e15e733e --- /dev/null +++ b/storage/perfschema/pfs_user.h @@ -0,0 +1,122 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_USER_H +#define PFS_USER_H + +/** + @file storage/perfschema/pfs_user.h + Performance schema user (declarations). +*/ + +#include "pfs_lock.h" +#include "lf.h" +#include "pfs_con_slice.h" + +struct PFS_global_param; +struct PFS_thread; + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +/** Hash key for a user. */ +struct PFS_user_key +{ + /** + Hash search key. + This has to be a string for LF_HASH, + the format is "<username><0x00>" + */ + char m_hash_key[USERNAME_LENGTH + 1]; + uint m_key_length; +}; + +/** Per user statistics. */ +struct PFS_ALIGNED PFS_user : public PFS_connection_slice +{ +public: + inline void init_refcount(void) + { + PFS_atomic::store_32(& m_refcount, 1); + } + + inline int get_refcount(void) + { + return PFS_atomic::load_32(& m_refcount); + } + + inline void inc_refcount(void) + { + PFS_atomic::add_32(& m_refcount, 1); + } + + inline void dec_refcount(void) + { + PFS_atomic::add_32(& m_refcount, -1); + } + + void aggregate(bool alive); + void aggregate_waits(void); + void aggregate_stages(void); + void aggregate_statements(void); + void aggregate_transactions(void); + void aggregate_memory(bool alive); + void aggregate_status(void); + void aggregate_stats(void); + void release(void); + + void carry_memory_stat_delta(PFS_memory_stat_delta *delta, uint index); + + /** Internal lock. */ + pfs_lock m_lock; + PFS_user_key m_key; + const char *m_username; + uint m_username_length; + + ulonglong m_disconnected_count; + +private: + int m_refcount; +}; + +int init_user(const PFS_global_param *param); +void cleanup_user(void); +int init_user_hash(const PFS_global_param *param); +void cleanup_user_hash(void); + +PFS_user * +find_or_create_user(PFS_thread *thread, + const char *username, uint username_length); + +PFS_user *sanitize_user(PFS_user *unsafe); +void purge_all_user(void); + + +/* For show status. */ + +extern LF_HASH user_hash; + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_variable.cc b/storage/perfschema/pfs_variable.cc new file mode 100644 index 00000000..239c55b6 --- /dev/null +++ b/storage/perfschema/pfs_variable.cc @@ -0,0 +1,1291 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + Copyright (c) 2020, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + +/** + @file storage/perfschema/pfs_variable.cc + Performance schema system variable and status variable (implementation). +*/ +#include "sql_plugin.h" +#include "pfs_variable.h" +#include "my_sys.h" +#include "debug_sync.h" +#include "pfs.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "sql_audit.h" // audit_global_variable_get + +static inline SHOW_SCOPE show_scope_from_type(enum enum_mysql_show_type type) +{ + switch(type) { + case SHOW_BOOL: + case SHOW_CHAR: + case SHOW_CHAR_PTR: + case SHOW_DOUBLE: + case SHOW_HAVE: + case SHOW_HA_ROWS: + case SHOW_KEY_CACHE_LONG: + case SHOW_LEX_STRING: + case SHOW_LONG_NOFLUSH: + case SHOW_MY_BOOL: + case SHOW_SINT: + case SHOW_SLONG: + case SHOW_SLONGLONG: + case SHOW_SYS: + case SHOW_UINT: + case SHOW_ULONG: + case SHOW_ULONGLONG: + return SHOW_SCOPE_GLOBAL; + + case SHOW_DOUBLE_STATUS: + case SHOW_LONGLONG_STATUS: + case SHOW_LONG_STATUS: + return SHOW_SCOPE_ALL; + + case SHOW_ARRAY: + case SHOW_FUNC: + case SHOW_SIMPLE_FUNC: + case SHOW_UNDEF: + default: + return SHOW_SCOPE_UNDEF; + } + return SHOW_SCOPE_UNDEF; +} + + +/** + CLASS PFS_system_variable_cache +*/ + +/** + Build a sorted list of all system variables from the system variable hash. + Filter by scope. Must be called inside of LOCK_plugin_delete. +*/ +bool PFS_system_variable_cache::init_show_var_array(enum_var_type scope, bool strict) +{ + assert(!m_initialized); + m_query_scope= scope; + + mysql_prlock_rdlock(&LOCK_system_variables_hash); + DEBUG_SYNC(m_current_thd, "acquired_LOCK_system_variables_hash"); + + /* Record the system variable hash version to detect subsequent changes. */ + m_version= get_system_variable_hash_version(); + + /* Build the SHOW_VAR array from the system variable hash. */ + SHOW_VAR *vars= enumerate_sys_vars(m_current_thd, true, m_query_scope/*, strict */); + m_show_var_array.reserve(get_system_variable_hash_records()); + for (int i=0; vars[i].name; i++) + m_show_var_array.set(i, vars[i]); + + mysql_prlock_unlock(&LOCK_system_variables_hash); + + /* Increase cache size if necessary. */ + m_cache.reserve(m_show_var_array.elements()); + + m_initialized= true; + return true; +} + +/** + Build an array of SHOW_VARs from the system variable hash. + Filter for SESSION scope. +*/ +bool PFS_system_variable_cache::do_initialize_session(void) +{ + /* Block plugins from unloading. */ + mysql_mutex_lock(&LOCK_plugin_delete); + + /* Build the array. */ + bool ret= init_show_var_array(OPT_SESSION, true); + + mysql_mutex_unlock(&LOCK_plugin_delete); + return ret; +} + +/** + Match system variable scope to desired scope. +*/ +bool PFS_system_variable_cache::match_scope(int scope) +{ + switch (scope) + { + case sys_var::GLOBAL: + return m_query_scope == OPT_GLOBAL; + break; + + case sys_var::SESSION: + return (m_query_scope == OPT_GLOBAL || m_query_scope == OPT_SESSION); + break; + + case sys_var::ONLY_SESSION: + return m_query_scope == OPT_SESSION; + break; + + default: + return false; + break; + } + return false; +} + +/** + Build a GLOBAL system variable cache. +*/ +int PFS_system_variable_cache::do_materialize_global(void) +{ + /* Block system variable additions or deletions. */ + mysql_mutex_lock(&LOCK_global_system_variables); + + m_materialized= false; + + /* + Build array of SHOW_VARs from system variable hash. Do this within + LOCK_plugin_delete to ensure that the hash table remains unchanged + during materialization. + */ + if (!m_external_init) + init_show_var_array(OPT_GLOBAL, true); + + /* Resolve the value for each SHOW_VAR in the array, add to cache. */ + for (SHOW_VAR *show_var= m_show_var_array.front(); + show_var->value && (show_var != m_show_var_array.end()); show_var++) + { + const char* name= show_var->name; + sys_var *value= (sys_var *)show_var->value; + assert(value); + + if ((m_query_scope == OPT_GLOBAL) && + (!my_strcasecmp(system_charset_info, name, "sql_log_bin"))) + { + /* + PLEASE READ: + http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-6.html + + SQL_LOG_BIN is: + - declared in sys_vars.cc as both GLOBAL and SESSION in 5.7 + - impossible to SET with SET GLOBAL (raises an error) + - and yet can be read with @@global.sql_log_bin + + When show_compatibility_56 = ON, + - SHOW GLOBAL VARIABLES does expose a row for SQL_LOG_BIN + - INFORMATION_SCHEMA.GLOBAL_VARIABLES also does expose a row, + both are for backward compatibility of existing applications, + so that no application logic change is required. + + Now, with show_compatibility_56 = OFF (aka, in this code) + - SHOW GLOBAL VARIABLES does -- not -- expose a row for SQL_LOG_BIN + - PERFORMANCE_SCHEMA.GLOBAL_VARIABLES also does -- not -- expose a row + so that a clean interface is exposed to (upgraded and modified) applications. + + The assert below will fail once SQL_LOG_BIN really is defined + as SESSION_ONLY (in 5.8), so that this special case can be removed. + */ + assert(value->scope() == sys_var::SESSION); + continue; + } + + /* Match the system variable scope to the target scope. */ + if (match_scope(value->scope())) + { + /* Resolve value, convert to text, add to cache. */ + System_variable system_var(m_current_thd, show_var, m_query_scope, false); + m_cache.push(system_var); + } + } + + m_materialized= true; + mysql_mutex_unlock(&LOCK_global_system_variables); + return 0; +} + +/** + Build a GLOBAL and SESSION system variable cache. +*/ +int PFS_system_variable_cache::do_materialize_all(THD *unsafe_thd) +{ + int ret= 1; + + m_unsafe_thd= unsafe_thd; + m_safe_thd= NULL; + m_materialized= false; + m_cache.clear(); + + /* Block plugins from unloading. */ + mysql_mutex_lock(&LOCK_plugin_delete); + + /* + Build array of SHOW_VARs from system variable hash. Do this within + LOCK_plugin_delete to ensure that the hash table remains unchanged + while this thread is materialized. + */ + if (!m_external_init) + init_show_var_array(OPT_SESSION, false); + + /* Get and lock a validated THD from the thread manager. */ + if ((m_safe_thd= get_THD(unsafe_thd)) != NULL) + { + DEBUG_SYNC(m_current_thd, "materialize_session_variable_array_THD_locked"); + for (SHOW_VAR *show_var= m_show_var_array.front(); + show_var->value && (show_var != m_show_var_array.end()); show_var++) + { + /* Resolve value, convert to text, add to cache. */ + System_variable system_var(m_safe_thd, show_var, m_query_scope, false); + m_cache.push(system_var); + } + + /* Release lock taken in get_THD(). */ + mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data); + + m_materialized= true; + ret= 0; + } + + mysql_mutex_unlock(&LOCK_plugin_delete); + return ret; +} + +/** + Allocate and assign mem_root for system variable materialization. +*/ +void PFS_system_variable_cache::set_mem_root(void) +{ + if (m_mem_sysvar_ptr == NULL) + { + init_sql_alloc(PSI_INSTRUMENT_ME, &m_mem_sysvar, SYSVAR_MEMROOT_BLOCK_SIZE, 0, 0); + m_mem_sysvar_ptr= &m_mem_sysvar; + } + m_mem_thd= ¤t_thd->mem_root; /* pointer to current THD mem_root */ + m_mem_thd_save= *m_mem_thd; /* restore later */ + *m_mem_thd= &m_mem_sysvar; /* use temporary mem_root */ +} + +/** + Mark memory blocks in the temporary mem_root as free. + Restore THD::mem_root. +*/ +void PFS_system_variable_cache::clear_mem_root(void) +{ + if (m_mem_sysvar_ptr) + { + free_root(&m_mem_sysvar, MYF(MY_MARK_BLOCKS_FREE)); + *m_mem_thd= m_mem_thd_save; /* restore original mem_root */ + m_mem_thd= NULL; + m_mem_thd_save= NULL; + } +} + +/** + Free the temporary mem_root. + Restore THD::mem_root if necessary. +*/ +void PFS_system_variable_cache::free_mem_root(void) +{ + if (m_mem_sysvar_ptr) + { + free_root(&m_mem_sysvar, MYF(0)); + m_mem_sysvar_ptr= NULL; + if (m_mem_thd && m_mem_thd_save) + { + *m_mem_thd= m_mem_thd_save; /* restore original mem_root */ + m_mem_thd= NULL; + m_mem_thd_save= NULL; + } + } +} + +/** + Build a SESSION system variable cache for a pfs_thread. + Requires that init_show_var_array() has already been called. + Return 0 for success. +*/ +int PFS_system_variable_cache::do_materialize_session(PFS_thread *pfs_thread) +{ + int ret= 1; + + m_pfs_thread= pfs_thread; + m_materialized= false; + m_cache.clear(); + + /* Block plugins from unloading. */ + mysql_mutex_lock(&LOCK_plugin_delete); + + /* The SHOW_VAR array must be initialized externally. */ + assert(m_initialized); + + /* Use a temporary mem_root to avoid depleting THD mem_root. */ + if (m_use_mem_root) + set_mem_root(); + + /* Get and lock a validated THD from the thread manager. */ + if ((m_safe_thd= get_THD(pfs_thread)) != NULL) + { + for (SHOW_VAR *show_var= m_show_var_array.front(); + show_var->value && (show_var != m_show_var_array.end()); show_var++) + { + sys_var *value= (sys_var *)show_var->value; + + /* Match the system variable scope to the target scope. */ + if (match_scope(value->scope())) + { + /* Resolve value, convert to text, add to cache. */ + System_variable system_var(m_safe_thd, show_var, m_query_scope, false); + m_cache.push(system_var); + } + } + + /* Release lock taken in get_THD(). */ + mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data); + + m_materialized= true; + ret= 0; + } + + /* Mark mem_root blocks as free. */ + if (m_use_mem_root) + clear_mem_root(); + + mysql_mutex_unlock(&LOCK_plugin_delete); + return ret; +} + +/** + Materialize a single system variable for a pfs_thread. + Requires that init_show_var_array() has already been called. + Return 0 for success. +*/ +int PFS_system_variable_cache::do_materialize_session(PFS_thread *pfs_thread, uint index) +{ + int ret= 1; + + m_pfs_thread= pfs_thread; + m_materialized= false; + m_cache.clear(); + + /* Block plugins from unloading. */ + mysql_mutex_lock(&LOCK_plugin_delete); + + /* The SHOW_VAR array must be initialized externally. */ + assert(m_initialized); + + /* Get and lock a validated THD from the thread manager. */ + if ((m_safe_thd= get_THD(pfs_thread)) != NULL) + { + SHOW_VAR *show_var= &m_show_var_array.at(index); + + if (show_var && show_var->value && + (show_var != m_show_var_array.end())) + { + sys_var *value= (sys_var *)show_var->value; + + /* Match the system variable scope to the target scope. */ + if (match_scope(value->scope())) + { + /* Resolve value, convert to text, add to cache. */ + System_variable system_var(m_safe_thd, show_var, m_query_scope, false); + m_cache.push(system_var); + } + } + + /* Release lock taken in get_THD(). */ + mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data); + + m_materialized= true; + ret= 0; + } + + mysql_mutex_unlock(&LOCK_plugin_delete); + return ret; +} + +/** + Build a SESSION system variable cache for a THD. +*/ +int PFS_system_variable_cache::do_materialize_session(THD *unsafe_thd) +{ + int ret= 1; + + m_unsafe_thd= unsafe_thd; + m_safe_thd= NULL; + m_materialized= false; + m_cache.clear(); + + /* Block plugins from unloading. */ + mysql_mutex_lock(&LOCK_plugin_delete); + + /* + Build array of SHOW_VARs from system variable hash. Do this within + LOCK_plugin_delete to ensure that the hash table remains unchanged + while this thread is materialized. + */ + if (!m_external_init) + init_show_var_array(OPT_SESSION, true); + + /* Get and lock a validated THD from the thread manager. */ + if ((m_safe_thd= get_THD(unsafe_thd)) != NULL) + { + for (SHOW_VAR *show_var= m_show_var_array.front(); + show_var->value && (show_var != m_show_var_array.end()); show_var++) + { + sys_var *value = (sys_var *)show_var->value; + + /* Match the system variable scope to the target scope. */ + if (match_scope(value->scope())) + { + /* Resolve value, convert to text, add to cache. */ + System_variable system_var(m_safe_thd, show_var, m_query_scope, false); + m_cache.push(system_var); + } + } + + /* Release lock taken in get_THD(). */ + mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data); + + m_materialized= true; + ret= 0; + } + + mysql_mutex_unlock(&LOCK_plugin_delete); + return ret; +} + + +/** + CLASS System_variable +*/ + +/** + Empty placeholder. +*/ +System_variable::System_variable() + : m_name(NULL), m_name_length(0), m_value_length(0), m_type(SHOW_UNDEF), m_scope(0), + m_ignore(false), m_charset(NULL), m_initialized(false) +{ + m_value_str[0]= '\0'; +} + +/** + GLOBAL or SESSION system variable. +*/ +System_variable::System_variable(THD *target_thd, const SHOW_VAR *show_var, + enum_var_type query_scope, bool ignore) + : m_name(NULL), m_name_length(0), m_value_length(0), m_type(SHOW_UNDEF), m_scope(0), + m_ignore(ignore), m_charset(NULL), m_initialized(false) +{ + init(target_thd, show_var, query_scope); +} + +/** + Get sys_var value from global or local source then convert to string. +*/ +void System_variable::init(THD *target_thd, const SHOW_VAR *show_var, + enum_var_type query_scope) +{ + if (show_var == NULL || show_var->name == NULL) + return; + + DBUG_ASSERT(show_var->type == SHOW_SYS); + + m_name= show_var->name; + m_name_length= strlen(m_name); + + /* Deprecated variables are ignored but must still be accounted for. */ + if (m_ignore) + { + m_value_str[0]= '\0'; + m_value_length= 0; + m_initialized= true; + return; + } + + /* Block remote target thread from updating this system variable. */ + /*XXX + THD *current_thread= current_thd; + if (target_thd != current_thread) + mysql_mutex_lock(&target_thd->LOCK_thd_sysvar);*/ + + sys_var *system_var= (sys_var *)show_var->value; + assert(system_var != NULL); + m_charset= system_var->charset(target_thd); + m_type= system_var->show_type(); + m_scope= system_var->scope(); + + /* Get the value of the system variable. */ + String buf(m_value_str, sizeof(m_value_str) - 1, system_charset_info); + if (!system_var->val_str_nolock(&buf, target_thd, + system_var->value_ptr(target_thd, query_scope, &null_clex_str))) + buf.length(0); + + m_value_length= MY_MIN(buf.length(), SHOW_VAR_FUNC_BUFF_SIZE); + + /* Returned value may reference a string other than m_value_str. */ + if (buf.ptr() != m_value_str) + memcpy(m_value_str, buf.ptr(), m_value_length); + m_value_str[m_value_length]= 0; + + /*XXX + if (target_thd != current_thread) + mysql_mutex_unlock(&target_thd->LOCK_thd_sysvar);*/ + + m_initialized= true; +} + + +/** + CLASS PFS_status_variable_cache +*/ + +PFS_status_variable_cache:: +PFS_status_variable_cache(bool external_init) : + PFS_variable_cache<Status_variable>(external_init), + m_show_command(false), m_sum_client_status(NULL) +{ + /* Determine if the originating query is a SHOW command. */ + m_show_command= (m_current_thd->lex->sql_command == SQLCOM_SHOW_STATUS); +} + +/** + Build cache of SESSION status variables for a user. +*/ +int PFS_status_variable_cache::materialize_user(PFS_user *pfs_user) +{ + if (!pfs_user) + return 1; + + if (is_materialized(pfs_user)) + return 0; + + if (!pfs_user->m_lock.is_populated()) + return 1; + + /* Set callback function. */ + m_sum_client_status= sum_user_status; + return do_materialize_client((PFS_client *)pfs_user); +} + +/** + Build cache of SESSION status variables for a host. +*/ +int PFS_status_variable_cache::materialize_host(PFS_host *pfs_host) +{ + if (!pfs_host) + return 1; + + if (is_materialized(pfs_host)) + return 0; + + if (!pfs_host->m_lock.is_populated()) + return 1; + + /* Set callback function. */ + m_sum_client_status= sum_host_status; + return do_materialize_client((PFS_client *)pfs_host); +} + +/** + Build cache of SESSION status variables for an account. +*/ +int PFS_status_variable_cache::materialize_account(PFS_account *pfs_account) +{ + if (!pfs_account) + return 1; + + if (is_materialized(pfs_account)) + return 0; + + if (!pfs_account->m_lock.is_populated()) + return 1; + + /* Set callback function. */ + m_sum_client_status= sum_account_status; + return do_materialize_client((PFS_client *)pfs_account); +} +/** + Compare status variable scope to desired scope. + @param variable_scope Scope of current status variable + @return TRUE if variable matches the query scope +*/ +bool PFS_status_variable_cache::match_scope(SHOW_SCOPE variable_scope, bool strict) +{ + switch (variable_scope) + { + case SHOW_SCOPE_GLOBAL: + return (m_query_scope == OPT_GLOBAL) || (! strict && (m_query_scope == OPT_SESSION)); + break; + case SHOW_SCOPE_SESSION: + /* Ignore session-only vars if aggregating by user, host or account. */ + if (m_aggregate) + return false; + else + return (m_query_scope == OPT_SESSION); + break; + case SHOW_SCOPE_ALL: + return (m_query_scope == OPT_GLOBAL || m_query_scope == OPT_SESSION); + break; + case SHOW_SCOPE_UNDEF: + default: + return false; + break; + } + return false; +} + +/* + Exclude specific status variables from the query by name or prefix. + Return TRUE if variable should be filtered. +*/ +bool PFS_status_variable_cache::filter_by_name(const SHOW_VAR *show_var) +{ + assert(show_var); + assert(show_var->name); + + if (show_var->type == SHOW_ARRAY) + { + /* The SHOW_ARRAY name is the prefix for the variables in the subarray. */ + const char *prefix= show_var->name; + /* Exclude COM counters if not a SHOW STATUS command. */ + if (!my_strcasecmp(system_charset_info, prefix, "Com") && !m_show_command) + return true; + } + else + { + /* + Slave status resides in Performance Schema replication tables. Exclude + these slave status variables from the SHOW STATUS command and from the + status tables. + Assume null prefix to ensure that only server-defined slave status + variables are filtered. + */ + const char *name= show_var->name; + if (!my_strcasecmp(system_charset_info, name, "Slave_running") || + !my_strcasecmp(system_charset_info, name, "Slave_retried_transactions") || + !my_strcasecmp(system_charset_info, name, "Slave_last_heartbeat") || + !my_strcasecmp(system_charset_info, name, "Slave_received_heartbeats") || + !my_strcasecmp(system_charset_info, name, "Slave_heartbeat_period")) + { + return true; + } + } + + return false; +} + +/** + Check that the variable type is aggregatable. + + @param variable_type Status variable type + @return TRUE if variable type can be aggregated +*/ +bool PFS_status_variable_cache::can_aggregate(enum_mysql_show_type variable_type) +{ + switch(variable_type) + { + /* + All server status counters that are totaled across threads are defined in + system_status_var as either SHOW_LONGLONG_STATUS or SHOW_LONG_STATUS. + These data types are not available to plugins. + */ + case SHOW_LONGLONG_STATUS: + case SHOW_LONG_STATUS: + return true; + break; + + /* Server and plugin */ + case SHOW_UNDEF: + case SHOW_BOOL: + case SHOW_CHAR: + case SHOW_CHAR_PTR: + case SHOW_ARRAY: + case SHOW_FUNC: + case SHOW_INT: + case SHOW_LONG: + case SHOW_LONGLONG: + case SHOW_DOUBLE: + /* Server only */ + case SHOW_HAVE: + case SHOW_MY_BOOL: + case SHOW_SYS: + case SHOW_LEX_STRING: + case SHOW_KEY_CACHE_LONG: + case SHOW_DOUBLE_STATUS: + case SHOW_HA_ROWS: + case SHOW_LONG_NOFLUSH: + case SHOW_SLONG: + default: + return false; + break; + } +} + +/** + Check if a status variable should be excluded from the query. + Return TRUE if the variable should be excluded. +*/ +bool PFS_status_variable_cache::filter_show_var(const SHOW_VAR *show_var, bool strict) +{ + /* Match the variable scope with the query scope. */ + if (!match_scope(show_scope_from_type(show_var->type), strict)) + return true; + + /* Exclude specific status variables by name or prefix. */ + if (filter_by_name(show_var)) + return true; + + /* For user, host or account, ignore variables having non-aggregatable types. */ + if (m_aggregate && !can_aggregate(show_var->type)) + return true; + + return false; +} + + +/** + Build an array of SHOW_VARs from the global status array. Expand nested + subarrays, filter unwanted variables. + NOTE: Must be done inside of LOCK_status to guard against plugin load/unload. +*/ +bool PFS_status_variable_cache::init_show_var_array(enum_var_type scope, bool strict) +{ + assert(!m_initialized); + + /* Resize if necessary. */ + m_show_var_array.reserve(all_status_vars.elements + 1); + + m_query_scope= scope; + + for (SHOW_VAR *show_var_iter= dynamic_element(&all_status_vars, 0, SHOW_VAR *); + show_var_iter != dynamic_element(&all_status_vars, all_status_vars.elements, SHOW_VAR *); + show_var_iter++) + { + SHOW_VAR show_var= *show_var_iter; + + /* Check if this status var should be excluded from the query. */ + if (filter_show_var(&show_var, strict)) + continue; + + if (show_var.type == SHOW_ARRAY) + { + /* Expand nested subarray. The name is used as a prefix. */ + expand_show_var_array((SHOW_VAR *)show_var.value, show_var.name, strict); + } + else + { + show_var.name= make_show_var_name(NULL, show_var.name); + m_show_var_array.push(show_var); + } + } + + /* Last element is NULL. */ + st_mysql_show_var empty= {0,0,SHOW_UNDEF}; + m_show_var_array.push(empty); + + /* Get the latest version of all_status_vars. */ + m_version= get_status_vars_version(); + + /* Increase cache size if necessary. */ + m_cache.reserve(m_show_var_array.elements()); + + m_initialized= true; + return true; +} + +/** + Expand a nested subarray of status variables, indicated by a type of SHOW_ARRAY. +*/ +void PFS_status_variable_cache::expand_show_var_array(const SHOW_VAR *show_var_array, const char *prefix, bool strict) +{ + for (const SHOW_VAR *show_var_ptr= show_var_array; + show_var_ptr && show_var_ptr->name; + show_var_ptr++) + { + SHOW_VAR show_var= *show_var_ptr; + + if (filter_show_var(&show_var, strict)) + continue; + + if (show_var.type == SHOW_ARRAY) + { + char name_buf[SHOW_VAR_MAX_NAME_LEN]; + show_var.name= make_show_var_name(prefix, show_var.name, name_buf, sizeof(name_buf)); + /* Expand nested subarray. The name is used as a prefix. */ + expand_show_var_array((SHOW_VAR *)show_var.value, show_var.name, strict); + } + else + { + /* Add the SHOW_VAR element. Make a local copy of the name string. */ + show_var.name= make_show_var_name(prefix, show_var.name); + m_show_var_array.push(show_var); + } + } +} + +/** + Build the complete status variable name, with prefix. Return in buffer provided. +*/ +char * PFS_status_variable_cache::make_show_var_name(const char* prefix, const char* name, + char *name_buf, size_t buf_len) +{ + assert(name_buf != NULL); + char *prefix_end= name_buf; + + if (prefix && *prefix) + { + /* Drop the prefix into the front of the name buffer. */ + prefix_end= my_stpnmov(name_buf, prefix, buf_len-1); + *prefix_end++= '_'; + } + + /* Restrict name length to remaining buffer size. */ + size_t max_name_len= name_buf + buf_len - prefix_end; + + /* Load the name into the buffer after the prefix. */ + my_stpnmov(prefix_end, name, max_name_len); + name_buf[buf_len-1]= 0; + + return (name_buf); +} + +/** + Make a copy of the name string prefixed with the subarray name if necessary. +*/ +char * PFS_status_variable_cache::make_show_var_name(const char* prefix, const char* name) +{ + char name_buf[SHOW_VAR_MAX_NAME_LEN]; + size_t buf_len= sizeof(name_buf); + make_show_var_name(prefix, name, name_buf, buf_len); + return m_current_thd->strdup(name_buf); /* freed at statement end */ +} + +/** + Build an internal SHOW_VAR array from the external status variable array. +*/ +bool PFS_status_variable_cache::do_initialize_session(void) +{ + /* Acquire LOCK_status to guard against plugin load/unload. */ + //if (m_current_thd->fill_status_recursion_level++ == 0) + mysql_mutex_lock(&LOCK_status); + + bool ret= init_show_var_array(OPT_SESSION, true); + + //if (m_current_thd->fill_status_recursion_level-- == 1) + mysql_mutex_unlock(&LOCK_status); + + return ret; +} + +/** + For the current THD, use initial_status_vars taken from before the query start. +*/ +STATUS_VAR *PFS_status_variable_cache::set_status_vars(void) +{ + STATUS_VAR *status_vars; + if (m_safe_thd == m_current_thd && m_current_thd->initial_status_var != NULL) + status_vars= m_current_thd->initial_status_var; + else + status_vars= &m_safe_thd->status_var; + + return status_vars; +} + +/** + Build cache for GLOBAL status variables using values totaled from all threads. +*/ +int PFS_status_variable_cache::do_materialize_global(void) +{ + STATUS_VAR status_totals; + + m_materialized= false; + DEBUG_SYNC(m_current_thd, "before_materialize_global_status_array"); + + /* Acquire LOCK_status to guard against plugin load/unload. */ + //if (m_current_thd->fill_status_recursion_level++ == 0) + mysql_mutex_lock(&LOCK_status); + + /* + Build array of SHOW_VARs from global status array. Do this within + LOCK_status to ensure that the array remains unchanged during + materialization. + */ + if (!m_external_init) + init_show_var_array(OPT_GLOBAL, true); + + /* + Collect totals for all active threads. Start with global status vars as a + baseline. + */ + PFS_connection_status_visitor visitor(&status_totals); + PFS_connection_iterator::visit_global(false, /* hosts */ + false, /* users */ + false, /* accounts */ + false, /* threads */ + true, /* THDs */ + &visitor); + /* + Build the status variable cache using the SHOW_VAR array as a reference. + Use the status totals collected from all threads. + */ + manifest(m_current_thd, m_show_var_array.front(), &status_totals, "", false, true); + + //if (m_current_thd->fill_status_recursion_level-- == 1) + mysql_mutex_unlock(&LOCK_status); + + m_materialized= true; + DEBUG_SYNC(m_current_thd, "after_materialize_global_status_array"); + + return 0; +} + +/** + Build GLOBAL and SESSION status variable cache using values for a non-instrumented thread. +*/ +int PFS_status_variable_cache::do_materialize_all(THD* unsafe_thd) +{ + int ret= 1; + assert(unsafe_thd != NULL); + + m_unsafe_thd= unsafe_thd; + m_materialized= false; + m_cache.clear(); + + /* Avoid recursive acquisition of LOCK_status. */ + //if (m_current_thd->fill_status_recursion_level++ == 0) + mysql_mutex_lock(&LOCK_status); + + /* + Build array of SHOW_VARs from global status array. Do this within + LOCK_status to ensure that the array remains unchanged while this + thread is materialized. + */ + if (!m_external_init) + init_show_var_array(OPT_SESSION, false); + + /* Get and lock a validated THD from the thread manager. */ + if ((m_safe_thd= get_THD(unsafe_thd)) != NULL) + { + /* + Build the status variable cache using the SHOW_VAR array as a reference. + Use the status values from the THD protected by the thread manager lock. + */ + STATUS_VAR *status_vars= set_status_vars(); + manifest(m_safe_thd, m_show_var_array.front(), status_vars, "", false, false); + + /* Release lock taken in get_THD(). */ + mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data); + + m_materialized= true; + ret= 0; + } + + //if (m_current_thd->fill_status_recursion_level-- == 1) + mysql_mutex_unlock(&LOCK_status); + return ret; +} + +/** + Build SESSION status variable cache using values for a non-instrumented thread. +*/ +int PFS_status_variable_cache::do_materialize_session(THD* unsafe_thd) +{ + int ret= 1; + assert(unsafe_thd != NULL); + + m_unsafe_thd= unsafe_thd; + m_materialized= false; + m_cache.clear(); + + /* Avoid recursive acquisition of LOCK_status. */ + //if (m_current_thd->fill_status_recursion_level++ == 0) + mysql_mutex_lock(&LOCK_status); + + /* + Build array of SHOW_VARs from global status array. Do this within + LOCK_status to ensure that the array remains unchanged while this + thread is materialized. + */ + if (!m_external_init) + init_show_var_array(OPT_SESSION, true); + + /* Get and lock a validated THD from the thread manager. */ + if ((m_safe_thd= get_THD(unsafe_thd)) != NULL) + { + /* + Build the status variable cache using the SHOW_VAR array as a reference. + Use the status values from the THD protected by the thread manager lock. + */ + STATUS_VAR *status_vars= set_status_vars(); + manifest(m_safe_thd, m_show_var_array.front(), status_vars, "", false, true); + + /* Release lock taken in get_THD(). */ + mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data); + + m_materialized= true; + ret= 0; + } + + //if (m_current_thd->fill_status_recursion_level-- == 1) + mysql_mutex_unlock(&LOCK_status); + return ret; +} + +/** + Build SESSION status variable cache using values for a PFS_thread. + NOTE: Requires that init_show_var_array() has already been called. +*/ +int PFS_status_variable_cache::do_materialize_session(PFS_thread *pfs_thread) +{ + int ret= 1; + assert(pfs_thread != NULL); + + m_pfs_thread= pfs_thread; + m_materialized= false; + m_cache.clear(); + + /* Acquire LOCK_status to guard against plugin load/unload. */ + //if (m_current_thd->fill_status_recursion_level++ == 0) + mysql_mutex_lock(&LOCK_status); + + /* The SHOW_VAR array must be initialized externally. */ + assert(m_initialized); + + /* Get and lock a validated THD from the thread manager. */ + if ((m_safe_thd= get_THD(pfs_thread)) != NULL) + { + /* + Build the status variable cache using the SHOW_VAR array as a reference. + Use the status values from the THD protected by the thread manager lock. + */ + STATUS_VAR *status_vars= set_status_vars(); + manifest(m_safe_thd, m_show_var_array.front(), status_vars, "", false, true); + + /* Release lock taken in get_THD(). */ + mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data); + + m_materialized= true; + ret= 0; + } + + //if (m_current_thd->fill_status_recursion_level-- == 1) + mysql_mutex_unlock(&LOCK_status); + return ret; +} + +/** + Build cache of SESSION status variables using the status values provided. + The cache is associated with a user, host or account, but not with any + particular thread. + NOTE: Requires that init_show_var_array() has already been called. +*/ +int PFS_status_variable_cache::do_materialize_client(PFS_client *pfs_client) +{ + assert(pfs_client != NULL); + STATUS_VAR status_totals; + + m_pfs_client= pfs_client; + m_materialized= false; + m_cache.clear(); + + /* Acquire LOCK_status to guard against plugin load/unload. */ + //if (m_current_thd->fill_status_recursion_level++ == 0) + mysql_mutex_lock(&LOCK_status); + + /* The SHOW_VAR array must be initialized externally. */ + assert(m_initialized); + + /* + Generate status totals from active threads and from totals aggregated + from disconnected threads. + */ + m_sum_client_status(pfs_client, &status_totals); + + /* + Build the status variable cache using the SHOW_VAR array as a reference and + the status totals collected from threads associated with this client. + */ + manifest(m_current_thd, m_show_var_array.front(), &status_totals, "", false, true); + + //if (m_current_thd->fill_status_recursion_level-- == 1) + mysql_mutex_unlock(&LOCK_status); + + m_materialized= true; + return 0; +} + +/* + Build the status variable cache from the expanded and sorted SHOW_VAR array. + Resolve status values using the STATUS_VAR struct provided. +*/ +void PFS_status_variable_cache::manifest(THD *thd, const SHOW_VAR *show_var_array, + STATUS_VAR *status_vars, const char *prefix, + bool nested_array, bool strict) +{ + for (const SHOW_VAR *show_var_iter= show_var_array; + show_var_iter && show_var_iter->name; + show_var_iter++) + { + // work buffer, must be aligned to handle long/longlong values + my_aligned_storage<SHOW_VAR_FUNC_BUFF_SIZE+1, MY_ALIGNOF(longlong)> + value_buf; + SHOW_VAR show_var_tmp; + const SHOW_VAR *show_var_ptr= show_var_iter; /* preserve array pointer */ + + /* + If the value is a function reference, then execute the function and + reevaluate the new SHOW_TYPE and value. Handle nested case where + SHOW_FUNC resolves to another SHOW_FUNC. + */ + if (show_var_ptr->type == SHOW_FUNC) + { + show_var_tmp= *show_var_ptr; + /* + Execute the function reference in show_var_tmp->value, which returns + show_var_tmp with a new type and new value. + */ + for (const SHOW_VAR *var= show_var_ptr; var->type == SHOW_FUNC; var= &show_var_tmp) + { + ((mysql_show_var_func)(var->value))(thd, &show_var_tmp, value_buf.data, NULL, m_query_scope); + } + show_var_ptr= &show_var_tmp; + } + + /* + If we are expanding a SHOW_ARRAY, filter variables that were not prefiltered by + init_show_var_array(). + */ + if (nested_array && filter_show_var(show_var_ptr, strict)) + continue; + + if (show_var_ptr->type == SHOW_ARRAY) + { + /* + Status variables of type SHOW_ARRAY were expanded and filtered by + init_show_var_array(), except where a SHOW_FUNC resolves into a + SHOW_ARRAY, such as with InnoDB. Recurse to expand the subarray. + */ + manifest(thd, (SHOW_VAR *)show_var_ptr->value, status_vars, show_var_ptr->name, true, strict); + } + else + { + /* Add the materialized status variable to the cache. */ + SHOW_VAR show_var= *show_var_ptr; + /* + For nested array expansions, make a copy of the variable name, just as + done in init_show_var_array(). + */ + if (nested_array) + show_var.name= make_show_var_name(prefix, show_var_ptr->name); + + /* Convert status value to string format. Add to the cache. */ + Status_variable status_var(&show_var, status_vars, m_query_scope); + m_cache.push(status_var); + } + } +} + +/** + CLASS Status_variable +*/ +Status_variable::Status_variable(const SHOW_VAR *show_var, STATUS_VAR *status_vars, enum_var_type query_scope) + : m_name_length(0), m_value_length(0), m_type(SHOW_UNDEF), + m_charset(NULL), m_initialized(false) +{ + init(show_var, status_vars, query_scope); +} + +/** + Resolve status value, convert to string. + show_var->value is an offset into status_vars. + NOTE: Assumes LOCK_status is held. +*/ +void Status_variable::init(const SHOW_VAR *show_var, STATUS_VAR *status_vars, enum_var_type query_scope) +{ + if (show_var == NULL || show_var->name == NULL) + return; + m_name= show_var->name; + m_name_length= strlen(m_name); + m_type= show_var->type; + + /* Get the value of the status variable. */ + const char *value; + value= get_one_variable(current_thd, show_var, query_scope, m_type, + status_vars, &m_charset, m_value_str, &m_value_length); + m_value_length= MY_MIN(m_value_length, SHOW_VAR_FUNC_BUFF_SIZE); + m_charset= system_charset_info; + + /* Returned value may reference a string other than m_value_str. */ + if (value != m_value_str) + memcpy(m_value_str, value, m_value_length); + m_value_str[m_value_length]= 0; + + m_initialized= true; +} + +/* + Get status totals for this user from active THDs and related accounts. +*/ +void sum_user_status(PFS_client *pfs_user, STATUS_VAR *status_totals) +{ + PFS_connection_status_visitor visitor(status_totals); + PFS_connection_iterator::visit_user((PFS_user *)pfs_user, + true, /* accounts */ + false, /* threads */ + true, /* THDs */ + &visitor); +} + +/* + Get status totals for this host from active THDs and related accounts. +*/ +void sum_host_status(PFS_client *pfs_host, STATUS_VAR *status_totals) +{ + PFS_connection_status_visitor visitor(status_totals); + PFS_connection_iterator::visit_host((PFS_host *)pfs_host, + true, /* accounts */ + false, /* threads */ + true, /* THDs */ + &visitor); +} + +/* + Get status totals for this account from active THDs and from totals aggregated + from disconnectd threads. +*/ +void sum_account_status(PFS_client *pfs_account, STATUS_VAR *status_totals) +{ + PFS_connection_status_visitor visitor(status_totals); + PFS_connection_iterator::visit_account((PFS_account *)pfs_account, + false, /* threads */ + true, /* THDs */ + &visitor); +} + +/** + Reset aggregated status counter stats for account, user and host. + NOTE: Assumes LOCK_status is held. +*/ +void reset_pfs_status_stats() +{ + reset_status_by_account(); + reset_status_by_user(); + reset_status_by_host(); +} + +/** @} */ diff --git a/storage/perfschema/pfs_variable.h b/storage/perfschema/pfs_variable.h new file mode 100644 index 00000000..d3ad4c7f --- /dev/null +++ b/storage/perfschema/pfs_variable.h @@ -0,0 +1,716 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + +#ifndef PFS_VARIABLE_H +#define PFS_VARIABLE_H + +/** + @file storage/perfschema/pfs_variable.h + Performance schema system and status variables (declarations). +*/ + +/** + OVERVIEW + -------- + Status and system variables are implemented differently in the server, but the + steps to process them in the Performance Schema are essentially the same: + + 1. INITIALIZE - Build or acquire a sorted list of variables to use for input. + Use the SHOW_VAR struct as an intermediate format common to system, status + and user vars: + + SHOW_VAR + Name - Text string + Value - Pointer to memory location, function, subarray structure + Type - Scalar, function, or subarray + Scope - SESSION, GLOBAL, BOTH + + Steps: + - Register the server's internal buffer with the class. Acquire locks + if necessary, then scan the contents of the input buffer. + - For system variables, convert each element to SHOW_VAR format, store in + a temporary array. + - For status variables, copy existing global status array into a local + array that can be used without locks. Expand nested subarrays, indicated + by a type of SHOW_ARRAY. + + 2. MATERIALIZE - Convert the list of SHOW_VAR variables to string format, + store in a local cache: + - Resolve each variable according to the type. + - Recursively process unexpanded nested arrays and callback functions. + - Aggregate values across threads for global status. + - Convert numeric values to a string. + - Prefix variable name with the plugin name. + + 3. OUTPUT - Iterate the cache for the SHOW command or table query. + + CLASS OVERVIEW + -------------- + 1. System_variable - A materialized system variable + 2. Status_variable - A materialized status variable + 3. PFS_variable_cache - Base class that defines the interface for the operations above. + public + init_show_var_array() - Build SHOW_VAR list of variables for processing + materialize_global() - Materialize global variables, aggregate across sessions + materialize_session() - Materialize variables for a given PFS_thread or THD + materialize_user() - Materialize variables for a user, aggregate across related threads. + materialize_host() - Materialize variables for a host, aggregate across related threads. + materialize_account() - Materialize variables for a account, aggregate across related threads. + private + m_show_var_array - Prealloc_array of SHOW_VARs for input to Materialize + m_cache - Prealloc_array of materialized variables for output + do_materialize_global() - Implementation of materialize_global() + do_materialize_session() - Implementation of materialize_session() + do_materialize_client() - Implementation of materialize_user/host/account() + + 4. PFS_system_variable_cache - System variable implementation of PFS_variable_cache + 5. PFS_status_variable_cache - Status variable implementation of PFS_variable_cache + 6. Find_THD_variable - Used by the thread manager to find and lock a THD. + + GLOSSARY + -------- + Status variable - Server or plugin status counter. Not dynamic. + System variable - Server or plugin configuration variable. Usually dynamic. + GLOBAL scope - Associated with the server, no context at thread level. + SESSION scope - Associated with a connection or thread, but no global context. + BOTH scope - Globally defined but applies at the session level. + Initialize - Build list of variables in SHOW_VAR format. + Materialize - Convert variables in SHOW_VAR list to string, cache for output. + Manifest - Substep of Materialize. Resolve variable values according to + type. This includes SHOW_FUNC types which are resolved by + executing a callback function (possibly recursively), and + SHOW_ARRAY types that expand into nested subarrays. + LOCK PRIORITIES + --------------- + System Variables + LOCK_plugin_delete (block plugin delete) + LOCK_system_variables_hash + LOCK_thd_data (block THD delete) + LOCK_thd_sysvar (block system variable updates, alloc_and_copy_thd_dynamic_variables) + LOCK_global_system_variables (very briefly held) + + Status Variables + LOCK_status + LOCK_thd_data (block THD delete) +*/ + +/* Iteration on THD from the sql layer. */ +#include "mysqld_thd_manager.h" +#define PFS_VAR +/* Class sys_var */ +#include "set_var.h" +/* convert_value_to_string */ +#include "sql_show.h" +/* PFS_thread */ +#include "pfs_instr.h" +#include "pfs_user.h" +#include "pfs_host.h" +#include "pfs_account.h" + +/* Global array of all server and plugin-defined status variables. */ +extern DYNAMIC_ARRAY all_status_vars; +extern bool status_vars_inited; +static const uint SYSVAR_MEMROOT_BLOCK_SIZE = 4096; + +extern mysql_mutex_t LOCK_plugin_delete; + +class Find_THD_Impl; +class Find_THD_variable; +typedef PFS_connection_slice PFS_client; + +/** + CLASS System_variable - System variable derived from sys_var object. +*/ +class System_variable +{ +public: + System_variable(); + System_variable(THD *target_thd, const SHOW_VAR *show_var, + enum_var_type query_scope, bool ignore); + ~System_variable() {} + + bool is_null() const { return !m_initialized; } + bool is_ignored() const { return m_ignore; } + +public: + const char *m_name; + size_t m_name_length; + char m_value_str[SHOW_VAR_FUNC_BUFF_SIZE+1]; + size_t m_value_length; + enum_mysql_show_type m_type; + int m_scope; + bool m_ignore; + const CHARSET_INFO *m_charset; + +private: + bool m_initialized; + void init(THD *thd, const SHOW_VAR *show_var, enum_var_type query_scope); +}; + + +/** + CLASS Status_variable - Status variable derived from SHOW_VAR. +*/ +class Status_variable +{ +public: + Status_variable() : m_name(NULL), m_name_length(0), m_value_length(0), + m_type(SHOW_UNDEF), + m_charset(NULL), m_initialized(false) {} + + Status_variable(const SHOW_VAR *show_var, STATUS_VAR *status_array, enum_var_type query_scope); + + ~Status_variable() {} + + bool is_null() const {return !m_initialized;}; + +public: + const char *m_name; + size_t m_name_length; + char m_value_str[SHOW_VAR_FUNC_BUFF_SIZE+1]; + size_t m_value_length; + SHOW_TYPE m_type; + const CHARSET_INFO *m_charset; +private: + bool m_initialized; + void init(const SHOW_VAR *show_var, STATUS_VAR *status_array, enum_var_type query_scope); +}; + + +/** + CLASS Find_THD_variable - Get and lock a validated THD from the thread manager. +*/ +class Find_THD_variable : public Find_THD_Impl +{ +public: + Find_THD_variable() : m_unsafe_thd(NULL) {} + Find_THD_variable(THD *unsafe_thd) : m_unsafe_thd(unsafe_thd) {} + + virtual bool operator()(THD *thd) + { + //TODO: filter bg threads? + if (thd != m_unsafe_thd) + return false; + + /* Hold this lock to keep THD during materialization. */ + mysql_mutex_lock(&thd->LOCK_thd_data); + return true; + } + void set_unsafe_thd(THD *unsafe_thd) { m_unsafe_thd= unsafe_thd; } +private: + THD *m_unsafe_thd; +}; + +/** + CLASS PFS_variable_cache - Base class for a system or status variable cache. +*/ +template <class Var_type> +class PFS_variable_cache +{ +public: + typedef Dynamic_array<Var_type> Variable_array; + + PFS_variable_cache(bool external_init) + : m_safe_thd(NULL), + m_unsafe_thd(NULL), + m_current_thd(current_thd), + m_pfs_thread(NULL), + m_pfs_client(NULL), + m_thd_finder(), + m_cache(PSI_INSTRUMENT_MEM), + m_initialized(false), + m_external_init(external_init), + m_materialized(false), + m_show_var_array(PSI_INSTRUMENT_MEM), + m_version(0), + m_query_scope(OPT_DEFAULT), + m_use_mem_root(false), + m_aggregate(false) + { } + + virtual ~PFS_variable_cache()= 0; + + /** + Build array of SHOW_VARs from the external variable source. + Filter using session scope. + */ + bool initialize_session(void); + + /** + Build array of SHOW_VARs suitable for aggregation by user, host or account. + Filter using session scope. + */ + bool initialize_client_session(void); + + /** + Build cache of GLOBAL system or status variables. + Aggregate across threads if applicable. + */ + int materialize_global(); + + /** + Build cache of GLOBAL and SESSION variables for a non-instrumented thread. + */ + int materialize_all(THD *thd); + + /** + Build cache of SESSION variables for a non-instrumented thread. + */ + int materialize_session(THD *thd); + + /** + Build cache of SESSION variables for an instrumented thread. + */ + int materialize_session(PFS_thread *pfs_thread, bool use_mem_root= false); + + /** + Cache a single SESSION variable for an instrumented thread. + */ + int materialize_session(PFS_thread *pfs_thread, uint index); + + /** + Build cache of SESSION status variables for a user. + */ + int materialize_user(PFS_user *pfs_user); + + /** + Build cache of SESSION status variables for a host. + */ + int materialize_host(PFS_host *pfs_host); + + /** + Build cache of SESSION status variables for an account. + */ + int materialize_account(PFS_account *pfs_account); + + /** + True if variables have been materialized. + */ + bool is_materialized(void) + { + return m_materialized; + } + + /** + True if variables have been materialized for given THD. + */ + bool is_materialized(THD *unsafe_thd) + { + return (unsafe_thd == m_unsafe_thd && m_materialized); + } + + /** + True if variables have been materialized for given PFS_thread. + */ + bool is_materialized(PFS_thread *pfs_thread) + { + return (pfs_thread == m_pfs_thread && m_materialized); + } + + /** + True if variables have been materialized for given PFS_user. + */ + bool is_materialized(PFS_user *pfs_user) + { + return (static_cast<PFS_client *>(pfs_user) == m_pfs_client && m_materialized); + } + + /** + True if variables have been materialized for given PFS_host. + */ + bool is_materialized(PFS_host *pfs_host) + { + return (static_cast<PFS_client *>(pfs_host) == m_pfs_client && m_materialized); + } + + /** + True if variables have been materialized for given PFS_account. + */ + bool is_materialized(PFS_account *pfs_account) + { + return (static_cast<PFS_client *>(pfs_account) == m_pfs_client && m_materialized); + } + + /** + True if variables have been materialized for given PFS_user/host/account. + */ + bool is_materialized(PFS_client *pfs_client) + { + return (static_cast<PFS_client *>(pfs_client) == m_pfs_client && m_materialized); + } + + /** + Get a validated THD from the thread manager. Execute callback function while + inside of the thread manager locks. + */ + THD *get_THD(THD *thd); + THD *get_THD(PFS_thread *pfs_thread); + + /** + Get a single variable from the cache. + Get the first element in the cache by default. + */ + const Var_type *get(uint index= 0) const + { + if (index >= m_cache.elements()) + return NULL; + + const Var_type *p= &m_cache.at(index); + return p; + } + + /** + Number of elements in the cache. + */ + uint size() + { + return (uint)m_cache.elements(); + } + +private: + virtual bool do_initialize_global(void) { return true; } + virtual bool do_initialize_session(void) { return true; } + virtual int do_materialize_global(void) { return 1; } + virtual int do_materialize_all(THD *thd) { return 1; } + virtual int do_materialize_session(THD *thd) { return 1; } + virtual int do_materialize_session(PFS_thread *) { return 1; } + virtual int do_materialize_session(PFS_thread *, uint index) { return 1; } + +protected: + /* Validated THD */ + THD *m_safe_thd; + + /* Unvalidated THD */ + THD *m_unsafe_thd; + + /* Current THD */ + THD *m_current_thd; + + /* Current PFS_thread. */ + PFS_thread *m_pfs_thread; + + /* Current PFS_user, host or account. */ + PFS_client *m_pfs_client; + + /* Callback for thread iterator. */ + Find_THD_variable m_thd_finder; + + /* Cache of materialized variables. */ + Variable_array m_cache; + + /* True when list of SHOW_VAR is complete. */ + bool m_initialized; + + /* + True if the SHOW_VAR array must be initialized externally from the + materialization step, such as with aggregations and queries by thread. + */ + bool m_external_init; + + /* True when cache is complete. */ + bool m_materialized; + + /* Array of variables to be materialized. Last element must be null. */ + Dynamic_array<SHOW_VAR> m_show_var_array; + + /* Version of global hash/array. Changes when vars added/removed. */ + ulonglong m_version; + + /* Query scope: GLOBAL or SESSION. */ + enum_var_type m_query_scope; + + /* True if temporary mem_root should be used for materialization. */ + bool m_use_mem_root; + + /* True if summarizing across users, hosts or accounts. */ + bool m_aggregate; + +}; + +/** + Required implementation for pure virtual destructor of a template class. +*/ +template <class Var_type> +PFS_variable_cache<Var_type>::~PFS_variable_cache() +{ +} + +/** + Get a validated THD from the thread manager. Execute callback function while + while inside the thread manager lock. +*/ +template <class Var_type> +THD *PFS_variable_cache<Var_type>::get_THD(THD *unsafe_thd) +{ + if (unsafe_thd == NULL) + { + /* + May happen, precisely because the pointer is unsafe + (THD just disconnected for example). + No need to walk Global_THD_manager for that. + */ + return NULL; + } + + m_thd_finder.set_unsafe_thd(unsafe_thd); + THD* safe_thd= Global_THD_manager::get_instance()->find_thd(&m_thd_finder); + return safe_thd; +} + +template <class Var_type> +THD *PFS_variable_cache<Var_type>::get_THD(PFS_thread *pfs_thread) +{ + assert(pfs_thread != NULL); + return get_THD(pfs_thread->m_thd); +} + +/** + Build array of SHOW_VARs from external source of system or status variables. + Filter using session scope. +*/ +template <class Var_type> +bool PFS_variable_cache<Var_type>::initialize_session(void) +{ + if (m_initialized) + return 0; + + return do_initialize_session(); +} + +/** + Build array of SHOW_VARs suitable for aggregation by user, host or account. + Filter using session scope. +*/ +template <class Var_type> +bool PFS_variable_cache<Var_type>::initialize_client_session(void) +{ + if (m_initialized) + return 0; + + /* Requires aggregation by user, host or account. */ + m_aggregate= true; + + return do_initialize_session(); +} + +/** + Build cache of all GLOBAL variables. +*/ +template <class Var_type> +int PFS_variable_cache<Var_type>::materialize_global() +{ + if (is_materialized()) + return 0; + + return do_materialize_global(); +} + +/** + Build cache of GLOBAL and SESSION variables for a non-instrumented thread. +*/ +template <class Var_type> +int PFS_variable_cache<Var_type>::materialize_all(THD *unsafe_thd) +{ + if (!unsafe_thd) + return 1; + + if (is_materialized(unsafe_thd)) + return 0; + + return do_materialize_all(unsafe_thd); +} + +/** + Build cache of SESSION variables for a non-instrumented thread. +*/ +template <class Var_type> +int PFS_variable_cache<Var_type>::materialize_session(THD *unsafe_thd) +{ + if (!unsafe_thd) + return 1; + + if (is_materialized(unsafe_thd)) + return 0; + + return do_materialize_session(unsafe_thd); +} + +/** + Build cache of SESSION variables for a thread. +*/ +template <class Var_type> +int PFS_variable_cache<Var_type>::materialize_session(PFS_thread *pfs_thread, bool use_mem_root) +{ + if (!pfs_thread) + return 1; + + if (is_materialized(pfs_thread)) + return 0; + + if (!pfs_thread->m_lock.is_populated() || pfs_thread->m_thd == NULL) + return 1; + + m_use_mem_root= use_mem_root; + + return do_materialize_session(pfs_thread); +} + +/** + Materialize a single variable for a thread. +*/ +template <class Var_type> +int PFS_variable_cache<Var_type>::materialize_session(PFS_thread *pfs_thread, uint index) +{ + /* No check for is_materialized(). */ + + if (!pfs_thread) + return 1; + + if (!pfs_thread->m_lock.is_populated() || pfs_thread->m_thd == NULL) + return 1; + + return do_materialize_session(pfs_thread, index); +} + +/** + CLASS PFS_system_variable_cache - System variable cache. +*/ +class PFS_system_variable_cache : public PFS_variable_cache<System_variable> +{ +public: + PFS_system_variable_cache(bool external_init) : + PFS_variable_cache<System_variable>(external_init), + m_mem_thd(NULL), m_mem_thd_save(NULL), + m_mem_sysvar_ptr(NULL) { } + bool match_scope(int scope); + ulonglong get_sysvar_hash_version(void) { return m_version; } + ~PFS_system_variable_cache() { free_mem_root(); } + +private: + /* Build SHOW_var array. */ + bool init_show_var_array(enum_var_type scope, bool strict); + bool do_initialize_session(void); + + /* Global */ + int do_materialize_global(void); + /* Global and Session - THD */ + int do_materialize_all(THD* thd); + /* Session - THD */ + int do_materialize_session(THD* thd); + /* Session - PFS_thread */ + int do_materialize_session(PFS_thread *thread); + /* Single variable - PFS_thread */ + int do_materialize_session(PFS_thread *pfs_thread, uint index); + + /* Temporary mem_root to use for materialization. */ + MEM_ROOT m_mem_sysvar; + /* Pointer to THD::mem_root. */ + MEM_ROOT **m_mem_thd; + /* Save THD::mem_root. */ + MEM_ROOT *m_mem_thd_save; + /* Pointer to temporary mem_root. */ + MEM_ROOT *m_mem_sysvar_ptr; + /* Allocate and/or assign temporary mem_root. */ + void set_mem_root(void); + /* Mark all memory blocks as free in temporary mem_root. */ + void clear_mem_root(void); + /* Free mem_root memory. */ + void free_mem_root(void); +}; + + +/** + CLASS PFS_status_variable_cache - Status variable cache +*/ +class PFS_status_variable_cache : public PFS_variable_cache<Status_variable> +{ +public: + PFS_status_variable_cache(bool external_init); + + int materialize_user(PFS_user *pfs_user); + int materialize_host(PFS_host *pfs_host); + int materialize_account(PFS_account *pfs_account); + + ulonglong get_status_array_version(void) { return m_version; } + +protected: + /* Get PFS_user, account or host associated with a PFS_thread. Implemented by table class. */ + virtual PFS_client *get_pfs(PFS_thread *pfs_thread) { return NULL; } + + /* True if query is a SHOW command. */ + bool m_show_command; + +private: + bool do_initialize_session(void); + + int do_materialize_global(void); + /* Global and Session - THD */ + int do_materialize_all(THD* thd); + int do_materialize_session(THD *thd); + int do_materialize_session(PFS_thread *thread); + int do_materialize_session(PFS_thread *thread, uint index) { return 0; } + int do_materialize_client(PFS_client *pfs_client); + + /* Callback to sum user, host or account status variables. */ + void (*m_sum_client_status)(PFS_client *pfs_client, STATUS_VAR *status_totals); + + /* Build SHOW_VAR array from external source. */ + bool init_show_var_array(enum_var_type scope, bool strict); + + /* Recursively expand nested SHOW_VAR arrays. */ + void expand_show_var_array(const SHOW_VAR *show_var_array, const char *prefix, bool strict); + + /* Exclude unwanted variables from the query. */ + bool filter_show_var(const SHOW_VAR *show_var, bool strict); + + /* Check the variable scope against the query scope. */ + bool match_scope(SHOW_SCOPE variable_scope, bool strict); + + /* Exclude specific status variables by name or prefix. */ + bool filter_by_name(const SHOW_VAR *show_var); + + /* Check if a variable has an aggregatable type. */ + bool can_aggregate(enum_mysql_show_type variable_type); + + /* Build status variable name with prefix. Return in the buffer provided. */ + char *make_show_var_name(const char* prefix, const char* name, char *name_buf, size_t buf_len); + + /* Build status variable name with prefix. Return copy of the string. */ + char *make_show_var_name(const char* prefix, const char* name); + + /* For the current THD, use initial_status_vars taken from before the query start. */ + STATUS_VAR *set_status_vars(void); + + /* Build the list of status variables from SHOW_VAR array. */ + void manifest(THD *thd, const SHOW_VAR *show_var_array, + STATUS_VAR *status_var_array, const char *prefix, bool nested_array, bool strict); +}; + +/* Callback functions to sum status variables for a given user, host or account. */ +void sum_user_status(PFS_client *pfs_user, STATUS_VAR *status_totals); +void sum_host_status(PFS_client *pfs_host, STATUS_VAR *status_totals); +void sum_account_status(PFS_client *pfs_account, STATUS_VAR *status_totals); + + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_visitor.cc b/storage/perfschema/pfs_visitor.cc new file mode 100644 index 00000000..92a5c99e --- /dev/null +++ b/storage/perfschema/pfs_visitor.cc @@ -0,0 +1,1670 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "my_global.h" +#include "my_sys.h" +#include "pfs_visitor.h" +#include "pfs_instr.h" +#include "pfs_instr_class.h" +#include "pfs_user.h" +#include "pfs_host.h" +#include "pfs_account.h" +#include "pfs_buffer_container.h" + +#include "mysqld_thd_manager.h" + +/** + @file storage/perfschema/pfs_visitor.cc + Visitors (implementation). +*/ + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +class All_THD_visitor_adapter : public Do_THD_Impl +{ +public: + All_THD_visitor_adapter(PFS_connection_visitor *visitor) + : m_visitor(visitor) + {} + + virtual void operator()(THD *thd) + { + m_visitor->visit_THD(thd); + } + +private: + PFS_connection_visitor *m_visitor; +}; + +/** Connection iterator */ +void PFS_connection_iterator::visit_global(bool with_hosts, bool with_users, + bool with_accounts, bool with_threads, + bool with_THDs, + PFS_connection_visitor *visitor) +{ + assert(visitor != NULL); + assert(! with_threads || ! with_THDs); + + visitor->visit_global(); + + if (with_hosts) + { + PFS_host_iterator it= global_host_container.iterate(); + PFS_host *pfs= it.scan_next(); + + while (pfs != NULL) + { + visitor->visit_host(pfs); + pfs= it.scan_next(); + } + } + + if (with_users) + { + PFS_user_iterator it= global_user_container.iterate(); + PFS_user *pfs= it.scan_next(); + + while (pfs != NULL) + { + visitor->visit_user(pfs); + pfs= it.scan_next(); + } + } + + if (with_accounts) + { + PFS_account_iterator it= global_account_container.iterate(); + PFS_account *pfs= it.scan_next(); + + while (pfs != NULL) + { + visitor->visit_account(pfs); + pfs= it.scan_next(); + } + } + + + if (with_threads) + { + PFS_thread_iterator it= global_thread_container.iterate(); + PFS_thread *pfs= it.scan_next(); + + while (pfs != NULL) + { + visitor->visit_thread(pfs); + pfs= it.scan_next(); + } + } + + if (with_THDs) + { + All_THD_visitor_adapter adapter(visitor); + Global_THD_manager::get_instance()->do_for_all_thd(& adapter); + } +} + +class All_host_THD_visitor_adapter : public Do_THD_Impl +{ +public: + All_host_THD_visitor_adapter(PFS_connection_visitor *visitor, PFS_host *host) + : m_visitor(visitor), m_host(host) + {} + + virtual void operator()(THD *thd) + { + PSI_thread *psi= thd->get_psi(); + PFS_thread *pfs= reinterpret_cast<PFS_thread*>(psi); + pfs= sanitize_thread(pfs); + if (pfs != NULL) + { + PFS_account *account= sanitize_account(pfs->m_account); + if (account != NULL) + { + if (account->m_host == m_host) + { + m_visitor->visit_THD(thd); + } + } + else if (pfs->m_host == m_host) + { + m_visitor->visit_THD(thd); + } + } + } + +private: + PFS_connection_visitor *m_visitor; + PFS_host *m_host; +}; + +void PFS_connection_iterator::visit_host(PFS_host *host, + bool with_accounts, bool with_threads, + bool with_THDs, + PFS_connection_visitor *visitor) +{ + assert(visitor != NULL); + assert(! with_threads || ! with_THDs); + + visitor->visit_host(host); + + if (with_accounts) + { + PFS_account_iterator it= global_account_container.iterate(); + PFS_account *pfs= it.scan_next(); + + while (pfs != NULL) + { + if (pfs->m_host == host) + { + visitor->visit_account(pfs); + } + pfs= it.scan_next(); + } + } + + if (with_threads) + { + PFS_thread_iterator it= global_thread_container.iterate(); + PFS_thread *pfs= it.scan_next(); + + while (pfs != NULL) + { + PFS_account *safe_account= sanitize_account(pfs->m_account); + if (((safe_account != NULL) && (safe_account->m_host == host)) /* 1 */ + || (pfs->m_host == host)) /* 2 */ + { + /* + If the thread belongs to: + - (1) a known user@host that belongs to this host, + - (2) a 'lost' user@host that belongs to this host + process it. + */ + visitor->visit_thread(pfs); + } + pfs= it.scan_next(); + } + } + + if (with_THDs) + { + All_host_THD_visitor_adapter adapter(visitor, host); + Global_THD_manager::get_instance()->do_for_all_thd(& adapter); + } +} + +class All_user_THD_visitor_adapter : public Do_THD_Impl +{ +public: + All_user_THD_visitor_adapter(PFS_connection_visitor *visitor, PFS_user *user) + : m_visitor(visitor), m_user(user) + {} + + virtual void operator()(THD *thd) + { + PSI_thread *psi= thd->get_psi(); + PFS_thread *pfs= reinterpret_cast<PFS_thread*>(psi); + pfs= sanitize_thread(pfs); + if (pfs != NULL) + { + PFS_account *account= sanitize_account(pfs->m_account); + if (account != NULL) + { + if (account->m_user == m_user) + { + m_visitor->visit_THD(thd); + } + } + else if (pfs->m_user == m_user) + { + m_visitor->visit_THD(thd); + } + } + } + +private: + PFS_connection_visitor *m_visitor; + PFS_user *m_user; +}; + +void PFS_connection_iterator::visit_user(PFS_user *user, + bool with_accounts, bool with_threads, + bool with_THDs, + PFS_connection_visitor *visitor) +{ + assert(visitor != NULL); + assert(! with_threads || ! with_THDs); + + visitor->visit_user(user); + + if (with_accounts) + { + PFS_account_iterator it= global_account_container.iterate(); + PFS_account *pfs= it.scan_next(); + + while (pfs != NULL) + { + if (pfs->m_user == user) + { + visitor->visit_account(pfs); + } + pfs= it.scan_next(); + } + } + + if (with_threads) + { + PFS_thread_iterator it= global_thread_container.iterate(); + PFS_thread *pfs= it.scan_next(); + + while (pfs != NULL) + { + PFS_account *safe_account= sanitize_account(pfs->m_account); + if (((safe_account != NULL) && (safe_account->m_user == user)) /* 1 */ + || (pfs->m_user == user)) /* 2 */ + { + /* + If the thread belongs to: + - (1) a known user@host that belongs to this user, + - (2) a 'lost' user@host that belongs to this user + process it. + */ + visitor->visit_thread(pfs); + } + pfs= it.scan_next(); + } + } + + if (with_THDs) + { + All_user_THD_visitor_adapter adapter(visitor, user); + Global_THD_manager::get_instance()->do_for_all_thd(& adapter); + } +} + +class All_account_THD_visitor_adapter : public Do_THD_Impl +{ +public: + All_account_THD_visitor_adapter(PFS_connection_visitor *visitor, PFS_account *account) + : m_visitor(visitor), m_account(account) + {} + + virtual void operator()(THD *thd) + { + PSI_thread *psi= thd->get_psi(); + PFS_thread *pfs= reinterpret_cast<PFS_thread*>(psi); + pfs= sanitize_thread(pfs); + if (pfs != NULL) + { + if (pfs->m_account == m_account) + { + m_visitor->visit_THD(thd); + } + } + } + +private: + PFS_connection_visitor *m_visitor; + PFS_account *m_account; +}; + +void PFS_connection_iterator::visit_account(PFS_account *account, + bool with_threads, + bool with_THDs, + PFS_connection_visitor *visitor) +{ + assert(visitor != NULL); + assert(! with_threads || ! with_THDs); + + visitor->visit_account(account); + + if (with_threads) + { + PFS_thread_iterator it= global_thread_container.iterate(); + PFS_thread *pfs= it.scan_next(); + + while (pfs != NULL) + { + if (pfs->m_account == account) + { + visitor->visit_thread(pfs); + } + pfs= it.scan_next(); + } + } + + if (with_THDs) + { + All_account_THD_visitor_adapter adapter(visitor, account); + Global_THD_manager::get_instance()->do_for_all_thd(& adapter); + } +} + +void PFS_connection_iterator::visit_THD(THD *thd, + PFS_connection_visitor *visitor) +{ + assert(visitor != NULL); + visitor->visit_THD(thd); +} + +void PFS_instance_iterator::visit_all(PFS_instance_visitor *visitor) +{ + visit_all_mutex(visitor); + visit_all_rwlock(visitor); + visit_all_cond(visitor); + visit_all_file(visitor); +} + +void PFS_instance_iterator::visit_all_mutex(PFS_instance_visitor *visitor) +{ + visit_all_mutex_classes(visitor); + visit_all_mutex_instances(visitor); +} + +void PFS_instance_iterator::visit_all_mutex_classes(PFS_instance_visitor *visitor) +{ + PFS_mutex_class *pfs= mutex_class_array; + PFS_mutex_class *pfs_last= pfs + mutex_class_max; + for ( ; pfs < pfs_last; pfs++) + { + if (pfs->m_name_length != 0) + { + visitor->visit_mutex_class(pfs); + } + } +} + +void PFS_instance_iterator::visit_all_mutex_instances(PFS_instance_visitor *visitor) +{ + PFS_mutex_iterator it= global_mutex_container.iterate(); + PFS_mutex *pfs= it.scan_next(); + + while (pfs != NULL) + { + visitor->visit_mutex(pfs); + pfs= it.scan_next(); + } +} + +void PFS_instance_iterator::visit_all_rwlock(PFS_instance_visitor *visitor) +{ + visit_all_rwlock_classes(visitor); + visit_all_rwlock_instances(visitor); +} + +void PFS_instance_iterator::visit_all_rwlock_classes(PFS_instance_visitor *visitor) +{ + PFS_rwlock_class *pfs= rwlock_class_array; + PFS_rwlock_class *pfs_last= pfs + rwlock_class_max; + for ( ; pfs < pfs_last; pfs++) + { + if (pfs->m_name_length != 0) + { + visitor->visit_rwlock_class(pfs); + } + } +} + +void PFS_instance_iterator::visit_all_rwlock_instances(PFS_instance_visitor *visitor) +{ + PFS_rwlock_iterator it= global_rwlock_container.iterate(); + PFS_rwlock *pfs= it.scan_next(); + + while (pfs != NULL) + { + visitor->visit_rwlock(pfs); + pfs= it.scan_next(); + } +} + +void PFS_instance_iterator::visit_all_cond(PFS_instance_visitor *visitor) +{ + visit_all_cond_classes(visitor); + visit_all_cond_instances(visitor); +} + +void PFS_instance_iterator::visit_all_cond_classes(PFS_instance_visitor *visitor) +{ + PFS_cond_class *pfs= cond_class_array; + PFS_cond_class *pfs_last= pfs + cond_class_max; + for ( ; pfs < pfs_last; pfs++) + { + if (pfs->m_name_length != 0) + { + visitor->visit_cond_class(pfs); + } + } +} + +void PFS_instance_iterator::visit_all_cond_instances(PFS_instance_visitor *visitor) +{ + PFS_cond_iterator it= global_cond_container.iterate(); + PFS_cond *pfs= it.scan_next(); + + while (pfs != NULL) + { + visitor->visit_cond(pfs); + pfs= it.scan_next(); + } +} + +void PFS_instance_iterator::visit_all_file(PFS_instance_visitor *visitor) +{ + visit_all_file_classes(visitor); + visit_all_file_instances(visitor); +} + +void PFS_instance_iterator::visit_all_file_classes(PFS_instance_visitor *visitor) +{ + PFS_file_class *pfs= file_class_array; + PFS_file_class *pfs_last= pfs + file_class_max; + for ( ; pfs < pfs_last; pfs++) + { + if (pfs->m_name_length != 0) + { + visitor->visit_file_class(pfs); + } + } +} + +void PFS_instance_iterator::visit_all_file_instances(PFS_instance_visitor *visitor) +{ + PFS_file_iterator it= global_file_container.iterate(); + PFS_file *pfs= it.scan_next(); + + while (pfs != NULL) + { + visitor->visit_file(pfs); + pfs= it.scan_next(); + } +} + +/** Instance iterator */ + +void PFS_instance_iterator::visit_mutex_instances(PFS_mutex_class *klass, + PFS_instance_visitor *visitor) +{ + assert(visitor != NULL); + + visitor->visit_mutex_class(klass); + + if (klass->is_singleton()) + { + PFS_mutex *pfs= sanitize_mutex(klass->m_singleton); + if (likely(pfs != NULL)) + { + if (likely(pfs->m_lock.is_populated())) + { + visitor->visit_mutex(pfs); + } + } + } + else + { + PFS_mutex_iterator it= global_mutex_container.iterate(); + PFS_mutex *pfs= it.scan_next(); + + while (pfs != NULL) + { + if (pfs->m_class == klass) + { + visitor->visit_mutex(pfs); + } + pfs= it.scan_next(); + } + } +} + +void PFS_instance_iterator::visit_rwlock_instances(PFS_rwlock_class *klass, + PFS_instance_visitor *visitor) +{ + assert(visitor != NULL); + + visitor->visit_rwlock_class(klass); + + if (klass->is_singleton()) + { + PFS_rwlock *pfs= sanitize_rwlock(klass->m_singleton); + if (likely(pfs != NULL)) + { + if (likely(pfs->m_lock.is_populated())) + { + visitor->visit_rwlock(pfs); + } + } + } + else + { + PFS_rwlock_iterator it= global_rwlock_container.iterate(); + PFS_rwlock *pfs= it.scan_next(); + + while (pfs != NULL) + { + if (pfs->m_class == klass) + { + visitor->visit_rwlock(pfs); + } + pfs= it.scan_next(); + } + } +} + +void PFS_instance_iterator::visit_cond_instances(PFS_cond_class *klass, + PFS_instance_visitor *visitor) +{ + assert(visitor != NULL); + + visitor->visit_cond_class(klass); + + if (klass->is_singleton()) + { + PFS_cond *pfs= sanitize_cond(klass->m_singleton); + if (likely(pfs != NULL)) + { + if (likely(pfs->m_lock.is_populated())) + { + visitor->visit_cond(pfs); + } + } + } + else + { + PFS_cond_iterator it= global_cond_container.iterate(); + PFS_cond *pfs= it.scan_next(); + + while (pfs != NULL) + { + if (pfs->m_class == klass) + { + visitor->visit_cond(pfs); + } + pfs= it.scan_next(); + } + } +} + +void PFS_instance_iterator::visit_file_instances(PFS_file_class *klass, + PFS_instance_visitor *visitor) +{ + assert(visitor != NULL); + + visitor->visit_file_class(klass); + + if (klass->is_singleton()) + { + PFS_file *pfs= sanitize_file(klass->m_singleton); + if (likely(pfs != NULL)) + { + if (likely(pfs->m_lock.is_populated())) + { + visitor->visit_file(pfs); + } + } + } + else + { + PFS_file_iterator it= global_file_container.iterate(); + PFS_file *pfs= it.scan_next(); + + while (pfs != NULL) + { + if (pfs->m_class == klass) + { + visitor->visit_file(pfs); + } + pfs= it.scan_next(); + } + } +} + +/** Socket instance iterator visting a socket class and all instances */ + +void PFS_instance_iterator::visit_socket_instances(PFS_socket_class *klass, + PFS_instance_visitor *visitor) +{ + assert(visitor != NULL); + + visitor->visit_socket_class(klass); + + if (klass->is_singleton()) + { + PFS_socket *pfs= sanitize_socket(klass->m_singleton); + if (likely(pfs != NULL)) + { + if (likely(pfs->m_lock.is_populated())) + { + visitor->visit_socket(pfs); + } + } + } + else + { + PFS_socket_iterator it= global_socket_container.iterate(); + PFS_socket *pfs= it.scan_next(); + + while (pfs != NULL) + { + if (pfs->m_class == klass) + { + visitor->visit_socket(pfs); + } + pfs= it.scan_next(); + } + } +} + +/** Socket instance iterator visting sockets owned by PFS_thread. */ + +void PFS_instance_iterator::visit_socket_instances(PFS_socket_class *klass, + PFS_instance_visitor *visitor, + PFS_thread *thread, + bool visit_class) +{ + assert(visitor != NULL); + assert(thread != NULL); + + if (visit_class) + visitor->visit_socket_class(klass); + + if (klass->is_singleton()) + { + PFS_socket *pfs= sanitize_socket(klass->m_singleton); + if (likely(pfs != NULL)) + { + if (unlikely(pfs->m_thread_owner == thread)) + visitor->visit_socket(pfs); + } + } + else + { + /* Get current socket stats from each socket instance owned by this thread */ + PFS_socket_iterator it= global_socket_container.iterate(); + PFS_socket *pfs= it.scan_next(); + + while (pfs != NULL) + { + if (unlikely((pfs->m_class == klass) && + (pfs->m_thread_owner == thread))) + { + visitor->visit_socket(pfs); + } + pfs= it.scan_next(); + } + } +} + +/** Generic instance iterator with PFS_thread as matching criteria */ + +void PFS_instance_iterator::visit_instances(PFS_instr_class *klass, + PFS_instance_visitor *visitor, + PFS_thread *thread, + bool visit_class) +{ + assert(visitor != NULL); + assert(klass != NULL); + + switch (klass->m_type) + { + case PFS_CLASS_SOCKET: + { + PFS_socket_class *socket_class= reinterpret_cast<PFS_socket_class*>(klass); + PFS_instance_iterator::visit_socket_instances(socket_class, visitor, + thread, visit_class); + } + break; + default: + break; + } +} + +/** Object iterator */ +void PFS_object_iterator::visit_all(PFS_object_visitor *visitor) +{ + visit_all_tables(visitor); +} + +class Proc_all_table_shares + : public PFS_buffer_processor<PFS_table_share> +{ +public: + Proc_all_table_shares(PFS_object_visitor *visitor) + : m_visitor(visitor) + {} + + virtual void operator()(PFS_table_share *pfs) + { + if (pfs->m_enabled) + { + m_visitor->visit_table_share(pfs); + } + } + +private: + PFS_object_visitor* m_visitor; +}; + +class Proc_all_table_handles + : public PFS_buffer_processor<PFS_table> +{ +public: + Proc_all_table_handles(PFS_object_visitor *visitor) + : m_visitor(visitor) + {} + + virtual void operator()(PFS_table *pfs) + { + PFS_table_share *safe_share= sanitize_table_share(pfs->m_share); + if (safe_share != NULL) + { + if (safe_share->m_enabled) + { + m_visitor->visit_table(pfs); + } + } + } + +private: + PFS_object_visitor* m_visitor; +}; + +void PFS_object_iterator::visit_all_tables(PFS_object_visitor *visitor) +{ + assert(visitor != NULL); + + visitor->visit_global(); + + /* For all the table shares ... */ + Proc_all_table_shares proc_shares(visitor); + global_table_share_container.apply(proc_shares); + + /* For all the table handles ... */ + Proc_all_table_handles proc_handles(visitor); + global_table_container.apply(proc_handles); +} + +class Proc_one_table_share_handles + : public PFS_buffer_processor<PFS_table> +{ +public: + Proc_one_table_share_handles(PFS_object_visitor *visitor, PFS_table_share *share) + : m_visitor(visitor), m_share(share) + {} + + virtual void operator()(PFS_table *pfs) + { + if (pfs->m_share == m_share) + { + m_visitor->visit_table(pfs); + } + } + +private: + PFS_object_visitor* m_visitor; + PFS_table_share* m_share; +}; + +void PFS_object_iterator::visit_tables(PFS_table_share *share, + PFS_object_visitor *visitor) +{ + assert(visitor != NULL); + + if (!share->m_enabled) + return; + + visitor->visit_table_share(share); + +#ifdef LATER + if (share->get_refcount() == 0) + return; +#endif + + /* For all the table handles ... */ + Proc_one_table_share_handles proc(visitor, share); + global_table_container.apply(proc); +} + +class Proc_one_table_share_indexes + : public PFS_buffer_processor<PFS_table> +{ +public: + Proc_one_table_share_indexes(PFS_object_visitor *visitor, PFS_table_share *share, uint index) + : m_visitor(visitor), m_share(share), m_index(index) + {} + + virtual void operator()(PFS_table *pfs) + { + if (pfs->m_share == m_share) + { + m_visitor->visit_table_index(pfs, m_index); + } + } + +private: + PFS_object_visitor* m_visitor; + PFS_table_share* m_share; + uint m_index; +}; + +void PFS_object_iterator::visit_table_indexes(PFS_table_share *share, + uint index, + PFS_object_visitor *visitor) +{ + assert(visitor != NULL); + + if (!share->m_enabled) + return; + + visitor->visit_table_share_index(share, index); + +#ifdef LATER + if (share->get_refcount() == 0) + return; +#endif + + /* For all the table handles ... */ + Proc_one_table_share_indexes proc(visitor, share, index); + global_table_container.apply(proc); +} + +/** Connection wait visitor */ + +PFS_connection_wait_visitor +::PFS_connection_wait_visitor(PFS_instr_class *klass) +{ + m_index= klass->m_event_name_index; +} + +PFS_connection_wait_visitor::~PFS_connection_wait_visitor() = default; + +void PFS_connection_wait_visitor::visit_global() +{ + /* + This visitor is used only for global instruments + that do not have instances. + For waits, do not sum by connection but by instances, + it is more efficient. + */ + assert( (m_index == global_idle_class.m_event_name_index) + || (m_index == global_metadata_class.m_event_name_index)); + + if (m_index == global_idle_class.m_event_name_index) + { + m_stat.aggregate(& global_idle_stat); + } + else + { + m_stat.aggregate(& global_metadata_stat); + } +} + +void PFS_connection_wait_visitor::visit_host(PFS_host *pfs) +{ + const PFS_single_stat *event_name_array; + event_name_array= pfs->read_instr_class_waits_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +void PFS_connection_wait_visitor::visit_user(PFS_user *pfs) +{ + const PFS_single_stat *event_name_array; + event_name_array= pfs->read_instr_class_waits_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +void PFS_connection_wait_visitor::visit_account(PFS_account *pfs) +{ + const PFS_single_stat *event_name_array; + event_name_array= pfs->read_instr_class_waits_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +void PFS_connection_wait_visitor::visit_thread(PFS_thread *pfs) +{ + const PFS_single_stat *event_name_array; + event_name_array= pfs->read_instr_class_waits_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +PFS_connection_all_wait_visitor::PFS_connection_all_wait_visitor() = default; + +PFS_connection_all_wait_visitor::~PFS_connection_all_wait_visitor() = default; + +void PFS_connection_all_wait_visitor::visit_global() +{ + /* Sum by instances, not by connection */ + assert(false); +} + +void PFS_connection_all_wait_visitor::visit_connection_slice(PFS_connection_slice *pfs) +{ + const PFS_single_stat *stat= pfs->read_instr_class_waits_stats(); + if (stat != NULL) + { + const PFS_single_stat *stat_last= stat + wait_class_max; + for ( ; stat < stat_last; stat++) + { + m_stat.aggregate(stat); + } + } +} + +void PFS_connection_all_wait_visitor::visit_host(PFS_host *pfs) +{ + visit_connection_slice(pfs); +} + +void PFS_connection_all_wait_visitor::visit_user(PFS_user *pfs) +{ + visit_connection_slice(pfs); +} + +void PFS_connection_all_wait_visitor::visit_account(PFS_account *pfs) +{ + visit_connection_slice(pfs); +} + +void PFS_connection_all_wait_visitor::visit_thread(PFS_thread *pfs) +{ + visit_connection_slice(pfs); +} + +PFS_connection_stage_visitor::PFS_connection_stage_visitor(PFS_stage_class *klass) +{ + m_index= klass->m_event_name_index; +} + +PFS_connection_stage_visitor::~PFS_connection_stage_visitor() = default; + +void PFS_connection_stage_visitor::visit_global() +{ + m_stat.aggregate(& global_instr_class_stages_array[m_index]); +} + +void PFS_connection_stage_visitor::visit_host(PFS_host *pfs) +{ + const PFS_stage_stat *event_name_array; + event_name_array= pfs->read_instr_class_stages_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +void PFS_connection_stage_visitor::visit_user(PFS_user *pfs) +{ + const PFS_stage_stat *event_name_array; + event_name_array= pfs->read_instr_class_stages_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +void PFS_connection_stage_visitor::visit_account(PFS_account *pfs) +{ + const PFS_stage_stat *event_name_array; + event_name_array= pfs->read_instr_class_stages_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +void PFS_connection_stage_visitor::visit_thread(PFS_thread *pfs) +{ + const PFS_stage_stat *event_name_array; + event_name_array= pfs->read_instr_class_stages_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +PFS_connection_statement_visitor +::PFS_connection_statement_visitor(PFS_statement_class *klass) +{ + m_index= klass->m_event_name_index; +} + +PFS_connection_statement_visitor::~PFS_connection_statement_visitor() = default; + +void PFS_connection_statement_visitor::visit_global() +{ + m_stat.aggregate(& global_instr_class_statements_array[m_index]); +} + +void PFS_connection_statement_visitor::visit_host(PFS_host *pfs) +{ + const PFS_statement_stat *event_name_array; + event_name_array= pfs->read_instr_class_statements_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +void PFS_connection_statement_visitor::visit_user(PFS_user *pfs) +{ + const PFS_statement_stat *event_name_array; + event_name_array= pfs->read_instr_class_statements_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +void PFS_connection_statement_visitor::visit_account(PFS_account *pfs) +{ + const PFS_statement_stat *event_name_array; + event_name_array= pfs->read_instr_class_statements_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +void PFS_connection_statement_visitor::visit_thread(PFS_thread *pfs) +{ + const PFS_statement_stat *event_name_array; + event_name_array= pfs->read_instr_class_statements_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +/** Instance wait visitor */ +PFS_connection_all_statement_visitor +::PFS_connection_all_statement_visitor() = default; + +PFS_connection_all_statement_visitor +::~PFS_connection_all_statement_visitor() = default; + +void PFS_connection_all_statement_visitor::visit_global() +{ + PFS_statement_stat *stat= global_instr_class_statements_array; + PFS_statement_stat *stat_last= stat + statement_class_max; + for ( ; stat < stat_last; stat++) + { + m_stat.aggregate(stat); + } +} + +void PFS_connection_all_statement_visitor::visit_connection_slice(PFS_connection_slice *pfs) +{ + const PFS_statement_stat *stat= pfs->read_instr_class_statements_stats(); + if (stat != NULL) + { + const PFS_statement_stat *stat_last= stat + statement_class_max; + for ( ; stat < stat_last; stat++) + { + m_stat.aggregate(stat); + } + } +} + +void PFS_connection_all_statement_visitor::visit_host(PFS_host *pfs) +{ + visit_connection_slice(pfs); +} + +void PFS_connection_all_statement_visitor::visit_user(PFS_user *pfs) +{ + visit_connection_slice(pfs); +} + +void PFS_connection_all_statement_visitor::visit_account(PFS_account *pfs) +{ + visit_connection_slice(pfs); +} + +void PFS_connection_all_statement_visitor::visit_thread(PFS_thread *pfs) +{ + visit_connection_slice(pfs); +} + +PFS_connection_transaction_visitor +::PFS_connection_transaction_visitor(PFS_transaction_class *klass) +{ + m_index= klass->m_event_name_index; +} + +PFS_connection_transaction_visitor::~PFS_connection_transaction_visitor() +{} + +void PFS_connection_transaction_visitor::visit_global() +{ + m_stat.aggregate(&global_transaction_stat); +} + +void PFS_connection_transaction_visitor::visit_host(PFS_host *pfs) +{ + const PFS_transaction_stat *event_name_array; + event_name_array= pfs->read_instr_class_transactions_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +void PFS_connection_transaction_visitor::visit_user(PFS_user *pfs) +{ + const PFS_transaction_stat *event_name_array; + event_name_array= pfs->read_instr_class_transactions_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +void PFS_connection_transaction_visitor::visit_account(PFS_account *pfs) +{ + const PFS_transaction_stat *event_name_array; + event_name_array= pfs->read_instr_class_transactions_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +void PFS_connection_transaction_visitor::visit_thread(PFS_thread *pfs) +{ + const PFS_transaction_stat *event_name_array; + event_name_array= pfs->read_instr_class_transactions_stats(); + if (event_name_array != NULL) + { + m_stat.aggregate(& event_name_array[m_index]); + } +} + +/** Disabled pending code review */ +#if 0 +/** Instance wait visitor */ +PFS_connection_all_transaction_visitor +::PFS_connection_all_transaction_visitor() +{} + +PFS_connection_all_transaction_visitor::~PFS_connection_all_transaction_visitor() +{} + +void PFS_connection_all_transaction_visitor::visit_global() +{ + m_stat.aggregate(&global_transaction_stat); +} + +void PFS_connection_all_transaction_visitor::visit_connection_slice(PFS_connection_slice *pfs) +{ + PFS_transaction_stat *stat= pfs->m_instr_class_transactions_stats; + m_stat.aggregate(stat); +} + +void PFS_connection_all_transaction_visitor::visit_host(PFS_host *pfs) +{ + visit_connection_slice(pfs); +} + +void PFS_connection_all_transaction_visitor::visit_user(PFS_user *pfs) +{ + visit_connection_slice(pfs); +} + +void PFS_connection_all_transaction_visitor::visit_account(PFS_account *pfs) +{ + visit_connection_slice(pfs); +} + +void PFS_connection_all_transaction_visitor::visit_thread(PFS_thread *pfs) +{ + visit_connection_slice(pfs); +} +#endif + +PFS_connection_stat_visitor::PFS_connection_stat_visitor() = default; + +PFS_connection_stat_visitor::~PFS_connection_stat_visitor() = default; + +void PFS_connection_stat_visitor::visit_global() +{} + +void PFS_connection_stat_visitor::visit_host(PFS_host *pfs) +{ + m_stat.aggregate_disconnected(pfs->m_disconnected_count); +} + +void PFS_connection_stat_visitor::visit_user(PFS_user *pfs) +{ + m_stat.aggregate_disconnected(pfs->m_disconnected_count); +} + +void PFS_connection_stat_visitor::visit_account(PFS_account *pfs) +{ + m_stat.aggregate_disconnected(pfs->m_disconnected_count); +} + +void PFS_connection_stat_visitor::visit_thread(PFS_thread *) +{ + m_stat.aggregate_active(1); +} + +PFS_connection_memory_visitor +::PFS_connection_memory_visitor(PFS_memory_class *klass) +{ + m_index= klass->m_event_name_index; + m_stat.reset(); +} + +PFS_connection_memory_visitor::~PFS_connection_memory_visitor() +{} + +void PFS_connection_memory_visitor::visit_global() +{ + PFS_memory_stat *stat; + stat= & global_instr_class_memory_array[m_index]; + stat->full_aggregate_to(& m_stat); +} + +void PFS_connection_memory_visitor::visit_host(PFS_host *pfs) +{ + const PFS_memory_stat *event_name_array; + event_name_array= pfs->read_instr_class_memory_stats(); + if (event_name_array != NULL) + { + const PFS_memory_stat *stat; + stat= & event_name_array[m_index]; + stat->full_aggregate_to(& m_stat); + } +} + +void PFS_connection_memory_visitor::visit_user(PFS_user *pfs) +{ + const PFS_memory_stat *event_name_array; + event_name_array= pfs->read_instr_class_memory_stats(); + if (event_name_array != NULL) + { + const PFS_memory_stat *stat; + stat= & event_name_array[m_index]; + stat->full_aggregate_to(& m_stat); + } +} + +void PFS_connection_memory_visitor::visit_account(PFS_account *pfs) +{ + const PFS_memory_stat *event_name_array; + event_name_array= pfs->read_instr_class_memory_stats(); + if (event_name_array != NULL) + { + const PFS_memory_stat *stat; + stat= & event_name_array[m_index]; + stat->full_aggregate_to(& m_stat); + } +} + +void PFS_connection_memory_visitor::visit_thread(PFS_thread *pfs) +{ + const PFS_memory_stat *event_name_array; + event_name_array= pfs->read_instr_class_memory_stats(); + if (event_name_array != NULL) + { + const PFS_memory_stat *stat; + stat= & event_name_array[m_index]; + stat->full_aggregate_to(& m_stat); + } +} + + +PFS_connection_status_visitor:: +PFS_connection_status_visitor(STATUS_VAR *status_vars) : m_status_vars(status_vars) +{ + memset(m_status_vars, 0, sizeof(STATUS_VAR)); +} + +PFS_connection_status_visitor::~PFS_connection_status_visitor() = default; + +/** Aggregate from global status. */ +void PFS_connection_status_visitor::visit_global() +{ + /* NOTE: Requires lock on LOCK_status. */ + mysql_mutex_assert_owner(&LOCK_status); + add_to_status(m_status_vars, &global_status_var); +} + +void PFS_connection_status_visitor::visit_host(PFS_host *pfs) +{ + pfs->m_status_stats.aggregate_to(m_status_vars); +} + +void PFS_connection_status_visitor::visit_user(PFS_user *pfs) +{ + pfs->m_status_stats.aggregate_to(m_status_vars); +} + +void PFS_connection_status_visitor::visit_account(PFS_account *pfs) +{ + pfs->m_status_stats.aggregate_to(m_status_vars); +} + +void PFS_connection_status_visitor::visit_thread(PFS_thread *pfs) +{ +} + +void PFS_connection_status_visitor::visit_THD(THD *thd) +{ + add_to_status(m_status_vars, &thd->status_var); +} + + +PFS_instance_wait_visitor::PFS_instance_wait_visitor() = default; + +PFS_instance_wait_visitor::~PFS_instance_wait_visitor() = default; + +void PFS_instance_wait_visitor::visit_mutex_class(PFS_mutex_class *pfs) +{ + m_stat.aggregate(&pfs->m_mutex_stat.m_wait_stat); +} + +void PFS_instance_wait_visitor::visit_rwlock_class(PFS_rwlock_class *pfs) +{ + m_stat.aggregate(&pfs->m_rwlock_stat.m_wait_stat); +} + +void PFS_instance_wait_visitor::visit_cond_class(PFS_cond_class *pfs) +{ + m_stat.aggregate(&pfs->m_cond_stat.m_wait_stat); +} + +void PFS_instance_wait_visitor::visit_file_class(PFS_file_class *pfs) +{ + pfs->m_file_stat.m_io_stat.sum_waits(&m_stat); +} + +void PFS_instance_wait_visitor::visit_socket_class(PFS_socket_class *pfs) +{ + pfs->m_socket_stat.m_io_stat.sum_waits(&m_stat); +} + +void PFS_instance_wait_visitor::visit_mutex(PFS_mutex *pfs) +{ + m_stat.aggregate(& pfs->m_mutex_stat.m_wait_stat); +} + +void PFS_instance_wait_visitor::visit_rwlock(PFS_rwlock *pfs) +{ + m_stat.aggregate(& pfs->m_rwlock_stat.m_wait_stat); +} + +void PFS_instance_wait_visitor::visit_cond(PFS_cond *pfs) +{ + m_stat.aggregate(& pfs->m_cond_stat.m_wait_stat); +} + +void PFS_instance_wait_visitor::visit_file(PFS_file *pfs) +{ + /* Combine per-operation file wait stats before aggregating */ + PFS_single_stat stat; + pfs->m_file_stat.m_io_stat.sum_waits(&stat); + m_stat.aggregate(&stat); +} + +void PFS_instance_wait_visitor::visit_socket(PFS_socket *pfs) +{ + /* Combine per-operation socket wait stats before aggregating */ + PFS_single_stat stat; + pfs->m_socket_stat.m_io_stat.sum_waits(&stat); + m_stat.aggregate(&stat); +} + +/** Table IO wait visitor */ + +PFS_object_wait_visitor::PFS_object_wait_visitor() = default; + +PFS_object_wait_visitor::~PFS_object_wait_visitor() = default; + +void PFS_object_wait_visitor::visit_global() +{ + global_table_io_stat.sum(& m_stat); + global_table_lock_stat.sum(& m_stat); +} + +void PFS_object_wait_visitor::visit_table_share(PFS_table_share *pfs) +{ + uint safe_key_count= sanitize_index_count(pfs->m_key_count); + pfs->sum(& m_stat, safe_key_count); +} + +void PFS_object_wait_visitor::visit_table(PFS_table *pfs) +{ + PFS_table_share *table_share= sanitize_table_share(pfs->m_share); + if (table_share != NULL) + { + uint safe_key_count= sanitize_index_count(table_share->m_key_count); + pfs->m_table_stat.sum(& m_stat, safe_key_count); + } +} + +PFS_table_io_wait_visitor::PFS_table_io_wait_visitor() = default; + +PFS_table_io_wait_visitor::~PFS_table_io_wait_visitor() = default; + +void PFS_table_io_wait_visitor::visit_global() +{ + global_table_io_stat.sum(& m_stat); +} + +void PFS_table_io_wait_visitor::visit_table_share(PFS_table_share *pfs) +{ + PFS_table_io_stat io_stat; + uint safe_key_count= sanitize_index_count(pfs->m_key_count); + uint index; + PFS_table_share_index *index_stat; + + /* Aggregate index stats */ + for (index= 0; index < safe_key_count; index++) + { + index_stat= pfs->find_index_stat(index); + if (index_stat != NULL) + io_stat.aggregate(& index_stat->m_stat); + } + + /* Aggregate global stats */ + index_stat= pfs->find_index_stat(MAX_INDEXES); + if (index_stat != NULL) + io_stat.aggregate(& index_stat->m_stat); + + io_stat.sum(& m_stat); +} + +void PFS_table_io_wait_visitor::visit_table(PFS_table *pfs) +{ + PFS_table_share *safe_share= sanitize_table_share(pfs->m_share); + + if (likely(safe_share != NULL)) + { + PFS_table_io_stat io_stat; + uint safe_key_count= sanitize_index_count(safe_share->m_key_count); + uint index; + + /* Aggregate index stats */ + for (index= 0; index < safe_key_count; index++) + io_stat.aggregate(& pfs->m_table_stat.m_index_stat[index]); + + /* Aggregate global stats */ + io_stat.aggregate(& pfs->m_table_stat.m_index_stat[MAX_INDEXES]); + + io_stat.sum(& m_stat); + } +} + +/** Table IO stat visitor */ + +PFS_table_io_stat_visitor::PFS_table_io_stat_visitor() = default; + +PFS_table_io_stat_visitor::~PFS_table_io_stat_visitor() = default; + +void PFS_table_io_stat_visitor::visit_table_share(PFS_table_share *pfs) +{ + uint safe_key_count= sanitize_index_count(pfs->m_key_count); + uint index; + PFS_table_share_index *index_stat; + + /* Aggregate index stats */ + for (index= 0; index < safe_key_count; index++) + { + index_stat= pfs->find_index_stat(index); + if (index_stat != NULL) + m_stat.aggregate(& index_stat->m_stat); + } + + /* Aggregate global stats */ + index_stat= pfs->find_index_stat(MAX_INDEXES); + if (index_stat != NULL) + m_stat.aggregate(& index_stat->m_stat); +} + +void PFS_table_io_stat_visitor::visit_table(PFS_table *pfs) +{ + PFS_table_share *safe_share= sanitize_table_share(pfs->m_share); + + if (likely(safe_share != NULL)) + { + uint safe_key_count= sanitize_index_count(safe_share->m_key_count); + uint index; + + /* Aggregate index stats */ + for (index= 0; index < safe_key_count; index++) + m_stat.aggregate(& pfs->m_table_stat.m_index_stat[index]); + + /* Aggregate global stats */ + m_stat.aggregate(& pfs->m_table_stat.m_index_stat[MAX_INDEXES]); + } +} + +/** Index IO stat visitor */ + +PFS_index_io_stat_visitor::PFS_index_io_stat_visitor() = default; + +PFS_index_io_stat_visitor::~PFS_index_io_stat_visitor() = default; + +void PFS_index_io_stat_visitor::visit_table_share_index(PFS_table_share *pfs, uint index) +{ + PFS_table_share_index *index_stat; + + index_stat= pfs->find_index_stat(index); + if (index_stat != NULL) + m_stat.aggregate(& index_stat->m_stat); +} + +void PFS_index_io_stat_visitor::visit_table_index(PFS_table *pfs, uint index) +{ + m_stat.aggregate(& pfs->m_table_stat.m_index_stat[index]); +} + +/** Table lock wait visitor */ + +PFS_table_lock_wait_visitor::PFS_table_lock_wait_visitor() = default; + +PFS_table_lock_wait_visitor::~PFS_table_lock_wait_visitor() = default; + +void PFS_table_lock_wait_visitor::visit_global() +{ + global_table_lock_stat.sum(& m_stat); +} + +void PFS_table_lock_wait_visitor::visit_table_share(PFS_table_share *pfs) +{ + pfs->sum_lock(& m_stat); +} + +void PFS_table_lock_wait_visitor::visit_table(PFS_table *pfs) +{ + pfs->m_table_stat.sum_lock(& m_stat); +} + +/** Table lock stat visitor */ + +PFS_table_lock_stat_visitor::PFS_table_lock_stat_visitor() = default; + +PFS_table_lock_stat_visitor::~PFS_table_lock_stat_visitor() = default; + +void PFS_table_lock_stat_visitor::visit_table_share(PFS_table_share *pfs) +{ + PFS_table_share_lock *lock_stat; + + lock_stat= pfs->find_lock_stat(); + if (lock_stat != NULL) + m_stat.aggregate(& lock_stat->m_stat); +} + +void PFS_table_lock_stat_visitor::visit_table(PFS_table *pfs) +{ + m_stat.aggregate(& pfs->m_table_stat.m_lock_stat); +} + +PFS_instance_socket_io_stat_visitor +::PFS_instance_socket_io_stat_visitor() = default; + +PFS_instance_socket_io_stat_visitor +::~PFS_instance_socket_io_stat_visitor() = default; + +void PFS_instance_socket_io_stat_visitor::visit_socket_class(PFS_socket_class *pfs) +{ + /* Aggregate wait times, event counts and byte counts */ + m_socket_io_stat.aggregate(&pfs->m_socket_stat.m_io_stat); +} + +void PFS_instance_socket_io_stat_visitor::visit_socket(PFS_socket *pfs) +{ + /* Aggregate wait times, event counts and byte counts */ + m_socket_io_stat.aggregate(&pfs->m_socket_stat.m_io_stat); +} + +PFS_instance_file_io_stat_visitor +::PFS_instance_file_io_stat_visitor() = default; + +PFS_instance_file_io_stat_visitor +::~PFS_instance_file_io_stat_visitor() = default; + +void PFS_instance_file_io_stat_visitor::visit_file_class(PFS_file_class *pfs) +{ + /* Aggregate wait times, event counts and byte counts */ + m_file_io_stat.aggregate(&pfs->m_file_stat.m_io_stat); +} + +void PFS_instance_file_io_stat_visitor::visit_file(PFS_file *pfs) +{ + /* Aggregate wait times, event counts and byte counts */ + m_file_io_stat.aggregate(&pfs->m_file_stat.m_io_stat); +} +/** @} */ diff --git a/storage/perfschema/pfs_visitor.h b/storage/perfschema/pfs_visitor.h new file mode 100644 index 00000000..03684ba9 --- /dev/null +++ b/storage/perfschema/pfs_visitor.h @@ -0,0 +1,687 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_VISITOR_H +#define PFS_VISITOR_H + +#include "pfs_stat.h" + +typedef struct system_status_var STATUS_VAR; + +/** + @file storage/perfschema/pfs_visitor.h + Visitors (declarations). +*/ + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +struct PFS_user; +struct PFS_account; +struct PFS_host; +struct PFS_thread; +struct PFS_instr_class; +struct PFS_mutex_class; +struct PFS_rwlock_class; +struct PFS_cond_class; +struct PFS_file_class; +struct PFS_socket_class; +struct PFS_memory_class; +struct PFS_table_share; +struct PFS_mutex; +struct PFS_rwlock; +struct PFS_cond; +struct PFS_file; +struct PFS_table; +struct PFS_stage_class; +struct PFS_statement_class; +struct PFS_transaction_class; +struct PFS_socket; +struct PFS_connection_slice; + +/** + Interface class to visit groups of connections. + @sa PFS_connection_iterator +*/ +class PFS_connection_visitor +{ +public: + PFS_connection_visitor() = default; + virtual ~PFS_connection_visitor() = default; + /** Visit all connections. */ + virtual void visit_global() {} + /** Visit all connections of a host. */ + virtual void visit_host(PFS_host *pfs) {} + /** Visit all connections of a user+host. */ + virtual void visit_account(PFS_account *pfs) {} + /** Visit all connections of a user. */ + virtual void visit_user(PFS_user *pfs) {} + /** Visit a thread. */ + virtual void visit_thread(PFS_thread *pfs) {} + /** Visit a THD associated with a thread. */ + virtual void visit_THD(THD *thd) {} +}; + +/** + Iterator over groups of connections. + @sa PFS_connection_visitor +*/ +class PFS_connection_iterator +{ +public: + /** + Visit all connections. + @param with_hosts when true, visit also all hosts. + @param with_users when true, visit also all users. + @param with_accounts when true, visit also all user+host. + @param with_threads when true, visit also all threads. + @param with_THDs when true, visit also all threads THD. + @param visitor the visitor to call + */ + static void visit_global(bool with_hosts, bool with_users, + bool with_accounts, bool with_threads, + bool with_THDs, + PFS_connection_visitor *visitor); + /** + Visit all connections of a host. + @param host the host to visit. + @param with_accounts when true, visit also all related user+host. + @param with_threads when true, visit also all related threads. + @param with_THDs when true, visit also all related threads THD. + @param visitor the visitor to call + */ + static void visit_host(PFS_host *host, bool with_accounts, bool with_threads, + bool with_THDs, + PFS_connection_visitor *visitor); + /** + Visit all connections of a user. + @param user the user to visit. + @param with_accounts when true, visit also all related user+host. + @param with_threads when true, visit also all related threads. + @param with_THDs when true, visit also all related threads THD. + @param visitor the visitor to call + */ + static void visit_user(PFS_user *user, bool with_accounts, bool with_threads, + bool with_THDs, + PFS_connection_visitor *visitor); + /** + Visit all connections of a user+host. + @param account the user+host to visit. + @param with_threads when true, visit also all related threads. + @param with_THDs when true, visit also all related threads THD. + @param visitor the visitor to call + */ + static void visit_account(PFS_account *account, bool with_threads, + bool with_THDs, + PFS_connection_visitor *visitor); + /** + Visit a thread or connection. + @param thread the thread to visit. + @param visitor the visitor to call + */ + static inline void visit_thread(PFS_thread *thread, + PFS_connection_visitor *visitor) + { visitor->visit_thread(thread); } + + /** + Visit THD. + @param thd the THD to visit. + @param visitor the visitor to call. + */ + static void visit_THD(THD *thd, PFS_connection_visitor *visitor); +}; + +/** + Interface class to visit groups of instrumentation point instances. + @sa PFS_instance_iterator +*/ +class PFS_instance_visitor +{ +public: + PFS_instance_visitor() = default; + virtual ~PFS_instance_visitor() = default; + /** Visit a mutex class. */ + virtual void visit_mutex_class(PFS_mutex_class *pfs) {} + /** Visit a rwlock class. */ + virtual void visit_rwlock_class(PFS_rwlock_class *pfs) {} + /** Visit a cond class. */ + virtual void visit_cond_class(PFS_cond_class *pfs) {} + /** Visit a file class. */ + virtual void visit_file_class(PFS_file_class *pfs) {} + /** Visit a socket class. */ + virtual void visit_socket_class(PFS_socket_class *pfs) {} + /** Visit a mutex instance. */ + virtual void visit_mutex(PFS_mutex *pfs) {} + /** Visit a rwlock instance. */ + virtual void visit_rwlock(PFS_rwlock *pfs) {} + /** Visit a cond instance. */ + virtual void visit_cond(PFS_cond *pfs) {} + /** Visit a file instance. */ + virtual void visit_file(PFS_file *pfs) {} + /** Visit a socket instance. */ + virtual void visit_socket(PFS_socket *pfs) {} +}; + +/** + Iterator over groups of instrumentation point instances. + @sa PFS_instance_visitor +*/ +class PFS_instance_iterator +{ +public: + static void visit_all(PFS_instance_visitor *visitor); + static void visit_all_mutex(PFS_instance_visitor *visitor); + static void visit_all_mutex_classes(PFS_instance_visitor *visitor); + static void visit_all_mutex_instances(PFS_instance_visitor *visitor); + static void visit_all_rwlock(PFS_instance_visitor *visitor); + static void visit_all_rwlock_classes(PFS_instance_visitor *visitor); + static void visit_all_rwlock_instances(PFS_instance_visitor *visitor); + static void visit_all_cond(PFS_instance_visitor *visitor); + static void visit_all_cond_classes(PFS_instance_visitor *visitor); + static void visit_all_cond_instances(PFS_instance_visitor *visitor); + static void visit_all_file(PFS_instance_visitor *visitor); + static void visit_all_file_classes(PFS_instance_visitor *visitor); + static void visit_all_file_instances(PFS_instance_visitor *visitor); + + /** + Visit a mutex class and related instances. + @param klass the klass to visit. + @param visitor the visitor to call + */ + static void visit_mutex_instances(PFS_mutex_class *klass, + PFS_instance_visitor *visitor); + /** + Visit a rwlock class and related instances. + @param klass the klass to visit. + @param visitor the visitor to call + */ + static void visit_rwlock_instances(PFS_rwlock_class *klass, + PFS_instance_visitor *visitor); + /** + Visit a cond class and related instances. + @param klass the klass to visit. + @param visitor the visitor to call + */ + static void visit_cond_instances(PFS_cond_class *klass, + PFS_instance_visitor *visitor); + /** + Visit a file class and related instances. + @param klass the klass to visit. + @param visitor the visitor to call + */ + static void visit_file_instances(PFS_file_class *klass, + PFS_instance_visitor *visitor); + /** + Visit a socket class and related instances. + @param klass the klass to visit. + @param visitor the visitor to call + */ + static void visit_socket_instances(PFS_socket_class *klass, + PFS_instance_visitor *visitor); + /** + Visit a socket class and related instances. + @param klass the klass to visit. + @param visitor the visitor to call + @param thread the owning thread to match + @param visit_class if true then visit the socket class + */ + static void visit_socket_instances(PFS_socket_class *klass, + PFS_instance_visitor *visitor, + PFS_thread *thread, + bool visit_class= true); + /** + Visit an instrument class and related instances. + @param klass the klass to visit. + @param visitor the visitor to call + @param thread comparison criteria + @param visit_class if true then visit the class + */ + static void visit_instances(PFS_instr_class *klass, + PFS_instance_visitor *visitor, + PFS_thread *thread, + bool visit_class= true); +}; + +/** + Interface class to visit groups of SQL objects. + @sa PFS_object_iterator +*/ +class PFS_object_visitor +{ +public: + PFS_object_visitor() = default; + virtual ~PFS_object_visitor() = default; + /** Visit global data. */ + virtual void visit_global() {} + /** Visit a table share. */ + virtual void visit_table_share(PFS_table_share *pfs) {} + /** Visit a table share index. */ + virtual void visit_table_share_index(PFS_table_share *pfs, uint index) {} + /** Visit a table. */ + virtual void visit_table(PFS_table *pfs) {} + /** Visit a table index. */ + virtual void visit_table_index(PFS_table *pfs, uint index) {} +}; + +/** + Iterator over groups of SQL objects. + @sa PFS_object_visitor +*/ +class PFS_object_iterator +{ +public: + /** Visit all objects. */ + static void visit_all(PFS_object_visitor *visitor); + /** Visit all tables and related handles. */ + static void visit_all_tables(PFS_object_visitor *visitor); + /** Visit a table and related table handles. */ + static void visit_tables(PFS_table_share *share, + PFS_object_visitor *visitor); + /** Visit a table index and related table handles indexes. */ + static void visit_table_indexes(PFS_table_share *share, + uint index, + PFS_object_visitor *visitor); +}; + +/** + A concrete connection visitor that aggregates + wait statistics for a given event_name. +*/ +class PFS_connection_wait_visitor : public PFS_connection_visitor +{ +public: + /** Constructor. */ + PFS_connection_wait_visitor(PFS_instr_class *klass); + virtual ~PFS_connection_wait_visitor(); + virtual void visit_global(); + virtual void visit_host(PFS_host *pfs); + virtual void visit_account(PFS_account *pfs); + virtual void visit_user(PFS_user *pfs); + virtual void visit_thread(PFS_thread *pfs); + + /** EVENT_NAME instrument index. */ + uint m_index; + /** Wait statistic collected. */ + PFS_single_stat m_stat; +}; + +/** + A concrete connection visitor that aggregates + wait statistics for all events. +*/ +class PFS_connection_all_wait_visitor : public PFS_connection_visitor +{ +public: + /** Constructor. */ + PFS_connection_all_wait_visitor(); + virtual ~PFS_connection_all_wait_visitor(); + virtual void visit_global(); + virtual void visit_host(PFS_host *pfs); + virtual void visit_account(PFS_account *pfs); + virtual void visit_user(PFS_user *pfs); + virtual void visit_thread(PFS_thread *pfs); + + /** Wait statistic collected. */ + PFS_single_stat m_stat; + +private: + void visit_connection_slice(PFS_connection_slice *pfs); +}; + +/** + A concrete connection visitor that aggregates + stage statistics. +*/ +class PFS_connection_stage_visitor : public PFS_connection_visitor +{ +public: + /** Constructor. */ + PFS_connection_stage_visitor(PFS_stage_class *klass); + virtual ~PFS_connection_stage_visitor(); + virtual void visit_global(); + virtual void visit_host(PFS_host *pfs); + virtual void visit_account(PFS_account *pfs); + virtual void visit_user(PFS_user *pfs); + virtual void visit_thread(PFS_thread *pfs); + + /** EVENT_NAME instrument index. */ + uint m_index; + /** Stage statistic collected. */ + PFS_stage_stat m_stat; +}; + +/** + A concrete connection visitor that aggregates + statement statistics for a given event_name. +*/ +class PFS_connection_statement_visitor : public PFS_connection_visitor +{ +public: + /** Constructor. */ + PFS_connection_statement_visitor(PFS_statement_class *klass); + virtual ~PFS_connection_statement_visitor(); + virtual void visit_global(); + virtual void visit_host(PFS_host *pfs); + virtual void visit_account(PFS_account *pfs); + virtual void visit_user(PFS_user *pfs); + virtual void visit_thread(PFS_thread *pfs); + + /** EVENT_NAME instrument index. */ + uint m_index; + /** Statement statistic collected. */ + PFS_statement_stat m_stat; +}; + +/** + A concrete connection visitor that aggregates + statement statistics for all events. +*/ +class PFS_connection_all_statement_visitor : public PFS_connection_visitor +{ +public: + /** Constructor. */ + PFS_connection_all_statement_visitor(); + virtual ~PFS_connection_all_statement_visitor(); + virtual void visit_global(); + virtual void visit_host(PFS_host *pfs); + virtual void visit_account(PFS_account *pfs); + virtual void visit_user(PFS_user *pfs); + virtual void visit_thread(PFS_thread *pfs); + + /** Statement statistic collected. */ + PFS_statement_stat m_stat; + +private: + void visit_connection_slice(PFS_connection_slice *pfs); +}; + +/** + A concrete connection visitor that aggregates + transaction statistics for a given event_name. +*/ +class PFS_connection_transaction_visitor : public PFS_connection_visitor +{ +public: + /** Constructor. */ + PFS_connection_transaction_visitor(PFS_transaction_class *klass); + virtual ~PFS_connection_transaction_visitor(); + virtual void visit_global(); + virtual void visit_host(PFS_host *pfs); + virtual void visit_account(PFS_account *pfs); + virtual void visit_user(PFS_user *pfs); + virtual void visit_thread(PFS_thread *pfs); + + /** EVENT_NAME instrument index. */ + uint m_index; + /** Statement statistic collected. */ + PFS_transaction_stat m_stat; +}; + +/** Disabled pending code review */ +#if 0 +/** + A concrete connection visitor that aggregates + transaction statistics for all events. +*/ +class PFS_connection_all_transaction_visitor : public PFS_connection_visitor +{ +public: + /** Constructor. */ + PFS_connection_all_transaction_visitor(); + virtual ~PFS_connection_all_transaction_visitor(); + virtual void visit_global(); + virtual void visit_host(PFS_host *pfs); + virtual void visit_account(PFS_account *pfs); + virtual void visit_user(PFS_user *pfs); + virtual void visit_thread(PFS_thread *pfs); + + /** Statement statistic collected. */ + PFS_transaction_stat m_stat; + +private: + void visit_connection_slice(PFS_connection_slice *pfs); +}; +#endif + +/** + A concrete connection visitor that aggregates + connection statistics. +*/ +class PFS_connection_stat_visitor : public PFS_connection_visitor +{ +public: + /** Constructor. */ + PFS_connection_stat_visitor(); + virtual ~PFS_connection_stat_visitor(); + virtual void visit_global(); + virtual void visit_host(PFS_host *pfs); + virtual void visit_account(PFS_account *pfs); + virtual void visit_user(PFS_user *pfs); + virtual void visit_thread(PFS_thread *pfs); + + /** Connection statistic collected. */ + PFS_connection_stat m_stat; +}; + +/** + A concrete connection visitor that aggregates + memory statistics for a given event_name. +*/ +class PFS_connection_memory_visitor : public PFS_connection_visitor +{ +public: + /** Constructor. */ + PFS_connection_memory_visitor(PFS_memory_class *klass); + virtual ~PFS_connection_memory_visitor(); + virtual void visit_global(); + virtual void visit_host(PFS_host *pfs); + virtual void visit_account(PFS_account *pfs); + virtual void visit_user(PFS_user *pfs); + virtual void visit_thread(PFS_thread *pfs); + + /** EVENT_NAME instrument index. */ + uint m_index; + /** Statement statistic collected. */ + PFS_memory_stat m_stat; +}; + +/** + A concrete connection visitor that aggregates + status variables. +*/ +class PFS_connection_status_visitor : public PFS_connection_visitor +{ +public: + /** Constructor. */ + PFS_connection_status_visitor(STATUS_VAR *status_vars); + virtual ~PFS_connection_status_visitor(); + virtual void visit_global(); + virtual void visit_host(PFS_host *pfs); + virtual void visit_account(PFS_account *pfs); + virtual void visit_user(PFS_user *pfs); + virtual void visit_thread(PFS_thread *pfs); + virtual void visit_THD(THD *thd); + +private: + STATUS_VAR *m_status_vars; +}; + +/** + A concrete instance visitor that aggregates + wait statistics. +*/ +class PFS_instance_wait_visitor : public PFS_instance_visitor +{ +public: + PFS_instance_wait_visitor(); + virtual ~PFS_instance_wait_visitor(); + virtual void visit_mutex_class(PFS_mutex_class *pfs); + virtual void visit_rwlock_class(PFS_rwlock_class *pfs); + virtual void visit_cond_class(PFS_cond_class *pfs); + virtual void visit_file_class(PFS_file_class *pfs); + virtual void visit_socket_class(PFS_socket_class *pfs); + virtual void visit_mutex(PFS_mutex *pfs); + virtual void visit_rwlock(PFS_rwlock *pfs); + virtual void visit_cond(PFS_cond *pfs); + virtual void visit_file(PFS_file *pfs); + virtual void visit_socket(PFS_socket *pfs); + + /** Wait statistic collected. */ + PFS_single_stat m_stat; +}; + +/** + A concrete object visitor that aggregates + object wait statistics. +*/ +class PFS_object_wait_visitor : public PFS_object_visitor +{ +public: + PFS_object_wait_visitor(); + virtual ~PFS_object_wait_visitor(); + virtual void visit_global(); + virtual void visit_table_share(PFS_table_share *pfs); + virtual void visit_table(PFS_table *pfs); + + /** Object wait statistic collected. */ + PFS_single_stat m_stat; +}; + +/** + A concrete object visitor that aggregates + table io wait statistics. +*/ +class PFS_table_io_wait_visitor : public PFS_object_visitor +{ +public: + PFS_table_io_wait_visitor(); + virtual ~PFS_table_io_wait_visitor(); + virtual void visit_global(); + virtual void visit_table_share(PFS_table_share *pfs); + virtual void visit_table(PFS_table *pfs); + + /** Table io wait statistic collected. */ + PFS_single_stat m_stat; +}; + +/** + A concrete object visitor that aggregates + table io statistics. +*/ +class PFS_table_io_stat_visitor : public PFS_object_visitor +{ +public: + PFS_table_io_stat_visitor(); + virtual ~PFS_table_io_stat_visitor(); + virtual void visit_table_share(PFS_table_share *pfs); + virtual void visit_table(PFS_table *pfs); + + /** Table io statistic collected. */ + PFS_table_io_stat m_stat; +}; + +/** + A concrete object visitor that aggregates + index io statistics. +*/ +class PFS_index_io_stat_visitor : public PFS_object_visitor +{ +public: + PFS_index_io_stat_visitor(); + virtual ~PFS_index_io_stat_visitor(); + virtual void visit_table_share_index(PFS_table_share *pfs, uint index); + virtual void visit_table_index(PFS_table *pfs, uint index); + + /** Index io statistic collected. */ + PFS_table_io_stat m_stat; +}; + +/** + A concrete object visitor that aggregates + table lock wait statistics. +*/ +class PFS_table_lock_wait_visitor : public PFS_object_visitor +{ +public: + PFS_table_lock_wait_visitor(); + virtual ~PFS_table_lock_wait_visitor(); + virtual void visit_global(); + virtual void visit_table_share(PFS_table_share *pfs); + virtual void visit_table(PFS_table *pfs); + + /** Table lock wait statistic collected. */ + PFS_single_stat m_stat; +}; + +/** + A concrete object visitor that aggregates + table lock statistics. +*/ +class PFS_table_lock_stat_visitor : public PFS_object_visitor +{ +public: + PFS_table_lock_stat_visitor(); + virtual ~PFS_table_lock_stat_visitor(); + virtual void visit_table_share(PFS_table_share *pfs); + virtual void visit_table(PFS_table *pfs); + + /** Table lock statistic collected. */ + PFS_table_lock_stat m_stat; +}; + +/** + A concrete instance visitor that aggregates + socket wait and byte count statistics. +*/ +class PFS_instance_socket_io_stat_visitor : public PFS_instance_visitor +{ +public: + PFS_instance_socket_io_stat_visitor(); + virtual ~PFS_instance_socket_io_stat_visitor(); + virtual void visit_socket_class(PFS_socket_class *pfs); + virtual void visit_socket(PFS_socket *pfs); + + /** Wait and byte count statistics collected. */ + PFS_socket_io_stat m_socket_io_stat; +}; + +/** + A concrete instance visitor that aggregates + file wait and byte count statistics. +*/ +class PFS_instance_file_io_stat_visitor : public PFS_instance_visitor +{ +public: + PFS_instance_file_io_stat_visitor(); + virtual ~PFS_instance_file_io_stat_visitor(); + virtual void visit_file_class(PFS_file_class *pfs); + virtual void visit_file(PFS_file *pfs); + + /** Wait and byte count statistics collected. */ + PFS_file_io_stat m_file_io_stat; +}; + +/** @} */ +#endif + diff --git a/storage/perfschema/rpl_gtid.h b/storage/perfschema/rpl_gtid.h new file mode 100644 index 00000000..6d9ecb0e --- /dev/null +++ b/storage/perfschema/rpl_gtid.h @@ -0,0 +1,17 @@ +#ifndef STORAGE_PERFSCHEMA_RPL_GTID_INCLUDED +#define STORAGE_PERFSCHEMA_RPL_GTID_INCLUDED + +struct TABLE; + +#include "../../sql/rpl_gtid.h" + +class Gtid_specification: public rpl_gtid +{ +public: + size_t to_string(char *buf) + { + return my_snprintf(buf, GTID_MAX_STR_LENGTH, "%u-%u-%llu", + domain_id, server_id, seq_no); + } +}; +#endif diff --git a/storage/perfschema/table_accounts.cc b/storage/perfschema/table_accounts.cc new file mode 100644 index 00000000..654a4400 --- /dev/null +++ b/storage/perfschema/table_accounts.cc @@ -0,0 +1,150 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "my_global.h" +#include "my_thread.h" +#include "table_accounts.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_account.h" +#include "pfs_visitor.h" +#include "pfs_memory.h" +#include "pfs_status.h" +#include "field.h" + +THR_LOCK table_accounts::m_table_lock; + + +PFS_engine_table_share_state +table_accounts::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_accounts::m_share= +{ + { C_STRING_WITH_LEN("accounts") }, + &pfs_truncatable_acl, + table_accounts::create, + NULL, /* write_row */ + table_accounts::delete_all_rows, + cursor_by_account::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE accounts(" + "USER CHAR(" USERNAME_CHAR_LENGTH_STR ") collate utf8_bin default null comment 'The connection''s client user name for the connection, or NULL if an internal thread.'," + "HOST CHAR(" HOSTNAME_LENGTH_STR ") collate utf8_bin default null comment 'The connection client''s host name, or NULL if an internal thread.'," + "CURRENT_CONNECTIONS bigint not null comment 'Current connections for the account.'," + "TOTAL_CONNECTIONS bigint not null comment 'Total connections for the account.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_accounts::create() +{ + return new table_accounts(); +} + +int +table_accounts::delete_all_rows(void) +{ + reset_events_waits_by_thread(); + reset_events_waits_by_account(); + reset_events_stages_by_thread(); + reset_events_stages_by_account(); + reset_events_statements_by_thread(); + reset_events_statements_by_account(); + reset_events_transactions_by_thread(); + reset_events_transactions_by_account(); + reset_memory_by_thread(); + reset_memory_by_account(); + reset_status_by_thread(); + reset_status_by_account(); + purge_all_account(); + return 0; +} + +table_accounts::table_accounts() + : cursor_by_account(& m_share), + m_row_exists(false) +{} + +void table_accounts::make_row(PFS_account *pfs) +{ + pfs_optimistic_state lock; + + m_row_exists= false; + pfs->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_account.make_row(pfs)) + return; + + PFS_connection_stat_visitor visitor; + PFS_connection_iterator::visit_account(pfs, + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! pfs->m_lock.end_optimistic_lock(& lock)) + return; + + m_row.m_connection_stat.set(& visitor.m_stat); + m_row_exists= true; +} + +int table_accounts::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* USER */ + case 1: /* HOST */ + m_row.m_account.set_field(f->field_index, f); + break; + case 2: /* CURRENT_CONNECTIONS */ + case 3: /* TOTAL_CONNECTIONS */ + m_row.m_connection_stat.set_field(f->field_index - 2, f); + break; + default: + assert(false); + } + } + } + return 0; +} + diff --git a/storage/perfschema/table_accounts.h b/storage/perfschema/table_accounts.h new file mode 100644 index 00000000..28348190 --- /dev/null +++ b/storage/perfschema/table_accounts.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ACCOUNTS_H +#define TABLE_ACCOUNTS_H + +#include "pfs_column_types.h" +#include "cursor_by_account.h" +#include "table_helper.h" + +struct PFS_account; + +/** + \addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of PERFORMANCE_SCHEMA.ACCOUNTS. +*/ +struct row_accounts +{ + /** Column USER, HOST. */ + PFS_account_row m_account; + /** Columns CURRENT_CONNECTIONS, TOTAL_CONNECTIONS. */ + PFS_connection_stat_row m_connection_stat; +}; + +/** Table PERFORMANCE_SCHEMA.ACCOUNTS. */ +class table_accounts : public cursor_by_account +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + /** Table builder */ + static PFS_engine_table* create(); + static int delete_all_rows(); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + +protected: + table_accounts(); + +public: + ~table_accounts() = default; + +private: + virtual void make_row(PFS_account *pfs); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_accounts m_row; + /** True if the current row exists. */ + bool m_row_exists; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_all_instr.cc b/storage/perfschema/table_all_instr.cc new file mode 100644 index 00000000..1ca0525a --- /dev/null +++ b/storage/perfschema/table_all_instr.cc @@ -0,0 +1,188 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_all_instr.cc + Abstract tables for all instruments (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "table_all_instr.h" +#include "pfs_global.h" +#include "pfs_buffer_container.h" + +ha_rows +table_all_instr::get_row_count(void) +{ + return global_mutex_container.get_row_count() + + global_rwlock_container.get_row_count() + + global_cond_container.get_row_count() + + global_file_container.get_row_count() + + global_socket_container.get_row_count() ; +} + +table_all_instr::table_all_instr(const PFS_engine_table_share *share) + : PFS_engine_table(share, &m_pos), + m_pos(), m_next_pos() +{} + +void table_all_instr::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_all_instr::rnd_next(void) +{ + PFS_mutex *mutex; + PFS_rwlock *rwlock; + PFS_cond *cond; + PFS_file *file; + PFS_socket *socket; + + for (m_pos.set_at(&m_next_pos); + m_pos.has_more_view(); + m_pos.next_view()) + { + switch (m_pos.m_index_1) { + case pos_all_instr::VIEW_MUTEX: + { + PFS_mutex_iterator it= global_mutex_container.iterate(m_pos.m_index_2); + mutex= it.scan_next(& m_pos.m_index_2); + if (mutex != NULL) + { + make_mutex_row(mutex); + m_next_pos.set_after(&m_pos); + return 0; + } + } + break; + case pos_all_instr::VIEW_RWLOCK: + { + PFS_rwlock_iterator it= global_rwlock_container.iterate(m_pos.m_index_2); + rwlock= it.scan_next(& m_pos.m_index_2); + if (rwlock != NULL) + { + make_rwlock_row(rwlock); + m_next_pos.set_after(&m_pos); + return 0; + } + } + break; + case pos_all_instr::VIEW_COND: + { + PFS_cond_iterator it= global_cond_container.iterate(m_pos.m_index_2); + cond= it.scan_next(& m_pos.m_index_2); + if (cond != NULL) + { + make_cond_row(cond); + m_next_pos.set_after(&m_pos); + return 0; + } + } + break; + case pos_all_instr::VIEW_FILE: + { + PFS_file_iterator it= global_file_container.iterate(m_pos.m_index_2); + file= it.scan_next(& m_pos.m_index_2); + if (file != NULL) + { + make_file_row(file); + m_next_pos.set_after(&m_pos); + return 0; + } + } + break; + case pos_all_instr::VIEW_SOCKET: + { + PFS_socket_iterator it= global_socket_container.iterate(m_pos.m_index_2); + socket= it.scan_next(& m_pos.m_index_2); + if (socket != NULL) + { + make_socket_row(socket); + m_next_pos.set_after(&m_pos); + return 0; + } + } + break; + } + } + + return HA_ERR_END_OF_FILE; +} + +int table_all_instr::rnd_pos(const void *pos) +{ + PFS_mutex *mutex; + PFS_rwlock *rwlock; + PFS_cond *cond; + PFS_file *file; + PFS_socket *socket; + + set_position(pos); + + switch (m_pos.m_index_1) { + case pos_all_instr::VIEW_MUTEX: + mutex= global_mutex_container.get(m_pos.m_index_2); + if (mutex != NULL) + { + make_mutex_row(mutex); + return 0; + } + break; + case pos_all_instr::VIEW_RWLOCK: + rwlock= global_rwlock_container.get(m_pos.m_index_2); + if (rwlock != NULL) + { + make_rwlock_row(rwlock); + return 0; + } + break; + case pos_all_instr::VIEW_COND: + cond= global_cond_container.get(m_pos.m_index_2); + if (cond != NULL) + { + make_cond_row(cond); + return 0; + } + break; + case pos_all_instr::VIEW_FILE: + file= global_file_container.get(m_pos.m_index_2); + if (file != NULL) + { + make_file_row(file); + return 0; + } + break; + case pos_all_instr::VIEW_SOCKET: + socket= global_socket_container.get(m_pos.m_index_2); + if (socket != NULL) + { + make_socket_row(socket); + return 0; + } + break; + } + + return HA_ERR_RECORD_DELETED; +} diff --git a/storage/perfschema/table_all_instr.h b/storage/perfschema/table_all_instr.h new file mode 100644 index 00000000..d7f3fe6a --- /dev/null +++ b/storage/perfschema/table_all_instr.h @@ -0,0 +1,123 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ALL_INSTR_H +#define TABLE_ALL_INSTR_H + +/** + @file storage/perfschema/table_all_instr.h + Abstract tables for all instruments (declarations). +*/ + +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_engine_table.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** Position of a cursor on table_all_instr. */ +struct pos_all_instr : public PFS_double_index, + public PFS_instrument_view_constants +{ + pos_all_instr() + : PFS_double_index(FIRST_VIEW, 0) + {} + + inline void reset(void) + { + m_index_1= FIRST_VIEW; + m_index_2= 0; + } + + inline bool has_more_view(void) + { return (m_index_1 <= LAST_VIEW); } + + inline void next_view(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** + Abstract table, a union of all instrumentations instances. + This table is a union of: + - a view on all mutex instances, + - a view on all rwlock instances, + - a view on all cond instances, + - a view on all file instances, + - a view on all socket instances +*/ +class table_all_instr : public PFS_engine_table +{ +public: + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + table_all_instr(const PFS_engine_table_share *share); + +public: + ~table_all_instr() = default; + +protected: + /** + Build a row in the mutex instance view. + @param pfs the mutex instance + */ + virtual void make_mutex_row(PFS_mutex *pfs)= 0; + /** + Build a row in the rwlock instance view. + @param pfs the rwlock instance + */ + virtual void make_rwlock_row(PFS_rwlock *pfs)= 0; + /** + Build a row in the condition instance view. + @param pfs the condition instance + */ + virtual void make_cond_row(PFS_cond *pfs)= 0; + /** + Build a row in the file instance view. + @param pfs the file instance + */ + virtual void make_file_row(PFS_file *pfs)= 0; + /** + Build a row in the socket instance view. + @param pfs the socket instance + */ + virtual void make_socket_row(PFS_socket *pfs)= 0; + + /** Current position. */ + pos_all_instr m_pos; + /** Next position. */ + pos_all_instr m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_esgs_by_account_by_event_name.cc b/storage/perfschema/table_esgs_by_account_by_event_name.cc new file mode 100644 index 00000000..42d3592a --- /dev/null +++ b/storage/perfschema/table_esgs_by_account_by_event_name.cc @@ -0,0 +1,218 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_esgs_by_account_by_event_name.cc + Table EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_esgs_by_account_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_esgs_by_account_by_event_name::m_table_lock; + + +PFS_engine_table_share_state +table_esgs_by_account_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_esgs_by_account_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_stages_summary_by_account_by_event_name") }, + &pfs_truncatable_acl, + table_esgs_by_account_by_event_name::create, + NULL, /* write_row */ + table_esgs_by_account_by_event_name::delete_all_rows, + table_esgs_by_account_by_event_name::get_row_count, + sizeof(pos_esgs_by_account_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_stages_summary_by_account_by_event_name(" + "USER CHAR(" USERNAME_CHAR_LENGTH_STR ") collate utf8_bin default null comment 'User. Used together with HOST and EVENT_NAME for grouping events.'," + "HOST CHAR(" HOSTNAME_LENGTH_STR ") collate utf8_bin default null comment 'Host. Used together with USER and EVENT_NAME for grouping events.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name. Used together with USER and HOST for grouping events.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events, which includes all timed and untimed events.'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the timed summarized events.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the timed summarized events.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the timed summarized events.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the timed summarized events.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_esgs_by_account_by_event_name::create(void) +{ + return new table_esgs_by_account_by_event_name(); +} + +int +table_esgs_by_account_by_event_name::delete_all_rows(void) +{ + reset_events_stages_by_thread(); + reset_events_stages_by_account(); + return 0; +} + +ha_rows +table_esgs_by_account_by_event_name::get_row_count(void) +{ + return global_account_container.get_row_count() * stage_class_max; +} + +table_esgs_by_account_by_event_name::table_esgs_by_account_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_esgs_by_account_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_esgs_by_account_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(stage_timer); + return 0; +} + +int table_esgs_by_account_by_event_name::rnd_next(void) +{ + PFS_account *account; + PFS_stage_class *stage_class; + bool has_more_account= true; + + for (m_pos.set_at(&m_next_pos); + has_more_account; + m_pos.next_account()) + { + account= global_account_container.get(m_pos.m_index_1, & has_more_account); + if (account != NULL) + { + stage_class= find_stage_class(m_pos.m_index_2); + if (stage_class) + { + make_row(account, stage_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_esgs_by_account_by_event_name::rnd_pos(const void *pos) +{ + PFS_account *account; + PFS_stage_class *stage_class; + + set_position(pos); + + account= global_account_container.get(m_pos.m_index_1); + if (account != NULL) + { + stage_class= find_stage_class(m_pos.m_index_2); + if (stage_class) + { + make_row(account, stage_class); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_esgs_by_account_by_event_name +::make_row(PFS_account *account, PFS_stage_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + account->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_account.make_row(account)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_stage_visitor visitor(klass); + PFS_connection_iterator::visit_account(account, + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! account->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(m_normalizer, & visitor.m_stat); +} + +int table_esgs_by_account_by_event_name +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* USER */ + case 1: /* HOST */ + m_row.m_account.set_field(f->field_index, f); + break; + case 2: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 3, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 3, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_esgs_by_account_by_event_name.h b/storage/perfschema/table_esgs_by_account_by_event_name.h new file mode 100644 index 00000000..fa0c7d8e --- /dev/null +++ b/storage/perfschema/table_esgs_by_account_by_event_name.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ESGS_BY_ACCOUNT_BY_EVENT_NAME_H +#define TABLE_ESGS_BY_ACCOUNT_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_esgs_by_account_by_event_name.h + Table EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_account.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. +*/ +struct row_esgs_by_account_by_event_name +{ + /** Column USER, HOST. */ + PFS_account_row m_account; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_stage_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. + Index 1 on account (0 based) + Index 2 on stage class (1 based) +*/ +struct pos_esgs_by_account_by_event_name +: public PFS_double_index +{ + pos_esgs_by_account_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_account(void) + { + m_index_1++; + m_index_2= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. */ +class table_esgs_by_account_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_esgs_by_account_by_event_name(); + +public: + ~table_esgs_by_account_by_event_name() = default; + +protected: + void make_row(PFS_account *account, PFS_stage_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_esgs_by_account_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_esgs_by_account_by_event_name m_pos; + /** Next position. */ + pos_esgs_by_account_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_esgs_by_host_by_event_name.cc b/storage/perfschema/table_esgs_by_host_by_event_name.cc new file mode 100644 index 00000000..e5982164 --- /dev/null +++ b/storage/perfschema/table_esgs_by_host_by_event_name.cc @@ -0,0 +1,218 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_esgs_by_host_by_event_name.cc + Table EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_esgs_by_host_by_event_name.h" +#include "pfs_global.h" +#include "pfs_account.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_esgs_by_host_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_esgs_by_host_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_esgs_by_host_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_stages_summary_by_host_by_event_name") }, + &pfs_truncatable_acl, + table_esgs_by_host_by_event_name::create, + NULL, /* write_row */ + table_esgs_by_host_by_event_name::delete_all_rows, + table_esgs_by_host_by_event_name::get_row_count, + sizeof(pos_esgs_by_host_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_stages_summary_by_host_by_event_name(" + "HOST CHAR(" STRINGIFY_ARG(HOSTNAME_LENGTH) ") collate utf8_bin default null comment 'Host. Used together with EVENT_NAME for grouping events.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name. Used together with HOST for grouping events.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events, which includes all timed and untimed events.'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the timed summarized events.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the timed summarized events.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the timed summarized events.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the timed summarized events.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_esgs_by_host_by_event_name::create(void) +{ + return new table_esgs_by_host_by_event_name(); +} + +int +table_esgs_by_host_by_event_name::delete_all_rows(void) +{ + reset_events_stages_by_thread(); + reset_events_stages_by_account(); + reset_events_stages_by_host(); + return 0; +} + +ha_rows +table_esgs_by_host_by_event_name::get_row_count(void) +{ + return global_host_container.get_row_count() * stage_class_max; +} + +table_esgs_by_host_by_event_name::table_esgs_by_host_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_esgs_by_host_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_esgs_by_host_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(stage_timer); + return 0; +} + +int table_esgs_by_host_by_event_name::rnd_next(void) +{ + PFS_host *host; + PFS_stage_class *stage_class; + bool has_more_host= true; + + for (m_pos.set_at(&m_next_pos); + has_more_host; + m_pos.next_host()) + { + host= global_host_container.get(m_pos.m_index_1, & has_more_host); + if (host != NULL) + { + stage_class= find_stage_class(m_pos.m_index_2); + if (stage_class) + { + make_row(host, stage_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_esgs_by_host_by_event_name::rnd_pos(const void *pos) +{ + PFS_host *host; + PFS_stage_class *stage_class; + + set_position(pos); + + host= global_host_container.get(m_pos.m_index_1); + if (host != NULL) + { + stage_class= find_stage_class(m_pos.m_index_2); + if (stage_class) + { + make_row(host, stage_class); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_esgs_by_host_by_event_name +::make_row(PFS_host *host, PFS_stage_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + host->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_host.make_row(host)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_stage_visitor visitor(klass); + PFS_connection_iterator::visit_host(host, + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! host->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(m_normalizer, & visitor.m_stat); +} + +int table_esgs_by_host_by_event_name +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* HOST */ + m_row.m_host.set_field(f); + break; + case 1: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 2, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_esgs_by_host_by_event_name.h b/storage/perfschema/table_esgs_by_host_by_event_name.h new file mode 100644 index 00000000..f71a479c --- /dev/null +++ b/storage/perfschema/table_esgs_by_host_by_event_name.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ESGS_BY_HOST_BY_EVENT_NAME_H +#define TABLE_ESGS_BY_HOST_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_esgs_by_host_by_event_name.h + Table EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_host.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME. +*/ +struct row_esgs_by_host_by_event_name +{ + /** Column HOST. */ + PFS_host_row m_host; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_stage_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME. + Index 1 on host (0 based) + Index 2 on stage class (1 based). +*/ +struct pos_esgs_by_host_by_event_name +: public PFS_double_index +{ + pos_esgs_by_host_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_host(void) + { + m_index_1++; + m_index_2= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME. */ +class table_esgs_by_host_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_esgs_by_host_by_event_name(); + +public: + ~table_esgs_by_host_by_event_name() = default; + +protected: + void make_row(PFS_host *host, PFS_stage_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_esgs_by_host_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_esgs_by_host_by_event_name m_pos; + /** Next position. */ + pos_esgs_by_host_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_esgs_by_thread_by_event_name.cc b/storage/perfschema/table_esgs_by_thread_by_event_name.cc new file mode 100644 index 00000000..c393eae9 --- /dev/null +++ b/storage/perfschema/table_esgs_by_thread_by_event_name.cc @@ -0,0 +1,209 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_esgs_by_thread_by_event_name.cc + Table EVENTS_STAGES_SUMMARY_BY_HOST_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_esgs_by_thread_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_esgs_by_thread_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_esgs_by_thread_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_esgs_by_thread_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_stages_summary_by_thread_by_event_name") }, + &pfs_truncatable_acl, + table_esgs_by_thread_by_event_name::create, + NULL, /* write_row */ + table_esgs_by_thread_by_event_name::delete_all_rows, + table_esgs_by_thread_by_event_name::get_row_count, + sizeof(pos_esgs_by_thread_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_stages_summary_by_thread_by_event_name(" + "THREAD_ID BIGINT unsigned not null comment 'Thread associated with the event. Together with EVENT_NAME uniquely identifies the row.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name. Used together with THREAD_ID for grouping events.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events, which includes all timed and untimed events.'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the timed summarized events.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the timed summarized events.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the timed summarized events.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the timed summarized events.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_esgs_by_thread_by_event_name::create(void) +{ + return new table_esgs_by_thread_by_event_name(); +} + +int +table_esgs_by_thread_by_event_name::delete_all_rows(void) +{ + reset_events_stages_by_thread(); + return 0; +} + +ha_rows +table_esgs_by_thread_by_event_name::get_row_count(void) +{ + return global_thread_container.get_row_count() * stage_class_max; +} + +table_esgs_by_thread_by_event_name::table_esgs_by_thread_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_esgs_by_thread_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_esgs_by_thread_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(stage_timer); + return 0; +} + +int table_esgs_by_thread_by_event_name::rnd_next(void) +{ + PFS_thread *thread; + PFS_stage_class *stage_class; + bool has_more_thread= true; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread); + if (thread != NULL) + { + stage_class= find_stage_class(m_pos.m_index_2); + if (stage_class) + { + make_row(thread, stage_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_esgs_by_thread_by_event_name::rnd_pos(const void *pos) +{ + PFS_thread *thread; + PFS_stage_class *stage_class; + + set_position(pos); + + thread= global_thread_container.get(m_pos.m_index_1); + if (thread != NULL) + { + stage_class= find_stage_class(m_pos.m_index_2); + if (stage_class) + { + make_row(thread, stage_class); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_esgs_by_thread_by_event_name +::make_row(PFS_thread *thread, PFS_stage_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + /* Protect this reader against a thread termination */ + thread->m_lock.begin_optimistic_lock(&lock); + + m_row.m_thread_internal_id= thread->m_thread_internal_id; + + m_row.m_event_name.make_row(klass); + + PFS_connection_stage_visitor visitor(klass); + PFS_connection_iterator::visit_thread(thread, & visitor); + + if (thread->m_lock.end_optimistic_lock(&lock)) + m_row_exists= true; + + m_row.m_stat.set(m_normalizer, & visitor.m_stat); +} + +int table_esgs_by_thread_by_event_name +::read_row_values(TABLE *table, unsigned char *, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + 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: /* NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 2, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_esgs_by_thread_by_event_name.h b/storage/perfschema/table_esgs_by_thread_by_event_name.h new file mode 100644 index 00000000..f9a52ebe --- /dev/null +++ b/storage/perfschema/table_esgs_by_thread_by_event_name.h @@ -0,0 +1,132 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ESGS_BY_THREAD_BY_EVENT_NAME_H +#define TABLE_ESGS_BY_THREAD_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_esgs_by_thread_by_event_name.h + Table EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME. +*/ +struct row_esgs_by_thread_by_event_name +{ + /** Column THREAD_ID. */ + ulonglong m_thread_internal_id; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_stage_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME. + Index 1 on thread (0 based). + Index 2 on stage class (1 based). +*/ +struct pos_esgs_by_thread_by_event_name +: public PFS_double_index +{ + pos_esgs_by_thread_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= 1; + } + + inline void next_stage(void) + { + m_index_2++; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_BY_THREAD_BY_EVENT_NAME. */ +class table_esgs_by_thread_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_esgs_by_thread_by_event_name(); + +public: + ~table_esgs_by_thread_by_event_name() = default; + +protected: + void make_row(PFS_thread *thread, PFS_stage_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_esgs_by_thread_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_esgs_by_thread_by_event_name m_pos; + /** Next position. */ + pos_esgs_by_thread_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_esgs_by_user_by_event_name.cc b/storage/perfschema/table_esgs_by_user_by_event_name.cc new file mode 100644 index 00000000..fee2efe3 --- /dev/null +++ b/storage/perfschema/table_esgs_by_user_by_event_name.cc @@ -0,0 +1,217 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_esgs_by_user_by_event_name.cc + Table EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_esgs_by_user_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_esgs_by_user_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_esgs_by_user_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_esgs_by_user_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_stages_summary_by_user_by_event_name") }, + &pfs_truncatable_acl, + table_esgs_by_user_by_event_name::create, + NULL, /* write_row */ + table_esgs_by_user_by_event_name::delete_all_rows, + table_esgs_by_user_by_event_name::get_row_count, + sizeof(pos_esgs_by_user_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_stages_summary_by_user_by_event_name(" + "USER CHAR(" USERNAME_CHAR_LENGTH_STR ") collate utf8_bin default null comment 'User. Used together with EVENT_NAME for grouping events.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name. Used together with USER for grouping events.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events, which includes all timed and untimed events.'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the timed summarized events.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the timed summarized events.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the timed summarized events.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the timed summarized events.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_esgs_by_user_by_event_name::create(void) +{ + return new table_esgs_by_user_by_event_name(); +} + +int +table_esgs_by_user_by_event_name::delete_all_rows(void) +{ + reset_events_stages_by_thread(); + reset_events_stages_by_account(); + reset_events_stages_by_user(); + return 0; +} + +ha_rows +table_esgs_by_user_by_event_name::get_row_count(void) +{ + return global_user_container.get_row_count() * stage_class_max; +} + +table_esgs_by_user_by_event_name::table_esgs_by_user_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_esgs_by_user_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_esgs_by_user_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(stage_timer); + return 0; +} + +int table_esgs_by_user_by_event_name::rnd_next(void) +{ + PFS_user *user; + PFS_stage_class *stage_class; + bool has_more_user= true; + + for (m_pos.set_at(&m_next_pos); + has_more_user; + m_pos.next_user()) + { + user= global_user_container.get(m_pos.m_index_1, & has_more_user); + if (user != NULL) + { + stage_class= find_stage_class(m_pos.m_index_2); + if (stage_class) + { + make_row(user, stage_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_esgs_by_user_by_event_name::rnd_pos(const void *pos) +{ + PFS_user *user; + PFS_stage_class *stage_class; + + set_position(pos); + + user= global_user_container.get(m_pos.m_index_1); + if (user != NULL) + { + stage_class= find_stage_class(m_pos.m_index_2); + if (stage_class) + { + make_row(user, stage_class); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_esgs_by_user_by_event_name +::make_row(PFS_user *user, PFS_stage_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + user->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_user.make_row(user)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_stage_visitor visitor(klass); + PFS_connection_iterator::visit_user(user, + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! user->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(m_normalizer, & visitor.m_stat); +} + +int table_esgs_by_user_by_event_name +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* USER */ + m_row.m_user.set_field(f); + break; + case 1: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 2, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_esgs_by_user_by_event_name.h b/storage/perfschema/table_esgs_by_user_by_event_name.h new file mode 100644 index 00000000..e8b42108 --- /dev/null +++ b/storage/perfschema/table_esgs_by_user_by_event_name.h @@ -0,0 +1,133 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ESGS_BY_USER_BY_EVENT_NAME_H +#define TABLE_ESGS_BY_USER_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_esgs_by_user_by_event_name.h + Table EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_user.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME. +*/ +struct row_esgs_by_user_by_event_name +{ + /** Column USER. */ + PFS_user_row m_user; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_stage_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME. + Index 1 on user (0 based) + Index 2 on stage class (1 based) +*/ +struct pos_esgs_by_user_by_event_name +: public PFS_double_index +{ + pos_esgs_by_user_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_user(void) + { + m_index_1++; + m_index_2= 1; + } + + inline void next_stage(void) + { + m_index_2++; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_BY_USER_BY_EVENT_NAME. */ +class table_esgs_by_user_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_esgs_by_user_by_event_name(); + +public: + ~table_esgs_by_user_by_event_name() = default; + +protected: + void make_row(PFS_user *user, PFS_stage_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_esgs_by_user_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_esgs_by_user_by_event_name m_pos; + /** Next position. */ + pos_esgs_by_user_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_esgs_global_by_event_name.cc b/storage/perfschema/table_esgs_global_by_event_name.cc new file mode 100644 index 00000000..37d134db --- /dev/null +++ b/storage/perfschema/table_esgs_global_by_event_name.cc @@ -0,0 +1,198 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_esgs_global_by_event_name.cc + Table EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_esgs_global_by_event_name.h" +#include "pfs_global.h" +#include "pfs_instr.h" +#include "pfs_timer.h" +#include "pfs_visitor.h" +#include "field.h" + +THR_LOCK table_esgs_global_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_esgs_global_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_esgs_global_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_stages_summary_global_by_event_name") }, + &pfs_truncatable_acl, + table_esgs_global_by_event_name::create, + NULL, /* write_row */ + table_esgs_global_by_event_name::delete_all_rows, + table_esgs_global_by_event_name::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_stages_summary_global_by_event_name(" + "EVENT_NAME VARCHAR(128) not null comment 'Event name.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events, which includes all timed and untimed events.'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the timed summarized events.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the timed summarized events.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the timed summarized events.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the timed summarized events.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_esgs_global_by_event_name::create(void) +{ + return new table_esgs_global_by_event_name(); +} + +int +table_esgs_global_by_event_name::delete_all_rows(void) +{ + reset_events_stages_by_thread(); + reset_events_stages_by_account(); + reset_events_stages_by_user(); + reset_events_stages_by_host(); + reset_events_stages_global(); + return 0; +} + +ha_rows +table_esgs_global_by_event_name::get_row_count(void) +{ + return stage_class_max; +} + +table_esgs_global_by_event_name::table_esgs_global_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(1), m_next_pos(1) +{} + +void table_esgs_global_by_event_name::reset_position(void) +{ + m_pos= 1; + m_next_pos= 1; +} + +int table_esgs_global_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(stage_timer); + return 0; +} + +int table_esgs_global_by_event_name::rnd_next(void) +{ + PFS_stage_class *stage_class; + + if (global_instr_class_stages_array == NULL) + return HA_ERR_END_OF_FILE; + + m_pos.set_at(&m_next_pos); + + stage_class= find_stage_class(m_pos.m_index); + if (stage_class) + { + make_row(stage_class); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int +table_esgs_global_by_event_name::rnd_pos(const void *pos) +{ + PFS_stage_class *stage_class; + + set_position(pos); + + if (global_instr_class_stages_array == NULL) + return HA_ERR_END_OF_FILE; + + stage_class=find_stage_class(m_pos.m_index); + if (stage_class) + { + make_row(stage_class); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + + +void table_esgs_global_by_event_name +::make_row(PFS_stage_class *klass) +{ + m_row.m_event_name.make_row(klass); + + PFS_connection_stage_visitor visitor(klass); + PFS_connection_iterator::visit_global(true, /* hosts */ + false, /* users */ + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + m_row.m_stat.set(m_normalizer, & visitor.m_stat); + m_row_exists= true; +} + +int table_esgs_global_by_event_name +::read_row_values(TABLE *table, unsigned char *, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 1, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 1, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_esgs_global_by_event_name.h b/storage/perfschema/table_esgs_global_by_event_name.h new file mode 100644 index 00000000..bfcd7017 --- /dev/null +++ b/storage/perfschema/table_esgs_global_by_event_name.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ESGS_GLOBAL_BY_EVENT_NAME_H +#define TABLE_ESGS_GLOBAL_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_esgs_global_by_event_name.h + Table EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME. +*/ +struct row_esgs_global_by_event_name +{ + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_stage_stat_row m_stat; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STAGES_SUMMARY_GLOBAL_BY_EVENT_NAME. */ +class table_esgs_global_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_esgs_global_by_event_name(); + +public: + ~table_esgs_global_by_event_name() = default; + +protected: + void make_row(PFS_stage_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_esgs_global_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_esms_by_account_by_event_name.cc b/storage/perfschema/table_esms_by_account_by_event_name.cc new file mode 100644 index 00000000..cb27e9db --- /dev/null +++ b/storage/perfschema/table_esms_by_account_by_event_name.cc @@ -0,0 +1,239 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_esms_by_account_by_event_name.cc + Table EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_esms_by_account_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_esms_by_account_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_esms_by_account_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_esms_by_account_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_statements_summary_by_account_by_event_name") }, + &pfs_truncatable_acl, + table_esms_by_account_by_event_name::create, + NULL, /* write_row */ + table_esms_by_account_by_event_name::delete_all_rows, + table_esms_by_account_by_event_name::get_row_count, + sizeof(pos_esms_by_account_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_statements_summary_by_account_by_event_name(" + "USER CHAR(" USERNAME_CHAR_LENGTH_STR ") collate utf8_bin default null comment 'User. Used together with HOST and EVENT_NAME for grouping events.'," + "HOST CHAR(" HOSTNAME_LENGTH_STR ") collate utf8_bin default null comment 'Host. Used together with USER and EVENT_NAME for grouping events.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name. Used together with USER and HOST for grouping events.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.'," + "SUM_LOCK_TIME BIGINT unsigned not null comment 'Sum of the LOCK_TIME column in the events_statements_current table.'," + "SUM_ERRORS BIGINT unsigned not null comment 'Sum of the ERRORS column in the events_statements_current table.'," + "SUM_WARNINGS BIGINT unsigned not null comment 'Sum of the WARNINGS column in the events_statements_current table.'," + "SUM_ROWS_AFFECTED BIGINT unsigned not null comment 'Sum of the ROWS_AFFECTED column in the events_statements_current table.'," + "SUM_ROWS_SENT BIGINT unsigned not null comment 'Sum of the ROWS_SENT column in the events_statements_current table.'," + "SUM_ROWS_EXAMINED BIGINT unsigned not null comment 'Sum of the ROWS_EXAMINED column in the events_statements_current table.'," + "SUM_CREATED_TMP_DISK_TABLES BIGINT unsigned not null comment 'Sum of the CREATED_TMP_DISK_TABLES column in the events_statements_current table.'," + "SUM_CREATED_TMP_TABLES BIGINT unsigned not null comment 'Sum of the CREATED_TMP_TABLES column in the events_statements_current table.'," + "SUM_SELECT_FULL_JOIN BIGINT unsigned not null comment 'Sum of the SELECT_FULL_JOIN column in the events_statements_current table.'," + "SUM_SELECT_FULL_RANGE_JOIN BIGINT unsigned not null comment 'Sum of the SELECT_FULL_RANGE_JOIN column in the events_statements_current table.'," + "SUM_SELECT_RANGE BIGINT unsigned not null comment 'Sum of the SELECT_RANGE column in the events_statements_current table.'," + "SUM_SELECT_RANGE_CHECK BIGINT unsigned not null comment 'Sum of the SELECT_RANGE_CHECK column in the events_statements_current table.'," + "SUM_SELECT_SCAN BIGINT unsigned not null comment 'Sum of the SELECT_SCAN column in the events_statements_current table.'," + "SUM_SORT_MERGE_PASSES BIGINT unsigned not null comment 'Sum of the SORT_MERGE_PASSES column in the events_statements_current table.'," + "SUM_SORT_RANGE BIGINT unsigned not null comment 'Sum of the SORT_RANGE column in the events_statements_current table.'," + "SUM_SORT_ROWS BIGINT unsigned not null comment 'Sum of the SORT_ROWS column in the events_statements_current table.'," + "SUM_SORT_SCAN BIGINT unsigned not null comment 'Sum of the SORT_SCAN column in the events_statements_current table.'," + "SUM_NO_INDEX_USED BIGINT unsigned not null comment 'Sum of the NO_INDEX_USED column in the events_statements_current table.'," + "SUM_NO_GOOD_INDEX_USED BIGINT unsigned not null comment 'Sum of the NO_GOOD_INDEX_USED column in the events_statements_current table.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_esms_by_account_by_event_name::create(void) +{ + return new table_esms_by_account_by_event_name(); +} + +int +table_esms_by_account_by_event_name::delete_all_rows(void) +{ + reset_events_statements_by_thread(); + reset_events_statements_by_account(); + return 0; +} + +ha_rows +table_esms_by_account_by_event_name::get_row_count(void) +{ + return global_account_container.get_row_count() * statement_class_max; +} + +table_esms_by_account_by_event_name::table_esms_by_account_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_esms_by_account_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_esms_by_account_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(statement_timer); + return 0; +} + +int table_esms_by_account_by_event_name::rnd_next(void) +{ + PFS_account *account; + PFS_statement_class *statement_class; + bool has_more_account= true; + + for (m_pos.set_at(&m_next_pos); + has_more_account; + m_pos.next_account()) + { + account= global_account_container.get(m_pos.m_index_1, & has_more_account); + if (account != NULL) + { + statement_class= find_statement_class(m_pos.m_index_2); + if (statement_class) + { + make_row(account, statement_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_esms_by_account_by_event_name::rnd_pos(const void *pos) +{ + PFS_account *account; + PFS_statement_class *statement_class; + + set_position(pos); + + account= global_account_container.get(m_pos.m_index_1); + if (account != NULL) + { + statement_class= find_statement_class(m_pos.m_index_2); + if (statement_class) + { + make_row(account, statement_class); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_esms_by_account_by_event_name +::make_row(PFS_account *account, PFS_statement_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + if (klass->is_mutable()) + return; + + account->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_account.make_row(account)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_statement_visitor visitor(klass); + PFS_connection_iterator::visit_account(account, + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! account->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(m_normalizer, & visitor.m_stat); +} + +int table_esms_by_account_by_event_name +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* USER */ + case 1: /* HOST */ + m_row.m_account.set_field(f->field_index, f); + break; + case 2: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 3, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 3, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_esms_by_account_by_event_name.h b/storage/perfschema/table_esms_by_account_by_event_name.h new file mode 100644 index 00000000..638cc28e --- /dev/null +++ b/storage/perfschema/table_esms_by_account_by_event_name.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ESMS_BY_ACCOUNT_BY_EVENT_NAME_H +#define TABLE_ESMS_BY_ACCOUNT_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_esms_by_account_by_event_name.h + Table EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_account.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. +*/ +struct row_esms_by_account_by_event_name +{ + /** Column USER, HOST. */ + PFS_account_row m_account; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_statement_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. + Index 1 on account (0 based) + Index 2 on statement class (1 based) +*/ +struct pos_esms_by_account_by_event_name +: public PFS_double_index +{ + pos_esms_by_account_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_account(void) + { + m_index_1++; + m_index_2= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. */ +class table_esms_by_account_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_esms_by_account_by_event_name(); + +public: + ~table_esms_by_account_by_event_name() = default; + +protected: + void make_row(PFS_account *account, PFS_statement_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_esms_by_account_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_esms_by_account_by_event_name m_pos; + /** Next position. */ + pos_esms_by_account_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_esms_by_digest.cc b/storage/perfschema/table_esms_by_digest.cc new file mode 100644 index 00000000..0dccaf1a --- /dev/null +++ b/storage/perfschema/table_esms_by_digest.cc @@ -0,0 +1,233 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_esms_by_digest.cc + Table EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_DIGEST (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_esms_by_digest.h" +#include "pfs_global.h" +#include "pfs_instr.h" +#include "pfs_timer.h" +#include "pfs_visitor.h" +#include "table_esms_by_digest.h" +#include "pfs_digest.h" +#include "field.h" + +THR_LOCK table_esms_by_digest::m_table_lock; + +PFS_engine_table_share_state +table_esms_by_digest::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_esms_by_digest::m_share= +{ + { C_STRING_WITH_LEN("events_statements_summary_by_digest") }, + &pfs_truncatable_acl, + table_esms_by_digest::create, + NULL, /* write_row */ + table_esms_by_digest::delete_all_rows, + table_esms_by_digest::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_statements_summary_by_digest(" + "SCHEMA_NAME VARCHAR(64) comment 'Database name. Records are summarised together with DIGEST.'," + "DIGEST VARCHAR(32) comment 'Performance Schema digest. Records are summarised together with SCHEMA NAME.'," + "DIGEST_TEXT LONGTEXT comment 'The unhashed form of the digest.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.'," + "SUM_LOCK_TIME BIGINT unsigned not null comment 'Sum of the LOCK_TIME column in the events_statements_current table.'," + "SUM_ERRORS BIGINT unsigned not null comment 'Sum of the ERRORS column in the events_statements_current table.'," + "SUM_WARNINGS BIGINT unsigned not null comment 'Sum of the WARNINGS column in the events_statements_current table.'," + "SUM_ROWS_AFFECTED BIGINT unsigned not null comment 'Sum of the ROWS_AFFECTED column in the events_statements_current table.'," + "SUM_ROWS_SENT BIGINT unsigned not null comment 'Sum of the ROWS_SENT column in the events_statements_current table.'," + "SUM_ROWS_EXAMINED BIGINT unsigned not null comment 'Sum of the ROWS_EXAMINED column in the events_statements_current table.'," + "SUM_CREATED_TMP_DISK_TABLES BIGINT unsigned not null comment 'Sum of the CREATED_TMP_DISK_TABLES column in the events_statements_current table.'," + "SUM_CREATED_TMP_TABLES BIGINT unsigned not null comment 'Sum of the CREATED_TMP_TABLES column in the events_statements_current table.'," + "SUM_SELECT_FULL_JOIN BIGINT unsigned not null comment 'Sum of the SELECT_FULL_JOIN column in the events_statements_current table.'," + "SUM_SELECT_FULL_RANGE_JOIN BIGINT unsigned not null comment 'Sum of the SELECT_FULL_RANGE_JOIN column in the events_statements_current table.'," + "SUM_SELECT_RANGE BIGINT unsigned not null comment 'Sum of the SELECT_RANGE column in the events_statements_current table.'," + "SUM_SELECT_RANGE_CHECK BIGINT unsigned not null comment 'Sum of the SELECT_RANGE_CHECK column in the events_statements_current table.'," + "SUM_SELECT_SCAN BIGINT unsigned not null comment 'Sum of the SELECT_SCAN column in the events_statements_current table.'," + "SUM_SORT_MERGE_PASSES BIGINT unsigned not null comment 'Sum of the SORT_MERGE_PASSES column in the events_statements_current table.'," + "SUM_SORT_RANGE BIGINT unsigned not null comment 'Sum of the SORT_RANGE column in the events_statements_current table.'," + "SUM_SORT_ROWS BIGINT unsigned not null comment 'Sum of the SORT_ROWS column in the events_statements_current table.'," + "SUM_SORT_SCAN BIGINT unsigned not null comment 'Sum of the SORT_SCAN column in the events_statements_current table.'," + "SUM_NO_INDEX_USED BIGINT unsigned not null comment 'Sum of the NO_INDEX_USED column in the events_statements_current table.'," + "SUM_NO_GOOD_INDEX_USED BIGINT unsigned not null comment 'Sum of the NO_GOOD_INDEX_USED column in the events_statements_current table.'," + "FIRST_SEEN TIMESTAMP(0) NOT NULL default 0 comment 'Time at which the digest was first seen.'," + "LAST_SEEN TIMESTAMP(0) NOT NULL default 0 comment 'Time at which the digest was most recently seen.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_esms_by_digest::create(void) +{ + return new table_esms_by_digest(); +} + +int +table_esms_by_digest::delete_all_rows(void) +{ + reset_esms_by_digest(); + return 0; +} + +ha_rows +table_esms_by_digest::get_row_count(void) +{ + return digest_max; +} + +table_esms_by_digest::table_esms_by_digest() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_esms_by_digest::reset_position(void) +{ + m_pos= 0; + m_next_pos= 0; +} + +int table_esms_by_digest::rnd_next(void) +{ + PFS_statements_digest_stat* digest_stat; + + if (statements_digest_stat_array == NULL) + return HA_ERR_END_OF_FILE; + + for (m_pos.set_at(&m_next_pos); + m_pos.m_index < digest_max; + m_pos.next()) + { + digest_stat= &statements_digest_stat_array[m_pos.m_index]; + if (digest_stat->m_lock.is_populated()) + { + if (digest_stat->m_first_seen != 0) + { + make_row(digest_stat); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_esms_by_digest::rnd_pos(const void *pos) +{ + PFS_statements_digest_stat* digest_stat; + + if (statements_digest_stat_array == NULL) + return HA_ERR_END_OF_FILE; + + set_position(pos); + digest_stat= &statements_digest_stat_array[m_pos.m_index]; + + if (digest_stat->m_lock.is_populated()) + { + if (digest_stat->m_first_seen != 0) + { + make_row(digest_stat); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + + +void table_esms_by_digest::make_row(PFS_statements_digest_stat* digest_stat) +{ + m_row_exists= false; + m_row.m_first_seen= digest_stat->m_first_seen; + m_row.m_last_seen= digest_stat->m_last_seen; + m_row.m_digest.make_row(digest_stat); + + /* + Get statements stats. + */ + time_normalizer *normalizer= time_normalizer::get(statement_timer); + m_row.m_stat.set(normalizer, & digest_stat->m_stat); + + m_row_exists= true; +} + +int table_esms_by_digest +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* + Set the null bits. It indicates how many fields could be null + in the table. + */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* SCHEMA_NAME */ + case 1: /* DIGEST */ + case 2: /* DIGEST_TEXT */ + m_row.m_digest.set_field(f->field_index, f); + break; + case 27: /* FIRST_SEEN */ + set_field_timestamp(f, m_row.m_first_seen); + break; + case 28: /* LAST_SEEN */ + set_field_timestamp(f, m_row.m_last_seen); + break; + default: /* 3, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 3, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_esms_by_digest.h b/storage/perfschema/table_esms_by_digest.h new file mode 100644 index 00000000..4711ffc5 --- /dev/null +++ b/storage/perfschema/table_esms_by_digest.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ESMS_BY_DIGEST_H +#define TABLE_ESMS_BY_DIGEST_H + +/** + @file storage/perfschema/table_esms_by_digest.h + Table EVENTS_STATEMENTS_SUMMARY_BY_DIGEST (declarations). +*/ + +#include "table_helper.h" +#include "pfs_digest.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_DIGEST. +*/ +struct row_esms_by_digest +{ + /** Columns DIGEST/DIGEST_TEXT. */ + PFS_digest_row m_digest; + + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_statement_stat_row m_stat; + + /** Column FIRST_SEEN. */ + ulonglong m_first_seen; + /** Column LAST_SEEN. */ + ulonglong m_last_seen; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_DIGEST. */ +class table_esms_by_digest : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_esms_by_digest(); + +public: + ~table_esms_by_digest() = default; + +protected: + void make_row(PFS_statements_digest_stat*); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_esms_by_digest m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_esms_by_host_by_event_name.cc b/storage/perfschema/table_esms_by_host_by_event_name.cc new file mode 100644 index 00000000..bd7a9f57 --- /dev/null +++ b/storage/perfschema/table_esms_by_host_by_event_name.cc @@ -0,0 +1,240 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_esms_by_host_by_event_name.cc + Table EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_esms_by_host_by_event_name.h" +#include "pfs_global.h" +#include "pfs_account.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_esms_by_host_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_esms_by_host_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_esms_by_host_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_statements_summary_by_host_by_event_name") }, + &pfs_truncatable_acl, + table_esms_by_host_by_event_name::create, + NULL, /* write_row */ + table_esms_by_host_by_event_name::delete_all_rows, + table_esms_by_host_by_event_name::get_row_count, + sizeof(pos_esms_by_host_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_statements_summary_by_host_by_event_name(" + "HOST CHAR(" STRINGIFY_ARG(HOSTNAME_LENGTH) ") collate utf8_bin default null comment 'Host. Used together with EVENT_NAME for grouping events.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name. Used together with HOST for grouping events.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.'," + "SUM_LOCK_TIME BIGINT unsigned not null comment 'Sum of the LOCK_TIME column in the events_statements_currentd table.'," + "SUM_ERRORS BIGINT unsigned not null comment 'Sum of the ERRORS column in the events_statements_current table.'," + "SUM_WARNINGS BIGINT unsigned not null comment 'Sum of the WARNINGS column in the events_statements_current table.'," + "SUM_ROWS_AFFECTED BIGINT unsigned not null comment 'Sum of the ROWS_AFFECTED column in the events_statements_current table.'," + "SUM_ROWS_SENT BIGINT unsigned not null comment 'Sum of the ROWS_SENT column in the events_statements_current table.'," + "SUM_ROWS_EXAMINED BIGINT unsigned not null comment 'Sum of the ROWS_EXAMINED column in the events_statements_current table.'," + "SUM_CREATED_TMP_DISK_TABLES BIGINT unsigned not null comment 'Sum of the CREATED_TMP_DISK_TABLES column in the events_statements_current table.'," + "SUM_CREATED_TMP_TABLES BIGINT unsigned not null comment 'Sum of the CREATED_TMP_TABLES column in the events_statements_current table.'," + "SUM_SELECT_FULL_JOIN BIGINT unsigned not null comment 'Sum of the SELECT_FULL_JOIN column in the events_statements_current table.'," + "SUM_SELECT_FULL_RANGE_JOIN BIGINT unsigned not null comment 'Sum of the SELECT_FULL_RANGE_JOINW column in the events_statements_current table.'," + "SUM_SELECT_RANGE BIGINT unsigned not null comment 'Sum of the SELECT_RANGE column in the events_statements_current table.'," + "SUM_SELECT_RANGE_CHECK BIGINT unsigned not null comment 'Sum of the SELECT_RANGE_CHECK column in the events_statements_current table.'," + "SUM_SELECT_SCAN BIGINT unsigned not null comment 'Sum of the SELECT_SCAN column in the events_statements_current table.'," + "SUM_SORT_MERGE_PASSES BIGINT unsigned not null comment 'Sum of the SORT_MERGE_PASSES column in the events_statements_current table.'," + "SUM_SORT_RANGE BIGINT unsigned not null comment 'Sum of the SORT_RANGE column in the events_statements_current table.'," + "SUM_SORT_ROWS BIGINT unsigned not null comment 'Sum of the SORT_ROWS column in the events_statements_current table.'," + "SUM_SORT_SCAN BIGINT unsigned not null comment 'Sum of the SORT_SCAN column in the events_statements_current table.'," + "SUM_NO_INDEX_USED BIGINT unsigned not null comment 'Sum of the NO_INDEX_USED column in the events_statements_current table.'," + "SUM_NO_GOOD_INDEX_USED BIGINT unsigned not null comment 'Sum of the NO_GOOD_INDEX_USED column in the events_statements_current table.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_esms_by_host_by_event_name::create(void) +{ + return new table_esms_by_host_by_event_name(); +} + +int +table_esms_by_host_by_event_name::delete_all_rows(void) +{ + reset_events_statements_by_thread(); + reset_events_statements_by_account(); + reset_events_statements_by_host(); + return 0; +} + +ha_rows +table_esms_by_host_by_event_name::get_row_count(void) +{ + return global_host_container.get_row_count() * statement_class_max; +} + +table_esms_by_host_by_event_name::table_esms_by_host_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_esms_by_host_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_esms_by_host_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(statement_timer); + return 0; +} + +int table_esms_by_host_by_event_name::rnd_next(void) +{ + PFS_host *host; + PFS_statement_class *statement_class; + bool has_more_host= true; + + for (m_pos.set_at(&m_next_pos); + has_more_host; + m_pos.next_host()) + { + host= global_host_container.get(m_pos.m_index_1, & has_more_host); + if (host != NULL) + { + statement_class= find_statement_class(m_pos.m_index_2); + if (statement_class) + { + make_row(host, statement_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_esms_by_host_by_event_name::rnd_pos(const void *pos) +{ + PFS_host *host; + PFS_statement_class *statement_class; + + set_position(pos); + + host= global_host_container.get(m_pos.m_index_1); + if (host != NULL) + { + statement_class= find_statement_class(m_pos.m_index_2); + if (statement_class) + { + make_row(host, statement_class); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_esms_by_host_by_event_name +::make_row(PFS_host *host, PFS_statement_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + if (klass->is_mutable()) + return; + + host->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_host.make_row(host)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_statement_visitor visitor(klass); + PFS_connection_iterator::visit_host(host, + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! host->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(m_normalizer, & visitor.m_stat); +} + +int table_esms_by_host_by_event_name +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* HOST */ + m_row.m_host.set_field(f); + break; + case 1: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 2, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_esms_by_host_by_event_name.h b/storage/perfschema/table_esms_by_host_by_event_name.h new file mode 100644 index 00000000..65a7256b --- /dev/null +++ b/storage/perfschema/table_esms_by_host_by_event_name.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ESMS_BY_HOST_BY_EVENT_NAME_H +#define TABLE_ESMS_BY_HOST_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_esms_by_host_by_event_name.h + Table EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_host.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME. +*/ +struct row_esms_by_host_by_event_name +{ + /** Column HOST */ + PFS_host_row m_host; + /** Column EVENT_NAME */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_statement_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME. + Index 1 on host (0 based) + Index 2 on statement class (1 based) +*/ +struct pos_esms_by_host_by_event_name +: public PFS_double_index +{ + pos_esms_by_host_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_host(void) + { + m_index_1++; + m_index_2= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME. */ +class table_esms_by_host_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_esms_by_host_by_event_name(); + +public: + ~table_esms_by_host_by_event_name() = default; + +protected: + void make_row(PFS_host *host, PFS_statement_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_esms_by_host_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_esms_by_host_by_event_name m_pos; + /** Next position. */ + pos_esms_by_host_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_esms_by_program.cc b/storage/perfschema/table_esms_by_program.cc new file mode 100644 index 00000000..e898d8ab --- /dev/null +++ b/storage/perfschema/table_esms_by_program.cc @@ -0,0 +1,252 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_esms_by_program.cc + Table EVENTS_STATEMENTS_SUMMARY_BY_PROGRAM (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "pfs_global.h" +#include "pfs_instr.h" +#include "pfs_timer.h" +#include "pfs_visitor.h" +#include "pfs_program.h" +#include "table_esms_by_program.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_esms_by_program::m_table_lock; + +PFS_engine_table_share_state +table_esms_by_program::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_esms_by_program::m_share= +{ + { C_STRING_WITH_LEN("events_statements_summary_by_program") }, + &pfs_truncatable_acl, + table_esms_by_program::create, + NULL, /* write_row */ + table_esms_by_program::delete_all_rows, + table_esms_by_program::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_statements_summary_by_program (" + "OBJECT_TYPE enum('EVENT', 'FUNCTION', 'PROCEDURE', 'TABLE', 'TRIGGER') comment 'Object type for which the summary is generated.'," + "OBJECT_SCHEMA varchar(64) NOT NULL comment 'The schema of the object for which the summary is generated.'," + "OBJECT_NAME varchar(64) NOT NULL comment 'The name of the object for which the summary is generated.'," + "COUNT_STAR bigint(20) unsigned NOT NULL comment 'The number of summarized events (from events_statements_current). This value includes all events, whether timed or nontimed.'," + "SUM_TIMER_WAIT bigint(20) unsigned NOT NULL comment 'The total wait time of the summarized timed events. This value is calculated only for timed events because nontimed events have a wait time of NULL. The same is true for the other xxx_TIMER_WAIT values.'," + "MIN_TIMER_WAIT bigint(20) unsigned NOT NULL comment 'The minimum wait time of the summarized timed events.'," + "AVG_TIMER_WAIT bigint(20) unsigned NOT NULL comment 'The average wait time of the summarized timed events.'," + "MAX_TIMER_WAIT bigint(20) unsigned NOT NULL comment 'The maximum wait time of the summarized timed events.'," + "COUNT_STATEMENTS bigint(20) unsigned NOT NULL comment 'Total number of nested statements invoked during stored program execution.'," + "SUM_STATEMENTS_WAIT bigint(20) unsigned NOT NULL comment 'The total wait time of the summarized timed statements. This value is calculated only for timed statements because nontimed statements have a wait time of NULL. The same is true for the other xxx_STATEMENT_WAIT values.'," + "MIN_STATEMENTS_WAIT bigint(20) unsigned NOT NULL comment 'The minimum wait time of the summarized timed statements.'," + "AVG_STATEMENTS_WAIT bigint(20) unsigned NOT NULL comment 'The average wait time of the summarized timed statements.'," + "MAX_STATEMENTS_WAIT bigint(20) unsigned NOT NULL comment 'The maximum wait time of the summarized timed statements.'," + "SUM_LOCK_TIME bigint(20) unsigned NOT NULL comment 'The total time spent (in picoseconds) waiting for table locks for the summarized statements.'," + "SUM_ERRORS bigint(20) unsigned NOT NULL comment 'The total number of errors that occurend for the summarized statements.'," + "SUM_WARNINGS bigint(20) unsigned NOT NULL comment 'The total number of warnings that occurend for the summarized statements.'," + "SUM_ROWS_AFFECTED bigint(20) unsigned NOT NULL comment 'The total number of affected rows by the summarized statements.'," + "SUM_ROWS_SENT bigint(20) unsigned NOT NULL comment 'The total number of rows returned by the summarized statements.'," + "SUM_ROWS_EXAMINED bigint(20) unsigned NOT NULL comment 'The total number of rows examined by the summarized statements.'," + "SUM_CREATED_TMP_DISK_TABLES bigint(20) unsigned NOT NULL comment 'The total number of on-disk temporary tables created by the summarized statements.'," + "SUM_CREATED_TMP_TABLES bigint(20) unsigned NOT NULL comment 'The total number of in-memory temporary tables created by the summarized statements.'," + "SUM_SELECT_FULL_JOIN bigint(20) unsigned NOT NULL comment 'The total number of full joins executed by the summarized statements.'," + "SUM_SELECT_FULL_RANGE_JOIN bigint(20) unsigned NOT NULL comment 'The total number of range search joins executed by the summarized statements.'," + "SUM_SELECT_RANGE bigint(20) unsigned NOT NULL comment 'The total number of joins that used ranges on the first table executed by the summarized statements.'," + "SUM_SELECT_RANGE_CHECK bigint(20) unsigned NOT NULL comment 'The total number of joins that check for key usage after each row executed by the summarized statements.'," + "SUM_SELECT_SCAN bigint(20) unsigned NOT NULL comment 'The total number of joins that did a full scan of the first table executed by the summarized statements.'," + "SUM_SORT_MERGE_PASSES bigint(20) unsigned NOT NULL comment 'The total number of merge passes that the sort algorithm has had to do for the summarized statements.'," + "SUM_SORT_RANGE bigint(20) unsigned NOT NULL comment 'The total number of sorts that were done using ranges for the summarized statements.'," + "SUM_SORT_ROWS bigint(20) unsigned NOT NULL comment 'The total number of sorted rows that were sorted by the summarized statements.'," + "SUM_SORT_SCAN bigint(20) unsigned NOT NULL comment 'The total number of sorts that were done by scanning the table by the summarized statements.'," + "SUM_NO_INDEX_USED bigint(20) unsigned NOT NULL comment 'The total number of statements that performed a table scan without using an index.'," + "SUM_NO_GOOD_INDEX_USED bigint(20) unsigned NOT NULL comment 'The total number of statements where no good index was found.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_esms_by_program::create(void) +{ + return new table_esms_by_program(); +} + +int +table_esms_by_program::delete_all_rows(void) +{ + reset_esms_by_program(); + return 0; +} + +ha_rows +table_esms_by_program::get_row_count(void) +{ + return global_program_container.get_row_count(); +} + +table_esms_by_program::table_esms_by_program() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_esms_by_program::reset_position(void) +{ + m_pos= 0; + m_next_pos= 0; +} + +int table_esms_by_program::rnd_next(void) +{ + PFS_program* pfs; + + m_pos.set_at(&m_next_pos); + PFS_program_iterator it= global_program_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int +table_esms_by_program::rnd_pos(const void *pos) +{ + PFS_program* pfs; + + set_position(pos); + + pfs= global_program_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + + +void table_esms_by_program::make_row(PFS_program* program) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + program->m_lock.begin_optimistic_lock(&lock); + + m_row.m_object_type= program->m_type; + + m_row.m_object_name_length= program->m_object_name_length; + if(m_row.m_object_name_length > 0) + memcpy(m_row.m_object_name, program->m_object_name, + m_row.m_object_name_length); + + m_row.m_schema_name_length= program->m_schema_name_length; + if(m_row.m_schema_name_length > 0) + memcpy(m_row.m_schema_name, program->m_schema_name, + m_row.m_schema_name_length); + + time_normalizer *normalizer= time_normalizer::get(statement_timer); + /* Get stored program's over all stats. */ + m_row.m_sp_stat.set(normalizer, &program->m_sp_stat); + /* Get sub statements' stats. */ + m_row.m_stmt_stat.set(normalizer, & program->m_stmt_stat); + + if (! program->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; +} + +int table_esms_by_program +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* + Set the null bits. It indicates how many fields could be null + in the table. + */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* OBJECT_TYPE */ + if(m_row.m_object_type != 0) + set_field_enum(f, m_row.m_object_type); + else + f->set_null(); + break; + case 1: /* OBJECT_SCHEMA */ + if(m_row.m_schema_name_length > 0) + set_field_varchar_utf8(f, m_row.m_schema_name, + m_row.m_schema_name_length); + else + f->set_null(); + break; + case 2: /* 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 3: /* COUNT_STAR */ + case 4: /* SUM_TIMER_WAIT */ + case 5: /* MIN_TIMER_WAIT */ + case 6: /* AVG_TIMER_WAIT */ + case 7: /* MAX_TIMER_WAIT */ + m_row.m_sp_stat.set_field(f->field_index - 3, f); + break; + default: /* 8, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stmt_stat.set_field(f->field_index - 8, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_esms_by_program.h b/storage/perfschema/table_esms_by_program.h new file mode 100644 index 00000000..f9e686ef --- /dev/null +++ b/storage/perfschema/table_esms_by_program.h @@ -0,0 +1,114 @@ +/* Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ESMS_BY_PROGRAM_H +#define TABLE_ESMS_BY_PROGRAM_H + +/** + @file storage/perfschema/table_esms_by_program.h + Table EVENTS_STATEMENTS_SUMMARY_BY_PROGRAM (declarations). +*/ + +#include "table_helper.h" +#include "pfs_program.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_PROGRAM. +*/ +struct row_esms_by_program +{ + /** Column OBJECT_TYPE. */ + enum_object_type m_object_type; + /** Column OBJECT_SCHEMA. */ + char m_schema_name[COL_OBJECT_SCHEMA_SIZE]; + int m_schema_name_length; + /** Column OBJECT_NAME. */ + char m_object_name[COL_OBJECT_NAME_SIZE]; + int m_object_name_length; + + /** + Columns COUNT_STAR + SUM_TIMER_WAIT + MIN_TIMER_WAIT + AVG_TIMER_WAIT + MAX_TIMER_WAIT + */ + PFS_sp_stat_row m_sp_stat; + + /** Columns COUNT_STATEMENTS,SUM_STATEMENTS_WAIT...SUM_NO_GOOD_INDEX_USED. */ + PFS_statement_stat_row m_stmt_stat; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_PROGRAM. */ +class table_esms_by_program : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_esms_by_program(); + +public: + ~table_esms_by_program() + {} + +protected: + void make_row(PFS_program*); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_esms_by_program m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_esms_by_thread_by_event_name.cc b/storage/perfschema/table_esms_by_thread_by_event_name.cc new file mode 100644 index 00000000..08d24436 --- /dev/null +++ b/storage/perfschema/table_esms_by_thread_by_event_name.cc @@ -0,0 +1,232 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_esms_by_thread_by_event_name.cc + Table EVENTS_STATEMENTS_SUMMARY_BY_HOST_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_esms_by_thread_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_esms_by_thread_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_esms_by_thread_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_esms_by_thread_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_statements_summary_by_thread_by_event_name") }, + &pfs_truncatable_acl, + table_esms_by_thread_by_event_name::create, + NULL, /* write_row */ + table_esms_by_thread_by_event_name::delete_all_rows, + table_esms_by_thread_by_event_name::get_row_count, + sizeof(pos_esms_by_thread_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_statements_summary_by_thread_by_event_name(" + "THREAD_ID BIGINT unsigned not null comment 'Thread associated with the event. Together with EVENT_NAME uniquely identifies the row.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name. Used together with THREAD_ID for grouping events.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.'," + "SUM_LOCK_TIME BIGINT unsigned not null comment 'Sum of the LOCK_TIME column in the events_statements_current table.'," + "SUM_ERRORS BIGINT unsigned not null comment 'Sum of the ERRORS column in the events_statements_current table.'," + "SUM_WARNINGS BIGINT unsigned not null comment 'Sum of the WARNINGS column in the events_statements_current table.'," + "SUM_ROWS_AFFECTED BIGINT unsigned not null comment 'Sum of the ROWS_AFFECTED column in the events_statements_current table.'," + "SUM_ROWS_SENT BIGINT unsigned not null comment 'Sum of the ROWS_SENT column in the events_statements_current table.'," + "SUM_ROWS_EXAMINED BIGINT unsigned not null comment 'Sum of the ROWS_EXAMINED column in the events_statements_current table.'," + "SUM_CREATED_TMP_DISK_TABLES BIGINT unsigned not null comment 'Sum of the CREATED_TMP_DISK_TABLES column in the events_statements_current table.'," + "SUM_CREATED_TMP_TABLES BIGINT unsigned not null comment 'Sum of the CREATED_TMP_TABLES column in the events_statements_current table.'," + "SUM_SELECT_FULL_JOIN BIGINT unsigned not null comment 'Sum of the SELECT_FULL_JOIN column in the events_statements_current table.'," + "SUM_SELECT_FULL_RANGE_JOIN BIGINT unsigned not null comment 'Sum of the SELECT_FULL_RANGE_JOIN column in the events_statements_current table.'," + "SUM_SELECT_RANGE BIGINT unsigned not null comment 'Sum of the SELECT_RANGE column in the events_statements_current table.'," + "SUM_SELECT_RANGE_CHECK BIGINT unsigned not null comment 'Sum of the SELECT_RANGE_CHECK column in the events_statements_current table.'," + "SUM_SELECT_SCAN BIGINT unsigned not null comment 'Sum of the SELECT_SCAN column in the events_statements_current table.'," + "SUM_SORT_MERGE_PASSES BIGINT unsigned not null comment 'Sum of the SORT_MERGE_PASSES column in the events_statements_current table.'," + "SUM_SORT_RANGE BIGINT unsigned not null comment 'Sum of the SORT_RANGE column in the events_statements_current table.'," + "SUM_SORT_ROWS BIGINT unsigned not null comment 'Sum of the SORT_ROWS column in the events_statements_current table.'," + "SUM_SORT_SCAN BIGINT unsigned not null comment 'Sum of the SORT_SCAN column in the events_statements_current table.'," + "SUM_NO_INDEX_USED BIGINT unsigned not null comment 'Sum of the NO_INDEX_USED column in the events_statements_current table.'," + "SUM_NO_GOOD_INDEX_USED BIGINT unsigned not null comment 'Sum of the NO_GOOD_INDEX_USED column in the events_statements_current table.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_esms_by_thread_by_event_name::create(void) +{ + return new table_esms_by_thread_by_event_name(); +} + +int +table_esms_by_thread_by_event_name::delete_all_rows(void) +{ + reset_events_statements_by_thread(); + return 0; +} + +ha_rows +table_esms_by_thread_by_event_name::get_row_count(void) +{ + return global_thread_container.get_row_count() * statement_class_max; +} + +table_esms_by_thread_by_event_name::table_esms_by_thread_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_esms_by_thread_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_esms_by_thread_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(statement_timer); + return 0; +} + +int table_esms_by_thread_by_event_name::rnd_next(void) +{ + PFS_thread *thread; + PFS_statement_class *statement_class; + bool has_more_thread= true; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread); + if (thread != NULL) + { + statement_class= find_statement_class(m_pos.m_index_2); + if (statement_class) + { + make_row(thread, statement_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_esms_by_thread_by_event_name::rnd_pos(const void *pos) +{ + PFS_thread *thread; + PFS_statement_class *statement_class; + + set_position(pos); + + thread= global_thread_container.get(m_pos.m_index_1); + if (thread != NULL) + { + statement_class= find_statement_class(m_pos.m_index_2); + if (statement_class) + { + make_row(thread, statement_class); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_esms_by_thread_by_event_name +::make_row(PFS_thread *thread, PFS_statement_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + if (klass->is_mutable()) + return; + + /* Protect this reader against a thread termination */ + thread->m_lock.begin_optimistic_lock(&lock); + + m_row.m_thread_internal_id= thread->m_thread_internal_id; + + m_row.m_event_name.make_row(klass); + + PFS_connection_statement_visitor visitor(klass); + PFS_connection_iterator::visit_thread(thread, & visitor); + + if (! thread->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(m_normalizer, & visitor.m_stat); +} + +int table_esms_by_thread_by_event_name +::read_row_values(TABLE *table, unsigned char *, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + 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_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 2, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_esms_by_thread_by_event_name.h b/storage/perfschema/table_esms_by_thread_by_event_name.h new file mode 100644 index 00000000..87a4e3c3 --- /dev/null +++ b/storage/perfschema/table_esms_by_thread_by_event_name.h @@ -0,0 +1,132 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ESMS_BY_THREAD_BY_EVENT_NAME_H +#define TABLE_ESMS_BY_THREAD_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_esms_by_thread_by_event_name.h + Table EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME. +*/ +struct row_esms_by_thread_by_event_name +{ + /** Column THREAD_ID. */ + ulonglong m_thread_internal_id; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_statement_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME. + Index 1 on thread (0 based). + Index 2 on statement class (1 based). +*/ +struct pos_esms_by_thread_by_event_name +: public PFS_double_index, public PFS_instrument_view_constants +{ + pos_esms_by_thread_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= 1; + } + + inline void next_statement(void) + { + m_index_2++; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_THREAD_BY_EVENT_NAME. */ +class table_esms_by_thread_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_esms_by_thread_by_event_name(); + +public: + ~table_esms_by_thread_by_event_name() = default; + +protected: + void make_row(PFS_thread *thread, PFS_statement_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_esms_by_thread_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_esms_by_thread_by_event_name m_pos; + /** Next position. */ + pos_esms_by_thread_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_esms_by_user_by_event_name.cc b/storage/perfschema/table_esms_by_user_by_event_name.cc new file mode 100644 index 00000000..4be0f09f --- /dev/null +++ b/storage/perfschema/table_esms_by_user_by_event_name.cc @@ -0,0 +1,239 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_esms_by_user_by_event_name.cc + Table EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_esms_by_user_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_esms_by_user_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_esms_by_user_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_esms_by_user_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_statements_summary_by_user_by_event_name") }, + &pfs_truncatable_acl, + table_esms_by_user_by_event_name::create, + NULL, /* write_row */ + table_esms_by_user_by_event_name::delete_all_rows, + table_esms_by_user_by_event_name::get_row_count, + sizeof(pos_esms_by_user_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_statements_summary_by_user_by_event_name(" + "USER CHAR(" USERNAME_CHAR_LENGTH_STR ") collate utf8_bin default null comment 'User. Used together with EVENT_NAME for grouping events.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name. Used together with USER for grouping events.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.'," + "SUM_LOCK_TIME BIGINT unsigned not null comment 'Sum of the LOCK_TIME column in the events_statements_current table.'," + "SUM_ERRORS BIGINT unsigned not null comment 'Sum of the ERRORS column in the events_statements_current table.'," + "SUM_WARNINGS BIGINT unsigned not null comment 'Sum of the WARNINGS column in the events_statements_current table.'," + "SUM_ROWS_AFFECTED BIGINT unsigned not null comment 'Sum of the ROWS_AFFECTED column in the events_statements_current table.'," + "SUM_ROWS_SENT BIGINT unsigned not null comment 'Sum of the ROWS_SENT column in the events_statements_current table.'," + "SUM_ROWS_EXAMINED BIGINT unsigned not null comment 'Sum of the ROWS_EXAMINED column in the events_statements_current table.'," + "SUM_CREATED_TMP_DISK_TABLES BIGINT unsigned not null comment 'Sum of the CREATED_TMP_DISK_TABLES column in the events_statements_current table.'," + "SUM_CREATED_TMP_TABLES BIGINT unsigned not null comment 'Sum of the CREATED_TMP_TABLES column in the events_statements_current table.'," + "SUM_SELECT_FULL_JOIN BIGINT unsigned not null comment 'Sum of the SELECT_FULL_JOIN column in the events_statements_current table.'," + "SUM_SELECT_FULL_RANGE_JOIN BIGINT unsigned not null comment 'Sum of the SELECT_FULL_RANGE_JOIN column in the events_statements_current table.'," + "SUM_SELECT_RANGE BIGINT unsigned not null comment 'Sum of the SELECT_RANGE column in the events_statements_current table.'," + "SUM_SELECT_RANGE_CHECK BIGINT unsigned not null comment 'Sum of the SELECT_RANGE_CHECK column in the events_statements_current table.'," + "SUM_SELECT_SCAN BIGINT unsigned not null comment 'Sum of the SELECT_SCAN column in the events_statements_current table.'," + "SUM_SORT_MERGE_PASSES BIGINT unsigned not null comment 'Sum of the SORT_MERGE_PASSES column in the events_statements_current table.'," + "SUM_SORT_RANGE BIGINT unsigned not null comment 'Sum of the SORT_RANGE column in the events_statements_current table.'," + "SUM_SORT_ROWS BIGINT unsigned not null comment 'Sum of the SORT_ROWS column in the events_statements_current table.'," + "SUM_SORT_SCAN BIGINT unsigned not null comment 'Sum of the SORT_SCAN column in the events_statements_current table.'," + "SUM_NO_INDEX_USED BIGINT unsigned not null comment 'Sum of the NO_INDEX_USED column in the events_statements_current table.'," + "SUM_NO_GOOD_INDEX_USED BIGINT unsigned not null comment 'Sum of the NO_GOOD_INDEX_USED column in the events_statements_current table.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_esms_by_user_by_event_name::create(void) +{ + return new table_esms_by_user_by_event_name(); +} + +int +table_esms_by_user_by_event_name::delete_all_rows(void) +{ + reset_events_statements_by_thread(); + reset_events_statements_by_account(); + reset_events_statements_by_user(); + return 0; +} + +ha_rows +table_esms_by_user_by_event_name::get_row_count(void) +{ + return global_user_container.get_row_count() * statement_class_max; +} + +table_esms_by_user_by_event_name::table_esms_by_user_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_esms_by_user_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_esms_by_user_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(statement_timer); + return 0; +} + +int table_esms_by_user_by_event_name::rnd_next(void) +{ + PFS_user *user; + PFS_statement_class *statement_class; + bool has_more_user= true; + + for (m_pos.set_at(&m_next_pos); + has_more_user; + m_pos.next_user()) + { + user= global_user_container.get(m_pos.m_index_1, & has_more_user); + if (user != NULL) + { + statement_class= find_statement_class(m_pos.m_index_2); + if (statement_class) + { + make_row(user, statement_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_esms_by_user_by_event_name::rnd_pos(const void *pos) +{ + PFS_user *user; + PFS_statement_class *statement_class; + + set_position(pos); + + user= global_user_container.get(m_pos.m_index_1); + if (user != NULL) + { + statement_class= find_statement_class(m_pos.m_index_2); + if (statement_class) + { + make_row(user, statement_class); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_esms_by_user_by_event_name +::make_row(PFS_user *user, PFS_statement_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + if (klass->is_mutable()) + return; + + user->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_user.make_row(user)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_statement_visitor visitor(klass); + PFS_connection_iterator::visit_user(user, + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! user->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(m_normalizer, & visitor.m_stat); +} + +int table_esms_by_user_by_event_name +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* USER */ + m_row.m_user.set_field(f); + break; + case 1: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 2, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_esms_by_user_by_event_name.h b/storage/perfschema/table_esms_by_user_by_event_name.h new file mode 100644 index 00000000..cbd388ef --- /dev/null +++ b/storage/perfschema/table_esms_by_user_by_event_name.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ESMS_BY_USER_BY_EVENT_NAME_H +#define TABLE_ESMS_BY_USER_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_esms_by_user_by_event_name.h + Table EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_user.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME. +*/ +struct row_esms_by_user_by_event_name +{ + /** Column USER. */ + PFS_user_row m_user; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_statement_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME. + Index 1 on user (0 based) + Index 2 on statement class (1 based) +*/ +struct pos_esms_by_user_by_event_name +: public PFS_double_index +{ + pos_esms_by_user_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_user(void) + { + m_index_1++; + m_index_2= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_USER_BY_EVENT_NAME. */ +class table_esms_by_user_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_esms_by_user_by_event_name(); + +public: + ~table_esms_by_user_by_event_name() = default; + +protected: + void make_row(PFS_user *user, PFS_statement_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_esms_by_user_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_esms_by_user_by_event_name m_pos; + /** Next position. */ + pos_esms_by_user_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_esms_global_by_event_name.cc b/storage/perfschema/table_esms_global_by_event_name.cc new file mode 100644 index 00000000..0fe61e2c --- /dev/null +++ b/storage/perfschema/table_esms_global_by_event_name.cc @@ -0,0 +1,222 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_esms_global_by_event_name.cc + Table EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_esms_global_by_event_name.h" +#include "pfs_global.h" +#include "pfs_instr.h" +#include "pfs_timer.h" +#include "pfs_visitor.h" +#include "field.h" + +THR_LOCK table_esms_global_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_esms_global_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_esms_global_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_statements_summary_global_by_event_name") }, + &pfs_truncatable_acl, + table_esms_global_by_event_name::create, + NULL, /* write_row */ + table_esms_global_by_event_name::delete_all_rows, + table_esms_global_by_event_name::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_statements_summary_global_by_event_name(" + "EVENT_NAME VARCHAR(128) not null comment 'Event name.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.'," + "SUM_LOCK_TIME BIGINT unsigned not null comment 'Sum of the LOCK_TIME column in the events_statements_current table.'," + "SUM_ERRORS BIGINT unsigned not null comment 'Sum of the ERRORS column in the events_statements_current table.'," + "SUM_WARNINGS BIGINT unsigned not null comment 'Sum of the WARNINGS column in the events_statements_current table.'," + "SUM_ROWS_AFFECTED BIGINT unsigned not null comment 'Sum of the ROWS_AFFECTED column in the events_statements_current table.'," + "SUM_ROWS_SENT BIGINT unsigned not null comment 'Sum of the ROWS_SENT column in the events_statements_current table.'," + "SUM_ROWS_EXAMINED BIGINT unsigned not null comment 'Sum of the ROWS_EXAMINED column in the events_statements_current table.'," + "SUM_CREATED_TMP_DISK_TABLES BIGINT unsigned not null comment 'Sum of the CREATED_TMP_DISK_TABLES column in the events_statements_current table.'," + "SUM_CREATED_TMP_TABLES BIGINT unsigned not null comment 'Sum of the CREATED_TMP_TABLES column in the events_statements_current table.'," + "SUM_SELECT_FULL_JOIN BIGINT unsigned not null comment 'Sum of the SELECT_FULL_JOIN column in the events_statements_current table.'," + "SUM_SELECT_FULL_RANGE_JOIN BIGINT unsigned not null comment 'Sum of the SELECT_FULL_RANGE_JOIN column in the events_statements_current table.'," + "SUM_SELECT_RANGE BIGINT unsigned not null comment 'Sum of the SELECT_RANGE column in the events_statements_current table.'," + "SUM_SELECT_RANGE_CHECK BIGINT unsigned not null comment 'Sum of the SELECT_RANGE_CHECK column in the events_statements_current table.'," + "SUM_SELECT_SCAN BIGINT unsigned not null comment 'Sum of the SELECT_SCAN column in the events_statements_current table.'," + "SUM_SORT_MERGE_PASSES BIGINT unsigned not null comment 'Sum of the SORT_MERGE_PASSES column in the events_statements_current table.'," + "SUM_SORT_RANGE BIGINT unsigned not null comment 'Sum of the SORT_RANGE column in the events_statements_current table.'," + "SUM_SORT_ROWS BIGINT unsigned not null comment 'Sum of the SORT_ROWS column in the events_statements_current table.'," + "SUM_SORT_SCAN BIGINT unsigned not null comment 'Sum of the SORT_SCAN column in the events_statements_current table.'," + "SUM_NO_INDEX_USED BIGINT unsigned not null comment 'Sum of the NO_INDEX_USED column in the events_statements_current table.'," + "SUM_NO_GOOD_INDEX_USED BIGINT unsigned not null comment 'Sum of the NO_GOOD_INDEX_USED column in the events_statements_current table.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_esms_global_by_event_name::create(void) +{ + return new table_esms_global_by_event_name(); +} + +int +table_esms_global_by_event_name::delete_all_rows(void) +{ + reset_events_statements_by_thread(); + reset_events_statements_by_account(); + reset_events_statements_by_user(); + reset_events_statements_by_host(); + reset_events_statements_global(); + return 0; +} + +ha_rows +table_esms_global_by_event_name::get_row_count(void) +{ + return statement_class_max; +} + +table_esms_global_by_event_name::table_esms_global_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(1), m_next_pos(1) +{} + +void table_esms_global_by_event_name::reset_position(void) +{ + m_pos= 1; + m_next_pos= 1; +} + +int table_esms_global_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(statement_timer); + return 0; +} + +int table_esms_global_by_event_name::rnd_next(void) +{ + PFS_statement_class *statement_class; + + if (global_instr_class_statements_array == NULL) + return HA_ERR_END_OF_FILE; + + m_pos.set_at(&m_next_pos); + + statement_class= find_statement_class(m_pos.m_index); + if (statement_class) + { + make_row(statement_class); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int +table_esms_global_by_event_name::rnd_pos(const void *pos) +{ + PFS_statement_class *statement_class; + + set_position(pos); + + if (global_instr_class_statements_array == NULL) + return HA_ERR_END_OF_FILE; + + statement_class=find_statement_class(m_pos.m_index); + if (statement_class) + { + make_row(statement_class); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + + +void table_esms_global_by_event_name +::make_row(PFS_statement_class *klass) +{ + m_row_exists= false; + + if (klass->is_mutable()) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_statement_visitor visitor(klass); + PFS_connection_iterator::visit_global(true, /* hosts */ + false, /* users */ + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + m_row.m_stat.set(m_normalizer, & visitor.m_stat); + m_row_exists= true; +} + +int table_esms_global_by_event_name +::read_row_values(TABLE *table, unsigned char *, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 1, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 1, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_esms_global_by_event_name.h b/storage/perfschema/table_esms_global_by_event_name.h new file mode 100644 index 00000000..21a6b1a1 --- /dev/null +++ b/storage/perfschema/table_esms_global_by_event_name.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ESMS_GLOBAL_BY_EVENT_NAME_H +#define TABLE_ESMS_GLOBAL_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_esms_global_by_event_name.h + Table EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME. +*/ +struct row_esms_global_by_event_name +{ + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_statement_stat_row m_stat; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_GLOBAL_BY_EVENT_NAME. */ +class table_esms_global_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_esms_global_by_event_name(); + +public: + ~table_esms_global_by_event_name() = default; + +protected: + void make_row(PFS_statement_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_esms_global_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_ets_by_account_by_event_name.cc b/storage/perfschema/table_ets_by_account_by_event_name.cc new file mode 100644 index 00000000..f2eef2ef --- /dev/null +++ b/storage/perfschema/table_ets_by_account_by_event_name.cc @@ -0,0 +1,232 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_ets_by_account_by_event_name.cc + Table EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_ets_by_account_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_ets_by_account_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_ets_by_account_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_ets_by_account_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_transactions_summary_by_account_by_event_name") }, + &pfs_truncatable_acl, + table_ets_by_account_by_event_name::create, + NULL, /* write_row */ + table_ets_by_account_by_event_name::delete_all_rows, + table_ets_by_account_by_event_name::get_row_count, + sizeof(pos_ets_by_account_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_transactions_summary_by_account_by_event_name(" + "USER CHAR(32) collate utf8_bin default null comment 'User for which summary is generated.'," + "HOST CHAR(60) collate utf8_bin default null comment 'Host for which summary is generated.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name for which summary is generated.'," + "COUNT_STAR BIGINT unsigned not null comment 'The number of summarized events. This value includes all events, whether timed or nontimed.'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'The total wait time of the summarized timed events. This value is calculated only for timed events because nontimed events have a wait time of NULL. The same is true for the other xxx_TIMER_WAIT values.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'The minimum wait time of the summarized timed events.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'The average wait time of the summarized timed events.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'The maximum wait time of the summarized timed events.'," + "COUNT_READ_WRITE BIGINT unsigned not null comment 'The total number of only READ/WRITE transaction events.'," + "SUM_TIMER_READ_WRITE BIGINT unsigned not null comment 'The total wait time of only READ/WRITE transaction events.'," + "MIN_TIMER_READ_WRITE BIGINT unsigned not null comment 'The minimum wait time of only READ/WRITE transaction events.'," + "AVG_TIMER_READ_WRITE BIGINT unsigned not null comment 'The average wait time of only READ/WRITE transaction events.'," + "MAX_TIMER_READ_WRITE BIGINT unsigned not null comment 'The maximum wait time of only READ/WRITE transaction events.'," + "COUNT_READ_ONLY BIGINT unsigned not null comment 'The total number of only READ ONLY transaction events.'," + "SUM_TIMER_READ_ONLY BIGINT unsigned not null comment 'The total wait time of only READ ONLY transaction events.'," + "MIN_TIMER_READ_ONLY BIGINT unsigned not null comment 'The minimum wait time of only READ ONLY transaction events.'," + "AVG_TIMER_READ_ONLY BIGINT unsigned not null comment 'The average wait time of only READ ONLY transaction events.'," + "MAX_TIMER_READ_ONLY BIGINT unsigned not null comment 'The maximum wait time of only READ ONLY transaction events.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_ets_by_account_by_event_name::create(void) +{ + return new table_ets_by_account_by_event_name(); +} + +int +table_ets_by_account_by_event_name::delete_all_rows(void) +{ + reset_events_transactions_by_thread(); + reset_events_transactions_by_account(); + return 0; +} + +ha_rows +table_ets_by_account_by_event_name::get_row_count(void) +{ + return global_account_container.get_row_count() * transaction_class_max; +} + +table_ets_by_account_by_event_name::table_ets_by_account_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_ets_by_account_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_ets_by_account_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(transaction_timer); + return 0; +} + +int table_ets_by_account_by_event_name::rnd_next(void) +{ + PFS_account *account; + PFS_transaction_class *transaction_class; + bool has_more_account= true; + + for (m_pos.set_at(&m_next_pos); + has_more_account; + m_pos.next_account()) + { + account= global_account_container.get(m_pos.m_index_1, & has_more_account); + if (account != NULL) + { + transaction_class= find_transaction_class(m_pos.m_index_2); + if (transaction_class) + { + make_row(account, transaction_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_ets_by_account_by_event_name::rnd_pos(const void *pos) +{ + PFS_account *account; + PFS_transaction_class *transaction_class; + + set_position(pos); + + account= global_account_container.get(m_pos.m_index_1); + if (account != NULL) + { + transaction_class= find_transaction_class(m_pos.m_index_2); + if (transaction_class) + { + make_row(account, transaction_class); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_ets_by_account_by_event_name +::make_row(PFS_account *account, PFS_transaction_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + account->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_account.make_row(account)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_transaction_visitor visitor(klass); + PFS_connection_iterator::visit_account(account, + true, /* threads */ + false, /* THDs */ + &visitor); + + if (! account->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(m_normalizer, &visitor.m_stat); +} + +int table_ets_by_account_by_event_name +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* USER */ + case 1: /* HOST */ + m_row.m_account.set_field(f->field_index, f); + break; + case 2: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: + /** + COUNT_STAR, SUM/MIN/AVG/MAX_TIMER_WAIT, + COUNT_READ_WRITE, SUM/MIN/AVG/MAX_TIMER_READ_WRITE, + COUNT_READ_ONLY, SUM/MIN/AVG/MAX_TIMER_READ_ONLY + */ + m_row.m_stat.set_field(f->field_index-3, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_ets_by_account_by_event_name.h b/storage/perfschema/table_ets_by_account_by_event_name.h new file mode 100644 index 00000000..a5202b29 --- /dev/null +++ b/storage/perfschema/table_ets_by_account_by_event_name.h @@ -0,0 +1,135 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ETS_BY_ACCOUNT_BY_EVENT_NAME_H +#define TABLE_ETS_BY_ACCOUNT_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_ets_by_account_by_event_name.h + Table EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_account.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. +*/ +struct row_ets_by_account_by_event_name +{ + /** Columns USER, HOST. */ + PFS_account_row m_account; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** + Columns COUNT_STAR, SUM/MIN/AVG/MAX_TIMER_WAIT, + COUNT_READ_WRITE, SUM/MIN/AVG/MAX_TIMER_READ_WRITE, + COUNT_READ_ONLY, SUM/MIN/AVG/MAX_TIMER_READ_ONLY + */ + PFS_transaction_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. + Index 1 on account (0 based) + Index 2 on transaction class (1 based) +*/ +struct pos_ets_by_account_by_event_name +: public PFS_double_index +{ + pos_ets_by_account_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_account(void) + { + m_index_1++; + m_index_2= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. */ +class table_ets_by_account_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_ets_by_account_by_event_name(); + +public: + ~table_ets_by_account_by_event_name() + {} + +protected: + void make_row(PFS_account *account, PFS_transaction_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_ets_by_account_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_ets_by_account_by_event_name m_pos; + /** Next position. */ + pos_ets_by_account_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_ets_by_host_by_event_name.cc b/storage/perfschema/table_ets_by_host_by_event_name.cc new file mode 100644 index 00000000..cbdddbaa --- /dev/null +++ b/storage/perfschema/table_ets_by_host_by_event_name.cc @@ -0,0 +1,233 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_ets_by_host_by_event_name.cc + Table EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_ets_by_host_by_event_name.h" +#include "pfs_global.h" +#include "pfs_account.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_ets_by_host_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_ets_by_host_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_ets_by_host_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_transactions_summary_by_host_by_event_name") }, + &pfs_truncatable_acl, + table_ets_by_host_by_event_name::create, + NULL, /* write_row */ + table_ets_by_host_by_event_name::delete_all_rows, + table_ets_by_host_by_event_name::get_row_count, + sizeof(pos_ets_by_host_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_transactions_summary_by_host_by_event_name(" + "HOST CHAR(60) collate utf8_bin default null comment 'Host for which summary is generated.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name for which summary is generated.'," + "COUNT_STAR BIGINT unsigned not null comment 'The number of summarized events. This value includes all events, whether timed or nontimed.'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'The total wait time of the summarized timed events. This value is calculated only for timed events because nontimed events have a wait time of NULL. The same is true for the other xxx_TIMER_WAIT values.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'The minimum wait time of the summarized timed events.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'The average wait time of the summarized timed events.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'The maximum wait time of the summarized timed events.'," + "COUNT_READ_WRITE BIGINT unsigned not null comment 'The total number of only READ/WRITE transaction events.'," + "SUM_TIMER_READ_WRITE BIGINT unsigned not null comment 'The total wait time of only READ/WRITE transaction events.'," + "MIN_TIMER_READ_WRITE BIGINT unsigned not null comment 'The minimum wait time of only READ/WRITE transaction events.'," + "AVG_TIMER_READ_WRITE BIGINT unsigned not null comment 'The average wait time of only READ/WRITE transaction events.'," + "MAX_TIMER_READ_WRITE BIGINT unsigned not null comment 'The maximum wait time of only READ/WRITE transaction events.'," + "COUNT_READ_ONLY BIGINT unsigned not null comment 'The total number of only READ ONLY transaction events.'," + "SUM_TIMER_READ_ONLY BIGINT unsigned not null comment 'The total wait time of only READ ONLY transaction events.'," + "MIN_TIMER_READ_ONLY BIGINT unsigned not null comment 'The minimum wait time of only READ ONLY transaction events.'," + "AVG_TIMER_READ_ONLY BIGINT unsigned not null comment 'The average wait time of only READ ONLY transaction events.'," + "MAX_TIMER_READ_ONLY BIGINT unsigned not null comment 'The maximum wait time of only READ ONLY transaction events.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_ets_by_host_by_event_name::create(void) +{ + return new table_ets_by_host_by_event_name(); +} + +int +table_ets_by_host_by_event_name::delete_all_rows(void) +{ + reset_events_transactions_by_thread(); + reset_events_transactions_by_account(); + reset_events_transactions_by_host(); + return 0; +} + +ha_rows +table_ets_by_host_by_event_name::get_row_count(void) +{ + return global_host_container.get_row_count() * transaction_class_max; +} + +table_ets_by_host_by_event_name::table_ets_by_host_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_ets_by_host_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_ets_by_host_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(transaction_timer); + return 0; +} + +int table_ets_by_host_by_event_name::rnd_next(void) +{ + PFS_host *host; + PFS_transaction_class *transaction_class; + bool has_more_host= true; + + for (m_pos.set_at(&m_next_pos); + has_more_host; + m_pos.next_host()) + { + host= global_host_container.get(m_pos.m_index_1, & has_more_host); + if (host != NULL) + { + transaction_class= find_transaction_class(m_pos.m_index_2); + if (transaction_class) + { + make_row(host, transaction_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_ets_by_host_by_event_name::rnd_pos(const void *pos) +{ + PFS_host *host; + PFS_transaction_class *transaction_class; + + set_position(pos); + + host= global_host_container.get(m_pos.m_index_1); + if (host != NULL) + { + transaction_class= find_transaction_class(m_pos.m_index_2); + if (transaction_class) + { + make_row(host, transaction_class); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_ets_by_host_by_event_name +::make_row(PFS_host *host, PFS_transaction_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + host->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_host.make_row(host)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_transaction_visitor visitor(klass); + PFS_connection_iterator::visit_host(host, + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! host->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(m_normalizer, & visitor.m_stat); +} + +int table_ets_by_host_by_event_name +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* HOST */ + m_row.m_host.set_field(f); + break; + case 1: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: + /** + COUNT_STAR, SUM/MIN/AVG/MAX_TIMER_WAIT, + COUNT_READ_WRITE, SUM/MIN/AVG/MAX_TIMER_READ_WRITE, + COUNT_READ_ONLY, SUM/MIN/AVG/MAX_TIMER_READ_ONLY + */ + m_row.m_stat.set_field(f->field_index-2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_ets_by_host_by_event_name.h b/storage/perfschema/table_ets_by_host_by_event_name.h new file mode 100644 index 00000000..00b4dbd0 --- /dev/null +++ b/storage/perfschema/table_ets_by_host_by_event_name.h @@ -0,0 +1,135 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ETS_BY_HOST_BY_EVENT_NAME_H +#define TABLE_ETS_BY_HOST_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_ets_by_host_by_event_name.h + Table EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_host.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME. +*/ +struct row_ets_by_host_by_event_name +{ + /** Column HOST */ + PFS_host_row m_host; + /** Column EVENT_NAME */ + PFS_event_name_row m_event_name; + /** + Columns COUNT_STAR, SUM/MIN/AVG/MAX_TIMER_WAIT, + COUNT_READ_WRITE, SUM/MIN/AVG/MAX_TIMER_READ_WRITE, + COUNT_READ_ONLY, SUM/MIN/AVG/MAX_TIMER_READ_ONLY + */ + PFS_transaction_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME. + Index 1 on host (0 based) + Index 2 on transaction class (1 based) +*/ +struct pos_ets_by_host_by_event_name +: public PFS_double_index +{ + pos_ets_by_host_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_host(void) + { + m_index_1++; + m_index_2= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME. */ +class table_ets_by_host_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_ets_by_host_by_event_name(); + +public: + ~table_ets_by_host_by_event_name() + {} + +protected: + void make_row(PFS_host *host, PFS_transaction_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_ets_by_host_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_ets_by_host_by_event_name m_pos; + /** Next position. */ + pos_ets_by_host_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_ets_by_thread_by_event_name.cc b/storage/perfschema/table_ets_by_thread_by_event_name.cc new file mode 100644 index 00000000..aff08f7f --- /dev/null +++ b/storage/perfschema/table_ets_by_thread_by_event_name.cc @@ -0,0 +1,225 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_ets_by_thread_by_event_name.cc + Table EVENTS_TRANSACTIONS_SUMMARY_BY_HOST_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_ets_by_thread_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_ets_by_thread_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_ets_by_thread_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_ets_by_thread_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_transactions_summary_by_thread_by_event_name") }, + &pfs_truncatable_acl, + table_ets_by_thread_by_event_name::create, + NULL, /* write_row */ + table_ets_by_thread_by_event_name::delete_all_rows, + table_ets_by_thread_by_event_name::get_row_count, + sizeof(pos_ets_by_thread_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_transactions_summary_by_thread_by_event_name(" + "THREAD_ID BIGINT unsigned not null comment 'Thread for which summary is generated.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name for which summary is generated.'," + "COUNT_STAR BIGINT unsigned not null comment 'The number of summarized events. This value includes all events, whether timed or nontimed.'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'The total wait time of the summarized timed events. This value is calculated only for timed events because nontimed events have a wait time of NULL. The same is true for the other xxx_TIMER_WAIT values.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'The minimum wait time of the summarized timed events.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'The average wait time of the summarized timed events.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'The maximum wait time of the summarized timed events.'," + "COUNT_READ_WRITE BIGINT unsigned not null comment 'The total number of only READ/WRITE transaction events.'," + "SUM_TIMER_READ_WRITE BIGINT unsigned not null comment 'The total wait time of only READ/WRITE transaction events.'," + "MIN_TIMER_READ_WRITE BIGINT unsigned not null comment 'The minimum wait time of only READ/WRITE transaction events.'," + "AVG_TIMER_READ_WRITE BIGINT unsigned not null comment 'The average wait time of only READ/WRITE transaction events.'," + "MAX_TIMER_READ_WRITE BIGINT unsigned not null comment 'The maximum wait time of only READ/WRITE transaction events.'," + "COUNT_READ_ONLY BIGINT unsigned not null comment 'The total number of only READ ONLY transaction events.'," + "SUM_TIMER_READ_ONLY BIGINT unsigned not null comment 'The total wait time of only READ ONLY transaction events.'," + "MIN_TIMER_READ_ONLY BIGINT unsigned not null comment 'The minimum wait time of only READ ONLY transaction events.'," + "AVG_TIMER_READ_ONLY BIGINT unsigned not null comment 'The average wait time of only READ ONLY transaction events.'," + "MAX_TIMER_READ_ONLY BIGINT unsigned not null comment 'The maximum wait time of only READ ONLY transaction events.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_ets_by_thread_by_event_name::create(void) +{ + return new table_ets_by_thread_by_event_name(); +} + +int +table_ets_by_thread_by_event_name::delete_all_rows(void) +{ + reset_events_transactions_by_thread(); + return 0; +} + +ha_rows +table_ets_by_thread_by_event_name::get_row_count(void) +{ + return global_thread_container.get_row_count() * transaction_class_max; +} + +table_ets_by_thread_by_event_name::table_ets_by_thread_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_ets_by_thread_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_ets_by_thread_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(transaction_timer); + return 0; +} + +int table_ets_by_thread_by_event_name::rnd_next(void) +{ + PFS_thread *thread; + PFS_transaction_class *transaction_class; + bool has_more_thread= true; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread); + if (thread != NULL) + { + transaction_class= find_transaction_class(m_pos.m_index_2); + if (transaction_class) + { + make_row(thread, transaction_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_ets_by_thread_by_event_name::rnd_pos(const void *pos) +{ + PFS_thread *thread; + PFS_transaction_class *transaction_class; + + set_position(pos); + + thread= global_thread_container.get(m_pos.m_index_1); + if (thread != NULL) + { + transaction_class= find_transaction_class(m_pos.m_index_2); + if (transaction_class) + { + make_row(thread, transaction_class); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_ets_by_thread_by_event_name +::make_row(PFS_thread *thread, PFS_transaction_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + /* Protect this reader against a thread termination */ + thread->m_lock.begin_optimistic_lock(&lock); + + m_row.m_thread_internal_id= thread->m_thread_internal_id; + + m_row.m_event_name.make_row(klass); + + PFS_connection_transaction_visitor visitor(klass); + PFS_connection_iterator::visit_thread(thread, &visitor); + + if (! thread->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(m_normalizer, &visitor.m_stat); +} + +int table_ets_by_thread_by_event_name +::read_row_values(TABLE *table, unsigned char *, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + 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_NAME */ + m_row.m_event_name.set_field(f); + break; + default: + /** + COUNT_STAR, SUM/MIN/AVG/MAX_TIMER_WAIT + COUNT_READ_WRITE, SUM/MIN/AVG/MAX_TIMER_READ_WRITE + COUNT_READ_ONLY, SUM/MIN/AVG/MAX_TIMER_READ_ONLY + */ + m_row.m_stat.set_field(f->field_index-2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_ets_by_thread_by_event_name.h b/storage/perfschema/table_ets_by_thread_by_event_name.h new file mode 100644 index 00000000..1fa62f84 --- /dev/null +++ b/storage/perfschema/table_ets_by_thread_by_event_name.h @@ -0,0 +1,139 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef TABLE_ETS_BY_THREAD_BY_EVENT_NAME_H +#define TABLE_ETS_BY_THREAD_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_ets_by_thread_by_event_name.h + Table EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME. +*/ +struct row_ets_by_thread_by_event_name +{ + /** Column THREAD_ID. */ + ulonglong m_thread_internal_id; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** + Columns COUNT_STAR, SUM/MIN/AVG/MAX_TIMER_WAIT, + COUNT_READ_WRITE, SUM/MIN/AVG/MAX_TIMER_READ_WRITE, + COUNT_READ_ONLY, SUM/MIN/AVG/MAX_TIMER_READ_ONLY + */ + PFS_transaction_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME. + Index 1 on thread (0 based). + Index 2 on transaction class (1 based). +*/ +struct pos_ets_by_thread_by_event_name +: public PFS_double_index, public PFS_instrument_view_constants +{ + pos_ets_by_thread_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= 1; + } + + inline void next_transaction(void) + { + m_index_2++; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_BY_THREAD_BY_EVENT_NAME. */ +class table_ets_by_thread_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_ets_by_thread_by_event_name(); + +public: + ~table_ets_by_thread_by_event_name() + {} + +protected: + void make_row(PFS_thread *thread, PFS_transaction_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_ets_by_thread_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_ets_by_thread_by_event_name m_pos; + /** Next position. */ + pos_ets_by_thread_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_ets_by_user_by_event_name.cc b/storage/perfschema/table_ets_by_user_by_event_name.cc new file mode 100644 index 00000000..6aff6838 --- /dev/null +++ b/storage/perfschema/table_ets_by_user_by_event_name.cc @@ -0,0 +1,232 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_ets_by_user_by_event_name.cc + Table EVENTS_TRANSACTIONS_SUMMARY_BY_USER_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_ets_by_user_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_ets_by_user_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_ets_by_user_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_ets_by_user_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_transactions_summary_by_user_by_event_name") }, + &pfs_truncatable_acl, + table_ets_by_user_by_event_name::create, + NULL, /* write_row */ + table_ets_by_user_by_event_name::delete_all_rows, + table_ets_by_user_by_event_name::get_row_count, + sizeof(pos_ets_by_user_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_transactions_summary_by_user_by_event_name(" + "USER CHAR(32) collate utf8_bin default null comment 'User for which summary is generated.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name for which summary is generated.'," + "COUNT_STAR BIGINT unsigned not null comment 'The number of summarized events. This value includes all events, whether timed or nontimed.'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'The total wait time of the summarized timed events. This value is calculated only for timed events because nontimed events have a wait time of NULL. The same is true for the other xxx_TIMER_WAIT values.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'The minimum wait time of the summarized timed events.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'The average wait time of the summarized timed events.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'The maximum wait time of the summarized timed events.'," + "COUNT_READ_WRITE BIGINT unsigned not null comment 'The total number of only READ/WRITE transaction events.'," + "SUM_TIMER_READ_WRITE BIGINT unsigned not null comment 'The total wait time of only READ/WRITE transaction events.'," + "MIN_TIMER_READ_WRITE BIGINT unsigned not null comment 'The minimum wait time of only READ/WRITE transaction events.'," + "AVG_TIMER_READ_WRITE BIGINT unsigned not null comment 'The average wait time of only READ/WRITE transaction events.'," + "MAX_TIMER_READ_WRITE BIGINT unsigned not null comment 'The maximum wait time of only READ/WRITE transaction events.'," + "COUNT_READ_ONLY BIGINT unsigned not null comment 'The total number of only READ ONLY transaction events.'," + "SUM_TIMER_READ_ONLY BIGINT unsigned not null comment 'The total wait time of only READ ONLY transaction events.'," + "MIN_TIMER_READ_ONLY BIGINT unsigned not null comment 'The minimum wait time of only READ ONLY transaction events.'," + "AVG_TIMER_READ_ONLY BIGINT unsigned not null comment 'The average wait time of only READ ONLY transaction events.'," + "MAX_TIMER_READ_ONLY BIGINT unsigned not null comment 'The maximum wait time of only READ ONLY transaction events.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_ets_by_user_by_event_name::create(void) +{ + return new table_ets_by_user_by_event_name(); +} + +int +table_ets_by_user_by_event_name::delete_all_rows(void) +{ + reset_events_transactions_by_thread(); + reset_events_transactions_by_account(); + reset_events_transactions_by_user(); + return 0; +} + +ha_rows +table_ets_by_user_by_event_name::get_row_count(void) +{ + return global_user_container.get_row_count() * transaction_class_max; +} + +table_ets_by_user_by_event_name::table_ets_by_user_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_ets_by_user_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_ets_by_user_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(transaction_timer); + return 0; +} + +int table_ets_by_user_by_event_name::rnd_next(void) +{ + PFS_user *user; + PFS_transaction_class *transaction_class; + bool has_more_user= true; + + for (m_pos.set_at(&m_next_pos); + has_more_user; + m_pos.next_user()) + { + user= global_user_container.get(m_pos.m_index_1, & has_more_user); + if (user != NULL) + { + transaction_class= find_transaction_class(m_pos.m_index_2); + if (transaction_class) + { + make_row(user, transaction_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_ets_by_user_by_event_name::rnd_pos(const void *pos) +{ + PFS_user *user; + PFS_transaction_class *transaction_class; + + set_position(pos); + + user= global_user_container.get(m_pos.m_index_1); + if (user != NULL) + { + transaction_class= find_transaction_class(m_pos.m_index_2); + if (transaction_class) + { + make_row(user, transaction_class); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_ets_by_user_by_event_name +::make_row(PFS_user *user, PFS_transaction_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + user->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_user.make_row(user)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_transaction_visitor visitor(klass); + PFS_connection_iterator::visit_user(user, + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! user->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(m_normalizer, & visitor.m_stat); +} + +int table_ets_by_user_by_event_name +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* USER */ + m_row.m_user.set_field(f); + break; + case 1: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: + /** + COUNT_STAR, SUM/MIN/AVG/MAX_TIMER_WAIT, + COUNT_READ_WRITE, SUM/MIN/AVG/MAX_TIMER_READ_WRITE, + COUNT_READ_ONLY, SUM/MIN/AVG/MAX_TIMER_READ_ONLY + */ + m_row.m_stat.set_field(f->field_index-2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_ets_by_user_by_event_name.h b/storage/perfschema/table_ets_by_user_by_event_name.h new file mode 100644 index 00000000..9bd5b859 --- /dev/null +++ b/storage/perfschema/table_ets_by_user_by_event_name.h @@ -0,0 +1,135 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_ETS_BY_USER_BY_EVENT_NAME_H +#define TABLE_ETS_BY_USER_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_ets_by_user_by_event_name.h + Table EVENTS_TRANSACTIONS_SUMMARY_BY_USER_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_user.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_BY_USER_BY_EVENT_NAME. +*/ +struct row_ets_by_user_by_event_name +{ + /** Column USER */ + PFS_user_row m_user; + /** Column EVENT_NAME */ + PFS_event_name_row m_event_name; + /** + Columns COUNT_STAR, SUM/MIN/AVG/MAX_TIMER_WAIT, + COUNT_READ_WRITE, SUM/MIN/AVG/MAX_TIMER_READ_WRITE, + COUNT_READ_ONLY, SUM/MIN/AVG/MAX_TIMER_READ_ONLY + */ + PFS_transaction_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_BY_USER_BY_EVENT_NAME. + Index 1 on user (0 based) + Index 2 on transaction class (1 based) +*/ +struct pos_ets_by_user_by_event_name +: public PFS_double_index +{ + pos_ets_by_user_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_user(void) + { + m_index_1++; + m_index_2= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_BY_USER_BY_EVENT_NAME. */ +class table_ets_by_user_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_ets_by_user_by_event_name(); + +public: + ~table_ets_by_user_by_event_name() + {} + +protected: + void make_row(PFS_user *user, PFS_transaction_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_ets_by_user_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_ets_by_user_by_event_name m_pos; + /** Next position. */ + pos_ets_by_user_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_ets_global_by_event_name.cc b/storage/perfschema/table_ets_global_by_event_name.cc new file mode 100644 index 00000000..b44d1151 --- /dev/null +++ b/storage/perfschema/table_ets_global_by_event_name.cc @@ -0,0 +1,207 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_ets_global_by_event_name.cc + Table EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_ets_global_by_event_name.h" +#include "pfs_global.h" +#include "pfs_instr.h" +#include "pfs_timer.h" +#include "pfs_visitor.h" +#include "field.h" + +THR_LOCK table_ets_global_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_ets_global_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_ets_global_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_transactions_summary_global_by_event_name") }, + &pfs_truncatable_acl, + table_ets_global_by_event_name::create, + NULL, /* write_row */ + table_ets_global_by_event_name::delete_all_rows, + table_ets_global_by_event_name::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_transactions_summary_global_by_event_name(" + "EVENT_NAME VARCHAR(128) not null comment 'Event name for which summary is generated.'," + "COUNT_STAR BIGINT unsigned not null comment 'The number of summarized events. This value includes all events, whether timed or nontimed.'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'The total wait time of the summarized timed events. This value is calculated only for timed events because nontimed events have a wait time of NULL. The same is true for the other xxx_TIMER_WAIT values.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'The minimum wait time of the summarized timed events.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'The average wait time of the summarized timed events.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'The maximum wait time of the summarized timed events.'," + "COUNT_READ_WRITE BIGINT unsigned not null comment 'The total number of only READ/WRITE transaction events.'," + "SUM_TIMER_READ_WRITE BIGINT unsigned not null comment 'The total wait time of only READ/WRITE transaction events.'," + "MIN_TIMER_READ_WRITE BIGINT unsigned not null comment 'The minimum wait time of only READ/WRITE transaction events.'," + "AVG_TIMER_READ_WRITE BIGINT unsigned not null comment 'The average wait time of only READ/WRITE transaction events.'," + "MAX_TIMER_READ_WRITE BIGINT unsigned not null comment 'The maximum wait time of only READ/WRITE transaction events.'," + "COUNT_READ_ONLY BIGINT unsigned not null comment 'The total number of only READ ONLY transaction events.'," + "SUM_TIMER_READ_ONLY BIGINT unsigned not null comment 'The total wait time of only READ ONLY transaction events.'," + "MIN_TIMER_READ_ONLY BIGINT unsigned not null comment 'The minimum wait time of only READ ONLY transaction events.'," + "AVG_TIMER_READ_ONLY BIGINT unsigned not null comment 'The average wait time of only READ ONLY transaction events.'," + "MAX_TIMER_READ_ONLY BIGINT unsigned not null comment 'The maximum wait time of only READ ONLY transaction events.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_ets_global_by_event_name::create(void) +{ + return new table_ets_global_by_event_name(); +} + +int +table_ets_global_by_event_name::delete_all_rows(void) +{ + reset_events_transactions_by_thread(); + reset_events_transactions_by_account(); + reset_events_transactions_by_user(); + reset_events_transactions_by_host(); + reset_events_transactions_global(); + return 0; +} + +ha_rows +table_ets_global_by_event_name::get_row_count(void) +{ + return transaction_class_max; +} + +table_ets_global_by_event_name::table_ets_global_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(1), m_next_pos(1) +{} + +void table_ets_global_by_event_name::reset_position(void) +{ + m_pos= 1; + m_next_pos= 1; +} + +int table_ets_global_by_event_name::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(transaction_timer); + return 0; +} + +int table_ets_global_by_event_name::rnd_next(void) +{ + PFS_transaction_class *transaction_class; + + m_pos.set_at(&m_next_pos); + + transaction_class= find_transaction_class(m_pos.m_index); + if (transaction_class) + { + make_row(transaction_class); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int +table_ets_global_by_event_name::rnd_pos(const void *pos) +{ + PFS_transaction_class *transaction_class; + + set_position(pos); + + transaction_class=find_transaction_class(m_pos.m_index); + if (transaction_class) + { + make_row(transaction_class); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + + +void table_ets_global_by_event_name +::make_row(PFS_transaction_class *klass) +{ + m_row.m_event_name.make_row(klass); + + PFS_connection_transaction_visitor visitor(klass); + PFS_connection_iterator::visit_global(true, /* hosts */ + false, /* users */ + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + m_row.m_stat.set(m_normalizer, & visitor.m_stat); + m_row_exists= true; +} + +int table_ets_global_by_event_name +::read_row_values(TABLE *table, unsigned char *, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* NAME */ + m_row.m_event_name.set_field(f); + break; + default: + /** + Columns COUNT_STAR, SUM/MIN/AVG/MAX_TIMER_WAIT, + COUNT_READ_WRITE, SUM/MIN/AVG/MAX_TIMER_READ_WRITE, + COUNT_READ_ONLY, SUM/MIN/AVG/MAX_TIMER_READ_ONLY + */ + m_row.m_stat.set_field(f->field_index - 1, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_ets_global_by_event_name.h b/storage/perfschema/table_ets_global_by_event_name.h new file mode 100644 index 00000000..d5a67f43 --- /dev/null +++ b/storage/perfschema/table_ets_global_by_event_name.h @@ -0,0 +1,106 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef TABLE_ETS_GLOBAL_BY_EVENT_NAME_H +#define TABLE_ETS_GLOBAL_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_ets_global_by_event_name.h + Table EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME. +*/ +struct row_ets_global_by_event_name +{ + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** + Columns COUNT_STAR, SUM/MIN/AVG/MAX_TIMER_WAIT, + COUNT_READ_WRITE, SUM/MIN/AVG/MAX_TIMER_READ_WRITE, + COUNT_READ_ONLY, SUM/MIN/AVG/MAX_TIMER_READ_ONLY + */ + PFS_transaction_stat_row m_stat; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_SUMMARY_GLOBAL_BY_EVENT_NAME. */ +class table_ets_global_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_ets_global_by_event_name(); + +public: + ~table_ets_global_by_event_name() + {} + +protected: + void make_row(PFS_transaction_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_ets_global_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_events_stages.cc b/storage/perfschema/table_events_stages.cc new file mode 100644 index 00000000..7b9b59a1 --- /dev/null +++ b/storage/perfschema/table_events_stages.cc @@ -0,0 +1,562 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_events_stages.cc + Table EVENTS_STAGES_xxx (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "table_events_stages.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_events_stages.h" +#include "pfs_timer.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_events_stages_current::m_table_lock; + +PFS_engine_table_share_state +table_events_stages_current::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_events_stages_current::m_share= +{ + { C_STRING_WITH_LEN("events_stages_current") }, + &pfs_truncatable_acl, + table_events_stages_current::create, + NULL, /* write_row */ + table_events_stages_current::delete_all_rows, + table_events_stages_current::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_stages_current(" + "THREAD_ID BIGINT unsigned not null comment 'Thread associated with the event. Together with EVENT_ID uniquely identifies the row.'," + "EVENT_ID BIGINT unsigned not null comment 'Thread''s current event number at the start of the event. Together with THREAD_ID uniquely identifies the row.'," + "END_EVENT_ID BIGINT unsigned comment 'NULL when the event starts, set to the thread''s current event number at the end of the event.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event instrument name and a NAME from the setup_instruments table'," + "SOURCE VARCHAR(64) comment 'Name and line number of the source file containing the instrumented code that produced the event.'," + "TIMER_START BIGINT unsigned comment 'Value in picoseconds when the event timing started or NULL if timing is not collected.'," + "TIMER_END BIGINT unsigned comment 'Value in picoseconds when the event timing ended, or NULL if the event has not ended or timing is not collected.'," + "TIMER_WAIT BIGINT unsigned comment 'Value in picoseconds of the event''s duration or NULL if the event has not ended or timing is not collected.'," + "WORK_COMPLETED BIGINT unsigned comment 'The number of work units completed for the stage. NULL if the stage event progress is not instrumented.'," + "WORK_ESTIMATED BIGINT unsigned comment 'The number of work units expected for the stage. NULL if the stage event progress is not instrumented.'," + "NESTING_EVENT_ID BIGINT unsigned comment 'EVENT_ID of event within which this event nests.'," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT') comment 'Nesting event type. Either transaction, statement, stage or wait.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +THR_LOCK table_events_stages_history::m_table_lock; + +PFS_engine_table_share_state +table_events_stages_history::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_events_stages_history::m_share= +{ + { C_STRING_WITH_LEN("events_stages_history") }, + &pfs_truncatable_acl, + table_events_stages_history::create, + NULL, /* write_row */ + table_events_stages_history::delete_all_rows, + table_events_stages_history::get_row_count, + sizeof(pos_events_stages_history), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_stages_history(" + "THREAD_ID BIGINT unsigned not null comment 'Thread associated with the event. Together with EVENT_ID uniquely identifies the row.'," + "EVENT_ID BIGINT unsigned not null comment 'Thread''s current event number at the start of the event. Together with THREAD_ID uniquely identifies the row.'," + "END_EVENT_ID BIGINT unsigned comment 'NULL when the event starts, set to the thread''s current event number at the end of the event.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event instrument name and a NAME from the setup_instruments table'," + "SOURCE VARCHAR(64) comment 'Name and line number of the source file containing the instrumented code that produced the event.'," + "TIMER_START BIGINT unsigned comment 'Value in picoseconds when the event timing started or NULL if timing is not collected.'," + "TIMER_END BIGINT unsigned comment 'Value in picoseconds when the event timing ended, or NULL if the event has not ended or timing is not collected.'," + "TIMER_WAIT BIGINT unsigned comment 'Value in picoseconds of the event''s duration or NULL if the event has not ended or timing is not collected.'," + "WORK_COMPLETED BIGINT unsigned comment 'The number of work units completed for the stage. NULL if the stage event progress is not instrumented.'," + "WORK_ESTIMATED BIGINT unsigned comment 'The number of work units expected for the stage. NULL if the stage event progress is not instrumented.'," + "NESTING_EVENT_ID BIGINT unsigned comment 'EVENT_ID of event within which this event nests.'," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT') comment 'Nesting event type. Either transaction, statement, stage or wait.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +THR_LOCK table_events_stages_history_long::m_table_lock; + +PFS_engine_table_share_state +table_events_stages_history_long::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_events_stages_history_long::m_share= +{ + { C_STRING_WITH_LEN("events_stages_history_long") }, + &pfs_truncatable_acl, + table_events_stages_history_long::create, + NULL, /* write_row */ + table_events_stages_history_long::delete_all_rows, + table_events_stages_history_long::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_stages_history_long(" + "THREAD_ID BIGINT unsigned not null comment 'Thread associated with the event. Together with EVENT_ID uniquely identifies the row.'," + "EVENT_ID BIGINT unsigned not null comment 'Thread''s current event number at the start of the event. Together with THREAD_ID uniquely identifies the row.'," + "END_EVENT_ID BIGINT unsigned comment 'NULL when the event starts, set to the thread''s current event number at the end of the event.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event instrument name and a NAME from the setup_instruments table'," + "SOURCE VARCHAR(64) comment 'Name and line number of the source file containing the instrumented code that produced the event.'," + "TIMER_START BIGINT unsigned comment 'Value in picoseconds when the event timing started or NULL if timing is not collected.'," + "TIMER_END BIGINT unsigned comment 'Value in picoseconds when the event timing ended, or NULL if the event has not ended or timing is not collected.'," + "TIMER_WAIT BIGINT unsigned comment 'Value in picoseconds of the event''s duration or NULL if the event has not ended or timing is not collected.'," + "WORK_COMPLETED BIGINT unsigned comment 'The number of work units completed for the stage. NULL if the stage event progress is not instrumented.'," + "WORK_ESTIMATED BIGINT unsigned comment 'The number of work units expected for the stage. NULL if the stage event progress is not instrumented.'," + "NESTING_EVENT_ID BIGINT unsigned comment 'EVENT_ID of event within which this event nests.'," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT') comment 'Nesting event type. Either transaction, statement, stage or wait.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +table_events_stages_common::table_events_stages_common +(const PFS_engine_table_share *share, void *pos) + : PFS_engine_table(share, pos), + m_row_exists(false) +{} + +/** + Build a row. + @param stage the stage the cursor is reading +*/ +void table_events_stages_common::make_row(PFS_events_stages *stage) +{ + ulonglong timer_end; + + m_row_exists= false; + + PFS_stage_class *unsafe= (PFS_stage_class*) stage->m_class; + PFS_stage_class *klass= sanitize_stage_class(unsafe); + if (unlikely(klass == NULL)) + return; + + m_row.m_thread_internal_id= stage->m_thread_internal_id; + m_row.m_event_id= stage->m_event_id; + m_row.m_end_event_id= stage->m_end_event_id; + m_row.m_nesting_event_id= stage->m_nesting_event_id; + m_row.m_nesting_event_type= stage->m_nesting_event_type; + + if (m_row.m_end_event_id == 0) + { + timer_end= get_timer_raw_value(stage_timer); + } + else + { + timer_end= stage->m_timer_end; + } + + m_normalizer->to_pico(stage->m_timer_start, timer_end, + & m_row.m_timer_start, & m_row.m_timer_end, & m_row.m_timer_wait); + + m_row.m_name= klass->m_name; + m_row.m_name_length= klass->m_name_length; + + /* Disable source file and line to avoid stale __FILE__ pointers. */ + m_row.m_source_length= 0; + + if (klass->is_progress()) + { + m_row.m_progress= true; + m_row.m_work_completed= stage->m_progress.m_work_completed; + m_row.m_work_estimated= stage->m_progress.m_work_estimated; + } + else + { + m_row.m_progress= false; + } + + m_row_exists= true; + return; +} + +int table_events_stages_common::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 2); + buf[0]= 0; + buf[1]= 0; + + 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: /* WORK_COMPLETED */ + if (m_row.m_progress) + set_field_ulonglong(f, m_row.m_work_completed); + else + f->set_null(); + break; + case 9: /* WORK_ESTIMATED */ + if (m_row.m_progress) + set_field_ulonglong(f, m_row.m_work_estimated); + else + f->set_null(); + break; + case 10: /* 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 11: /* 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; + default: + assert(false); + } + } + } + return 0; +} + +PFS_engine_table* table_events_stages_current::create(void) +{ + return new table_events_stages_current(); +} + +table_events_stages_current::table_events_stages_current() + : table_events_stages_common(&m_share, &m_pos), + m_pos(0), m_next_pos(0) +{} + +void table_events_stages_current::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_events_stages_current::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(stage_timer); + return 0; +} + +int table_events_stages_current::rnd_next(void) +{ + PFS_thread *pfs_thread; + PFS_events_stages *stage; + + m_pos.set_at(&m_next_pos); + PFS_thread_iterator it= global_thread_container.iterate(m_pos.m_index); + pfs_thread= it.scan_next(& m_pos.m_index); + if (pfs_thread != NULL) + { + stage= &pfs_thread->m_stage_current; + make_row(stage); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_events_stages_current::rnd_pos(const void *pos) +{ + PFS_thread *pfs_thread; + PFS_events_stages *stage; + + set_position(pos); + + pfs_thread= global_thread_container.get(m_pos.m_index); + if (pfs_thread != NULL) + { + stage= &pfs_thread->m_stage_current; + make_row(stage); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +int table_events_stages_current::delete_all_rows(void) +{ + reset_events_stages_current(); + return 0; +} + +ha_rows +table_events_stages_current::get_row_count(void) +{ + return global_thread_container.get_row_count(); +} + +PFS_engine_table* table_events_stages_history::create(void) +{ + return new table_events_stages_history(); +} + +table_events_stages_history::table_events_stages_history() + : table_events_stages_common(&m_share, &m_pos), + m_pos(), m_next_pos() +{} + +void table_events_stages_history::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_events_stages_history::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(stage_timer); + return 0; +} + +int table_events_stages_history::rnd_next(void) +{ + PFS_thread *pfs_thread; + PFS_events_stages *stage; + bool has_more_thread= true; + + if (events_stages_history_per_thread == 0) + return HA_ERR_END_OF_FILE; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + pfs_thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread); + if (pfs_thread != NULL) + { + if (m_pos.m_index_2 >= events_stages_history_per_thread) + { + /* This thread does not have more (full) history */ + continue; + } + + if ( ! pfs_thread->m_stages_history_full && + (m_pos.m_index_2 >= pfs_thread->m_stages_history_index)) + { + /* This thread does not have more (not full) history */ + continue; + } + + stage= &pfs_thread->m_stages_history[m_pos.m_index_2]; + + if (stage->m_class != NULL) + { + make_row(stage); + /* 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_stages_history::rnd_pos(const void *pos) +{ + PFS_thread *pfs_thread; + PFS_events_stages *stage; + + assert(events_stages_history_per_thread != 0); + set_position(pos); + + assert(m_pos.m_index_2 < events_stages_history_per_thread); + + pfs_thread= global_thread_container.get(m_pos.m_index_1); + if (pfs_thread != NULL) + { + if ( ! pfs_thread->m_stages_history_full && + (m_pos.m_index_2 >= pfs_thread->m_stages_history_index)) + return HA_ERR_RECORD_DELETED; + + stage= &pfs_thread->m_stages_history[m_pos.m_index_2]; + + if (stage->m_class != NULL) + { + make_row(stage); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +int table_events_stages_history::delete_all_rows(void) +{ + reset_events_stages_history(); + return 0; +} + +ha_rows +table_events_stages_history::get_row_count(void) +{ + return events_stages_history_per_thread * global_thread_container.get_row_count(); +} + +PFS_engine_table* table_events_stages_history_long::create(void) +{ + return new table_events_stages_history_long(); +} + +table_events_stages_history_long::table_events_stages_history_long() + : table_events_stages_common(&m_share, &m_pos), + m_pos(0), m_next_pos(0) +{} + +void table_events_stages_history_long::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_events_stages_history_long::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(stage_timer); + return 0; +} + +int table_events_stages_history_long::rnd_next(void) +{ + PFS_events_stages *stage; + uint limit; + + if (events_stages_history_long_size == 0) + return HA_ERR_END_OF_FILE; + + if (events_stages_history_long_full) + limit= events_stages_history_long_size; + else + limit= events_stages_history_long_index.m_u32 % events_stages_history_long_size; + + for (m_pos.set_at(&m_next_pos); m_pos.m_index < limit; m_pos.next()) + { + stage= &events_stages_history_long_array[m_pos.m_index]; + + if (stage->m_class != NULL) + { + make_row(stage); + /* 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_stages_history_long::rnd_pos(const void *pos) +{ + PFS_events_stages *stage; + uint limit; + + if (events_stages_history_long_size == 0) + return HA_ERR_RECORD_DELETED; + + set_position(pos); + + if (events_stages_history_long_full) + limit= events_stages_history_long_size; + else + limit= events_stages_history_long_index.m_u32 % events_stages_history_long_size; + + if (m_pos.m_index > limit) + return HA_ERR_RECORD_DELETED; + + stage= &events_stages_history_long_array[m_pos.m_index]; + + if (stage->m_class == NULL) + return HA_ERR_RECORD_DELETED; + + make_row(stage); + return 0; +} + +int table_events_stages_history_long::delete_all_rows(void) +{ + reset_events_stages_history_long(); + return 0; +} + +ha_rows +table_events_stages_history_long::get_row_count(void) +{ + return events_stages_history_long_size; +} + diff --git a/storage/perfschema/table_events_stages.h b/storage/perfschema/table_events_stages.h new file mode 100644 index 00000000..d8b973ac --- /dev/null +++ b/storage/perfschema/table_events_stages.h @@ -0,0 +1,220 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_EVENTS_STAGES_H +#define TABLE_EVENTS_STAGES_H + +/** + @file storage/perfschema/table_events_stages.h + Table EVENTS_STAGES_xxx (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_events_stages.h" + +struct PFS_thread; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of table_events_stages_common. */ +struct row_events_stages +{ + /** Column THREAD_ID. */ + ulonglong m_thread_internal_id; + /** Column EVENT_ID. */ + ulonglong m_event_id; + /** Column END_EVENT_ID. */ + ulonglong m_end_event_id; + /** Column NESTING_EVENT_ID. */ + ulonglong m_nesting_event_id; + /** Column NESTING_EVENT_TYPE. */ + enum_event_type m_nesting_event_type; + /** Column EVENT_NAME. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** Column TIMER_START. */ + ulonglong m_timer_start; + /** Column TIMER_END. */ + ulonglong m_timer_end; + /** Column TIMER_WAIT. */ + ulonglong m_timer_wait; + /** Column SOURCE. */ + char m_source[COL_SOURCE_SIZE]; + /** Length in bytes of @c m_source. */ + uint m_source_length; + bool m_progress; + /** Column WORK_COMPLETED. */ + ulonglong m_work_completed; + /** Column WORK_ESTIMATED. */ + ulonglong m_work_estimated; +}; + +/** Position of a cursor on PERFORMANCE_SCHEMA.EVENTS_STAGES_HISTORY. */ +struct pos_events_stages_history : public PFS_double_index +{ + pos_events_stages_history() + : PFS_double_index(0, 0) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** + Adapter, for table sharing the structure of + PERFORMANCE_SCHEMA.EVENTS_STAGES_CURRENT. +*/ +class table_events_stages_common : public PFS_engine_table +{ +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_events_stages_common(const PFS_engine_table_share *share, void *pos); + + ~table_events_stages_common() = default; + + void make_row(PFS_events_stages *stage); + + /** Current row. */ + row_events_stages m_row; + /** True if the current row exists. */ + bool m_row_exists; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STAGES_CURRENT. */ +class table_events_stages_current : public table_events_stages_common +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + table_events_stages_current(); + +public: + ~table_events_stages_current() = default; + +private: + friend class table_events_stages_history; + friend class table_events_stages_history_long; + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STAGES_HISTORY. */ +class table_events_stages_history : public table_events_stages_common +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + table_events_stages_history(); + +public: + ~table_events_stages_history() = default; + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current position. */ + pos_events_stages_history m_pos; + /** Next position. */ + pos_events_stages_history m_next_pos; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STAGES_HISTORY_LONG. */ +class table_events_stages_history_long : public table_events_stages_common +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + table_events_stages_history_long(); + +public: + ~table_events_stages_history_long() = default; + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_events_statements.cc b/storage/perfschema/table_events_statements.cc new file mode 100644 index 00000000..7801dab5 --- /dev/null +++ b/storage/perfschema/table_events_statements.cc @@ -0,0 +1,962 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_events_statements.cc + Table EVENTS_STATEMENTS_xxx (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "table_events_statements.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_events_statements.h" +#include "pfs_timer.h" +#include "sp_head.h" /* TYPE_ENUM_FUNCTION, ... */ +#include "table_helper.h" +#include "my_md5.h" +#include "pfs_buffer_container.h" + +THR_LOCK table_events_statements_current::m_table_lock; + +PFS_engine_table_share_state +table_events_statements_current::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_events_statements_current::m_share= +{ + { C_STRING_WITH_LEN("events_statements_current") }, + &pfs_truncatable_acl, + table_events_statements_current::create, + NULL, /* write_row */ + table_events_statements_current::delete_all_rows, + table_events_statements_current::get_row_count, + sizeof(pos_events_statements_current), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_statements_current(" + "THREAD_ID BIGINT unsigned not null comment 'Thread associated with the event. Together with EVENT_ID uniquely identifies the row.'," + "EVENT_ID BIGINT unsigned not null comment 'Thread''s current event number at the start of the event. Together with THREAD_ID uniquely identifies the row.'," + "END_EVENT_ID BIGINT unsigned comment 'NULL when the event starts, set to the thread''s current event number at the end of the event.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event instrument name and a NAME from the setup_instruments table'," + "SOURCE VARCHAR(64) comment 'Name and line number of the source file containing the instrumented code that produced the event.'," + "TIMER_START BIGINT unsigned comment 'Value in picoseconds when the event timing started or NULL if timing is not collected.'," + "TIMER_END BIGINT unsigned comment 'Value in picoseconds when the event timing ended, or NULL if the event has not ended or timing is not collected.'," + "TIMER_WAIT BIGINT unsigned comment 'Value in picoseconds of the event''s duration or NULL if the event has not ended or timing is not collected.'," + "LOCK_TIME bigint unsigned not null comment 'Time in picoseconds spent waiting for locks. The time is calculated in microseconds but stored in picoseconds for compatibility with other timings.'," + "SQL_TEXT LONGTEXT comment 'The SQL statement, or NULL if the command is not associated with an SQL statement.'," + "DIGEST VARCHAR(32) comment 'Statement digest.'," + "DIGEST_TEXT LONGTEXT comment 'Statement digest text.'," + "CURRENT_SCHEMA VARCHAR(64) comment 'Statement''s default database for the statement, or NULL if there was none.'," + "OBJECT_TYPE VARCHAR(64) comment 'NULL for top level statements. The parent statement object type for nested statements (stored programs).'," + "OBJECT_SCHEMA VARCHAR(64) comment 'NULL for top level statements. The parent statement object schema for nested statements (stored programs).'," + "OBJECT_NAME VARCHAR(64) comment 'NULL for top level statements. The parent statement object name for nested statements (stored programs).'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned comment 'Address in memory of the statement object.'," + "MYSQL_ERRNO INTEGER comment 'Error code. See MariaDB Error Codes for a full list.'," + "RETURNED_SQLSTATE VARCHAR(5) comment 'The SQLSTATE value.'," + "MESSAGE_TEXT VARCHAR(128) comment 'Statement error message. See MariaDB Error Codes.'," + "ERRORS BIGINT unsigned not null comment '0 if SQLSTATE signifies completion (starting with 00) or warning (01), otherwise 1.'," + "WARNINGS BIGINT unsigned not null comment 'Number of warnings from the diagnostics area.'," + "ROWS_AFFECTED BIGINT unsigned not null comment 'Number of rows affected the statement affected.'," + "ROWS_SENT BIGINT unsigned not null comment 'Number of rows returned.'," + "ROWS_EXAMINED BIGINT unsigned not null comment 'Number of rows read during the statement''s execution.'," + "CREATED_TMP_DISK_TABLES BIGINT unsigned not null comment 'Number of on-disk temp tables created by the statement.'," + "CREATED_TMP_TABLES BIGINT unsigned not null comment 'Number of temp tables created by the statement.'," + "SELECT_FULL_JOIN BIGINT unsigned not null comment 'Number of joins performed by the statement which did not use an index.'," + "SELECT_FULL_RANGE_JOIN BIGINT unsigned not null comment 'Number of joins performed by the statement which used a range search of the first table.'," + "SELECT_RANGE BIGINT unsigned not null comment 'Number of joins performed by the statement which used a range of the first table.'," + "SELECT_RANGE_CHECK BIGINT unsigned not null comment 'Number of joins without keys performed by the statement that check for key usage after each row.'," + "SELECT_SCAN BIGINT unsigned not null comment 'Number of joins performed by the statement which used a full scan of the first table.'," + "SORT_MERGE_PASSES BIGINT unsigned not null comment 'Number of merge passes by the sort algorithm performed by the statement. If too high, you may need to increase the sort_buffer_size.'," + "SORT_RANGE BIGINT unsigned not null comment 'Number of sorts performed by the statement which used a range.'," + "SORT_ROWS BIGINT unsigned not null comment 'Number of rows sorted by the statement.'," + "SORT_SCAN BIGINT unsigned not null comment 'Number of sorts performed by the statement which used a full table scan.'," + "NO_INDEX_USED BIGINT unsigned not null comment '0 if the statement performed a table scan with an index, 1 if without an index.'," + "NO_GOOD_INDEX_USED BIGINT unsigned not null comment '0 if a good index was found for the statement, 1 if no good index was found. See the Range checked for each record description in the EXPLAIN article.'," + "NESTING_EVENT_ID BIGINT unsigned comment 'NULL for top level statements. The parent statement event id for nested statements (stored programs).'," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT') comment 'NULL for top level statements. The parent statement event type for nested statements (stored programs).'," + "NESTING_EVENT_LEVEL INT comment '0 for top level statements. The parent statement level plus 1 for nested statements (stored programs).')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +THR_LOCK table_events_statements_history::m_table_lock; + +PFS_engine_table_share_state +table_events_statements_history::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_events_statements_history::m_share= +{ + { C_STRING_WITH_LEN("events_statements_history") }, + &pfs_truncatable_acl, + table_events_statements_history::create, + NULL, /* write_row */ + table_events_statements_history::delete_all_rows, + table_events_statements_history::get_row_count, + sizeof(pos_events_statements_history), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_statements_history(" + "THREAD_ID BIGINT unsigned not null comment 'Thread associated with the event. Together with EVENT_ID uniquely identifies the row.'," + "EVENT_ID BIGINT unsigned not null comment 'Thread''s current event number at the start of the event. Together with THREAD_ID uniquely identifies the row.'," + "END_EVENT_ID BIGINT unsigned comment 'NULL when the event starts, set to the thread''s current event number at the end of the event.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event instrument name and a NAME from the setup_instruments table'," + "SOURCE VARCHAR(64) comment 'Name and line number of the source file containing the instrumented code that produced the event.'," + "TIMER_START BIGINT unsigned comment 'Value in picoseconds when the event timing started or NULL if timing is not collected.'," + "TIMER_END BIGINT unsigned comment 'Value in picoseconds when the event timing ended, or NULL if the event has not ended or timing is not collected.'," + "TIMER_WAIT BIGINT unsigned comment 'Value in picoseconds of the event''s duration or NULL if the event has not ended or timing is not collected.'," + "LOCK_TIME bigint unsigned not null comment 'Time in picoseconds spent waiting for locks. The time is calculated in microseconds but stored in picoseconds for compatibility with other timings.'," + "SQL_TEXT LONGTEXT comment 'The SQL statement, or NULL if the command is not associated with an SQL statement.'," + "DIGEST VARCHAR(32) comment 'Statement digest.'," + "DIGEST_TEXT LONGTEXT comment 'Statement digest text.'," + "CURRENT_SCHEMA VARCHAR(64) comment 'Statement''s default database for the statement, or NULL if there was none.'," + "OBJECT_TYPE VARCHAR(64) comment 'NULL for top level statements. The parent statement object type for nested statements (stored programs).'," + "OBJECT_SCHEMA VARCHAR(64) comment 'NULL for top level statements. The parent statement object schema for nested statements (stored programs).'," + "OBJECT_NAME VARCHAR(64) comment 'NULL for top level statements. The parent statement object name for nested statements (stored programs).'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned comment 'Address in memory of the statement object.'," + "MYSQL_ERRNO INTEGER comment 'Error code. See MariaDB Error Codes for a full list.'," + "RETURNED_SQLSTATE VARCHAR(5) comment 'The SQLSTATE value.'," + "MESSAGE_TEXT VARCHAR(128) comment 'Statement error message. See MariaDB Error Codes.'," + "ERRORS BIGINT unsigned not null comment '0 if SQLSTATE signifies completion (starting with 00) or warning (01), otherwise 1.'," + "WARNINGS BIGINT unsigned not null comment 'Number of warnings from the diagnostics area.'," + "ROWS_AFFECTED BIGINT unsigned not null comment 'Number of rows affected the statement affected.'," + "ROWS_SENT BIGINT unsigned not null comment 'Number of rows returned.'," + "ROWS_EXAMINED BIGINT unsigned not null comment 'Number of rows read during the statement''s execution.'," + "CREATED_TMP_DISK_TABLES BIGINT unsigned not null comment 'Number of on-disk temp tables created by the statement.'," + "CREATED_TMP_TABLES BIGINT unsigned not null comment 'Number of temp tables created by the statement.'," + "SELECT_FULL_JOIN BIGINT unsigned not null comment 'Number of joins performed by the statement which did not use an index.'," + "SELECT_FULL_RANGE_JOIN BIGINT unsigned not null comment 'Number of joins performed by the statement which used a range search of the first table.'," + "SELECT_RANGE BIGINT unsigned not null comment 'Number of joins performed by the statement which used a range of the first table.'," + "SELECT_RANGE_CHECK BIGINT unsigned not null comment 'Number of joins without keys performed by the statement that check for key usage after each row.'," + "SELECT_SCAN BIGINT unsigned not null comment 'Number of joins performed by the statement which used a full scan of the first table.'," + "SORT_MERGE_PASSES BIGINT unsigned not null comment 'Number of merge passes by the sort algorithm performed by the statement. If too high, you may need to increase the sort_buffer_size.'," + "SORT_RANGE BIGINT unsigned not null comment 'Number of sorts performed by the statement which used a range.'," + "SORT_ROWS BIGINT unsigned not null comment 'Number of rows sorted by the statement.'," + "SORT_SCAN BIGINT unsigned not null comment 'Number of sorts performed by the statement which used a full table scan.'," + "NO_INDEX_USED BIGINT unsigned not null comment '0 if the statement performed a table scan with an index, 1 if without an index.'," + "NO_GOOD_INDEX_USED BIGINT unsigned not null comment '0 if a good index was found for the statement, 1 if no good index was found. See the Range checked for each record description in the EXPLAIN article.'," + "NESTING_EVENT_ID BIGINT unsigned comment 'NULL for top level statements. The parent statement event id for nested statements (stored programs).'," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT') comment 'NULL for top level statements. The parent statement event type for nested statements (stored programs).'," + "NESTING_EVENT_LEVEL INT comment '0 for top level statements. The parent statement level plus 1 for nested statements (stored programs).')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +THR_LOCK table_events_statements_history_long::m_table_lock; + +PFS_engine_table_share_state +table_events_statements_history_long::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_events_statements_history_long::m_share= +{ + { C_STRING_WITH_LEN("events_statements_history_long") }, + &pfs_truncatable_acl, + table_events_statements_history_long::create, + NULL, /* write_row */ + table_events_statements_history_long::delete_all_rows, + table_events_statements_history_long::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_statements_history_long(" + "THREAD_ID BIGINT unsigned not null comment 'Thread associated with the event. Together with EVENT_ID uniquely identifies the row.'," + "EVENT_ID BIGINT unsigned not null comment 'Thread''s current event number at the start of the event. Together with THREAD_ID uniquely identifies the row.'," + "END_EVENT_ID BIGINT unsigned comment 'NULL when the event starts, set to the thread''s current event number at the end of the event.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event instrument name and a NAME from the setup_instruments table'," + "SOURCE VARCHAR(64) comment 'Name and line number of the source file containing the instrumented code that produced the event.'," + "TIMER_START BIGINT unsigned comment 'Value in picoseconds when the event timing started or NULL if timing is not collected.'," + "TIMER_END BIGINT unsigned comment 'Value in picoseconds when the event timing ended, or NULL if the event has not ended or timing is not collected.'," + "TIMER_WAIT BIGINT unsigned comment 'Value in picoseconds of the event''s duration or NULL if the event has not ended or timing is not collected.'," + "LOCK_TIME bigint unsigned not null comment 'Time in picoseconds spent waiting for locks. The time is calculated in microseconds but stored in picoseconds for compatibility with other timings.'," + "SQL_TEXT LONGTEXT comment 'The SQL statement, or NULL if the command is not associated with an SQL statement.'," + "DIGEST VARCHAR(32) comment 'Statement digest.'," + "DIGEST_TEXT LONGTEXT comment 'Statement digest text.'," + "CURRENT_SCHEMA VARCHAR(64) comment 'Statement''s default database for the statement, or NULL if there was none.'," + "OBJECT_TYPE VARCHAR(64) comment 'NULL for top level statements. The parent statement object type for nested statements (stored programs).'," + "OBJECT_SCHEMA VARCHAR(64) comment 'NULL for top level statements. The parent statement object schema for nested statements (stored programs).'," + "OBJECT_NAME VARCHAR(64) comment 'NULL for top level statements. The parent statement object name for nested statements (stored programs).'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned comment 'Address in memory of the statement object.'," + "MYSQL_ERRNO INTEGER comment 'Error code. See MariaDB Error Codes for a full list.'," + "RETURNED_SQLSTATE VARCHAR(5) comment 'The SQLSTATE value.'," + "MESSAGE_TEXT VARCHAR(128) comment 'Statement error message. See MariaDB Error Codes.'," + "ERRORS BIGINT unsigned not null comment '0 if SQLSTATE signifies completion (starting with 00) or warning (01), otherwise 1.'," + "WARNINGS BIGINT unsigned not null comment 'Number of warnings from the diagnostics area.'," + "ROWS_AFFECTED BIGINT unsigned not null comment 'Number of rows affected the statement affected.'," + "ROWS_SENT BIGINT unsigned not null comment 'Number of rows returned.'," + "ROWS_EXAMINED BIGINT unsigned not null comment 'Number of rows read during the statement''s execution.'," + "CREATED_TMP_DISK_TABLES BIGINT unsigned not null comment 'Number of on-disk temp tables created by the statement.'," + "CREATED_TMP_TABLES BIGINT unsigned not null comment 'Number of temp tables created by the statement.'," + "SELECT_FULL_JOIN BIGINT unsigned not null comment 'Number of joins performed by the statement which did not use an index.'," + "SELECT_FULL_RANGE_JOIN BIGINT unsigned not null comment 'Number of joins performed by the statement which used a range search of the first table.'," + "SELECT_RANGE BIGINT unsigned not null comment 'Number of joins performed by the statement which used a range of the first table.'," + "SELECT_RANGE_CHECK BIGINT unsigned not null comment 'Number of joins without keys performed by the statement that check for key usage after each row.'," + "SELECT_SCAN BIGINT unsigned not null comment 'Number of joins performed by the statement which used a full scan of the first table.'," + "SORT_MERGE_PASSES BIGINT unsigned not null comment 'Number of merge passes by the sort algorithm performed by the statement. If too high, you may need to increase the sort_buffer_size.'," + "SORT_RANGE BIGINT unsigned not null comment 'Number of sorts performed by the statement which used a range.'," + "SORT_ROWS BIGINT unsigned not null comment 'Number of rows sorted by the statement.'," + "SORT_SCAN BIGINT unsigned not null comment 'Number of sorts performed by the statement which used a full table scan.'," + "NO_INDEX_USED BIGINT unsigned not null comment '0 if the statement performed a table scan with an index, 1 if without an index.'," + "NO_GOOD_INDEX_USED BIGINT unsigned not null comment '0 if a good index was found for the statement, 1 if no good index was found. See the Range checked for each record description in the EXPLAIN article.'," + "NESTING_EVENT_ID BIGINT unsigned comment 'NULL for top level statements. The parent statement event id for nested statements (stored programs).'," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT') comment 'NULL for top level statements. The parent statement event type for nested statements (stored programs).'," + "NESTING_EVENT_LEVEL INT comment '0 for top level statements. The parent statement level plus 1 for nested statements (stored programs).')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +table_events_statements_common::table_events_statements_common +(const PFS_engine_table_share *share, void *pos) + : PFS_engine_table(share, pos), + m_row_exists(false) +{} + +/** + Build a row. + @param statement the statement the cursor is reading +*/ +void table_events_statements_common::make_row_part_1(PFS_events_statements *statement, + sql_digest_storage *digest) +{ + ulonglong timer_end; + + m_row_exists= false; + + PFS_statement_class *unsafe= (PFS_statement_class*) + statement->m_event.m_class; + PFS_statement_class *klass= sanitize_statement_class(unsafe); + if (unlikely(klass == NULL)) + return; + + m_row.m_thread_internal_id= statement->m_event.m_thread_internal_id; + m_row.m_event_id= statement->m_event.m_event_id; + m_row.m_end_event_id= statement->m_event.m_end_event_id; + m_row.m_nesting_event_id= statement->m_event.m_nesting_event_id; + m_row.m_nesting_event_type= statement->m_event.m_nesting_event_type; + m_row.m_nesting_event_level= statement->m_event.m_nesting_event_level; + + if (m_row.m_end_event_id == 0) + { + timer_end= get_timer_raw_value(statement_timer); + } + else + { + timer_end= statement->m_event.m_timer_end; + } + + m_normalizer->to_pico(statement->m_event.m_timer_start, timer_end, + & m_row.m_timer_start, & m_row.m_timer_end, & m_row.m_timer_wait); + m_row.m_lock_time= statement->m_lock_time * MICROSEC_TO_PICOSEC; + + m_row.m_name= klass->m_name; + m_row.m_name_length= klass->m_name_length; + + CHARSET_INFO *cs= get_charset(statement->m_sqltext_cs_number, MYF(0)); + size_t valid_length= statement->m_sqltext_length; + + if (cs != NULL) + { + if (cs->mbmaxlen > 1) + { + valid_length= Well_formed_prefix(cs, + statement->m_sqltext, + valid_length).length(); + } + } + + m_row.m_sqltext.set_charset(cs); + m_row.m_sqltext.length(0); + m_row.m_sqltext.append(statement->m_sqltext, (uint32)valid_length, cs); + + /* Indicate that sqltext is truncated or not well-formed. */ + if (statement->m_sqltext_truncated || valid_length < statement->m_sqltext_length) + { + size_t chars= m_row.m_sqltext.numchars(); + if (chars > 3) + { + chars-= 3; + uint32 bytes_offset= m_row.m_sqltext.charpos(chars, 0); + m_row.m_sqltext.length(bytes_offset); + m_row.m_sqltext.append("...", 3); + } + } + + m_row.m_current_schema_name_length= statement->m_current_schema_name_length; + if (m_row.m_current_schema_name_length > 0) + memcpy(m_row.m_current_schema_name, statement->m_current_schema_name, m_row.m_current_schema_name_length); + + m_row.m_object_type= statement->m_sp_type; + + m_row.m_schema_name_length= statement->m_schema_name_length; + if (m_row.m_schema_name_length > 0) + memcpy(m_row.m_schema_name, statement->m_schema_name, m_row.m_schema_name_length); + + m_row.m_object_name_length= statement->m_object_name_length; + if (m_row.m_object_name_length > 0) + memcpy(m_row.m_object_name, statement->m_object_name, m_row.m_object_name_length); + + /* Disable source file and line to avoid stale __FILE__ pointers. */ + m_row.m_source_length= 0; + + memcpy(m_row.m_message_text, statement->m_message_text, sizeof(m_row.m_message_text)); + m_row.m_sql_errno= statement->m_sql_errno; + memcpy(m_row.m_sqlstate, statement->m_sqlstate, SQLSTATE_LENGTH); + m_row.m_error_count= statement->m_error_count; + m_row.m_warning_count= statement->m_warning_count; + m_row.m_rows_affected= statement->m_rows_affected; + + m_row.m_rows_sent= statement->m_rows_sent; + m_row.m_rows_examined= statement->m_rows_examined; + m_row.m_created_tmp_disk_tables= statement->m_created_tmp_disk_tables; + m_row.m_created_tmp_tables= statement->m_created_tmp_tables; + m_row.m_select_full_join= statement->m_select_full_join; + m_row.m_select_full_range_join= statement->m_select_full_range_join; + m_row.m_select_range= statement->m_select_range; + m_row.m_select_range_check= statement->m_select_range_check; + m_row.m_select_scan= statement->m_select_scan; + m_row.m_sort_merge_passes= statement->m_sort_merge_passes; + m_row.m_sort_range= statement->m_sort_range; + m_row.m_sort_rows= statement->m_sort_rows; + m_row.m_sort_scan= statement->m_sort_scan; + m_row.m_no_index_used= statement->m_no_index_used; + m_row.m_no_good_index_used= statement->m_no_good_index_used; + + /* + Making a copy of digest storage. + */ + digest->copy(& statement->m_digest_storage); + + m_row_exists= true; + return; +} + +void table_events_statements_common::make_row_part_2(const sql_digest_storage *digest) +{ + /* + Filling up statement digest information. + */ + size_t safe_byte_count= digest->m_byte_count; + if (safe_byte_count > 0 && + safe_byte_count <= pfs_max_digest_length) + { + /* Generate the DIGEST string from the MD5 digest */ + MD5_HASH_TO_STRING(digest->m_md5, + m_row.m_digest.m_digest); + m_row.m_digest.m_digest_length= MD5_HASH_TO_STRING_LENGTH; + + /* Generate the DIGEST_TEXT string from the token array */ + compute_digest_text(digest, &m_row.m_digest.m_digest_text); + + if (m_row.m_digest.m_digest_text.length() == 0) + m_row.m_digest.m_digest_length= 0; + } + else + { + m_row.m_digest.m_digest_length= 0; + m_row.m_digest.m_digest_text.length(0); + } + + return; +} + +int table_events_statements_common::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + uint len; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 3); + buf[0]= 0; + buf[1]= 0; + buf[2]= 0; + + 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: /* LOCK_TIME */ + if (m_row.m_lock_time != 0) + set_field_ulonglong(f, m_row.m_lock_time); + else + f->set_null(); + break; + case 9: /* SQL_TEXT */ + if (m_row.m_sqltext.length()) + set_field_longtext_utf8(f, m_row.m_sqltext.ptr(), m_row.m_sqltext.length()); + else + f->set_null(); + break; + case 10: /* DIGEST */ + if (m_row.m_digest.m_digest_length > 0) + set_field_varchar_utf8(f, m_row.m_digest.m_digest, + m_row.m_digest.m_digest_length); + else + f->set_null(); + break; + case 11: /* DIGEST_TEXT */ + if (m_row.m_digest.m_digest_text.length() > 0) + set_field_longtext_utf8(f, m_row.m_digest.m_digest_text.ptr(), + m_row.m_digest.m_digest_text.length()); + else + f->set_null(); + break; + case 12: /* CURRENT_SCHEMA */ + if (m_row.m_current_schema_name_length) + set_field_varchar_utf8(f, m_row.m_current_schema_name, + m_row.m_current_schema_name_length); + else + f->set_null(); + break; + case 13: /* OBJECT_TYPE */ + if (m_row.m_object_name_length > 0) + set_field_object_type(f, m_row.m_object_type); + else + f->set_null(); + break; + case 14: /* OBJECT_SCHEMA */ + if (m_row.m_schema_name_length) + set_field_varchar_utf8(f, m_row.m_schema_name, + m_row.m_schema_name_length); + else + f->set_null(); + break; + case 15: /* OBJECT_NAME */ + if (m_row.m_object_name_length) + set_field_varchar_utf8(f, m_row.m_object_name, + m_row.m_object_name_length); + else + f->set_null(); + break; + case 16: /* OBJECT_INSTANCE_BEGIN */ + f->set_null(); + break; + case 17: /* MYSQL_ERRNO */ + set_field_ulong(f, m_row.m_sql_errno); + break; + case 18: /* RETURNED_SQLSTATE */ + if (m_row.m_sqlstate[0] != 0) + set_field_varchar_utf8(f, m_row.m_sqlstate, SQLSTATE_LENGTH); + else + f->set_null(); + break; + case 19: /* MESSAGE_TEXT */ + len= static_cast<uint>(strlen(m_row.m_message_text)); + if (len) + set_field_varchar_utf8(f, m_row.m_message_text, len); + else + f->set_null(); + break; + case 20: /* ERRORS */ + set_field_ulonglong(f, m_row.m_error_count); + break; + case 21: /* WARNINGS */ + set_field_ulonglong(f, m_row.m_warning_count); + break; + case 22: /* ROWS_AFFECTED */ + set_field_ulonglong(f, m_row.m_rows_affected); + break; + case 23: /* ROWS_SENT */ + set_field_ulonglong(f, m_row.m_rows_sent); + break; + case 24: /* ROWS_EXAMINED */ + set_field_ulonglong(f, m_row.m_rows_examined); + break; + case 25: /* CREATED_TMP_DISK_TABLES */ + set_field_ulonglong(f, m_row.m_created_tmp_disk_tables); + break; + case 26: /* CREATED_TMP_TABLES */ + set_field_ulonglong(f, m_row.m_created_tmp_tables); + break; + case 27: /* SELECT_FULL_JOIN */ + set_field_ulonglong(f, m_row.m_select_full_join); + break; + case 28: /* SELECT_FULL_RANGE_JOIN */ + set_field_ulonglong(f, m_row.m_select_full_range_join); + break; + case 29: /* SELECT_RANGE */ + set_field_ulonglong(f, m_row.m_select_range); + break; + case 30: /* SELECT_RANGE_CHECK */ + set_field_ulonglong(f, m_row.m_select_range_check); + break; + case 31: /* SELECT_SCAN */ + set_field_ulonglong(f, m_row.m_select_scan); + break; + case 32: /* SORT_MERGE_PASSES */ + set_field_ulonglong(f, m_row.m_sort_merge_passes); + break; + case 33: /* SORT_RANGE */ + set_field_ulonglong(f, m_row.m_sort_range); + break; + case 34: /* SORT_ROWS */ + set_field_ulonglong(f, m_row.m_sort_rows); + break; + case 35: /* SORT_SCAN */ + set_field_ulonglong(f, m_row.m_sort_scan); + break; + case 36: /* NO_INDEX_USED */ + set_field_ulonglong(f, m_row.m_no_index_used); + break; + case 37: /* NO_GOOD_INDEX_USED */ + set_field_ulonglong(f, m_row.m_no_good_index_used); + break; + case 38: /* 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 39: /* 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 40: /* NESTING_EVENT_LEVEL */ + set_field_ulong(f, m_row.m_nesting_event_level); + break; + default: + assert(false); + } + } + } + return 0; +} + +PFS_engine_table* table_events_statements_current::create(void) +{ + return new table_events_statements_current(); +} + +table_events_statements_current::table_events_statements_current() + : table_events_statements_common(&m_share, &m_pos), + m_pos(), m_next_pos() +{} + +void table_events_statements_current::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_events_statements_current::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(statement_timer); + return 0; +} + +int table_events_statements_current::rnd_next(void) +{ + PFS_thread *pfs_thread; + PFS_events_statements *statement; + bool has_more_thread= true; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + pfs_thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread); + if (pfs_thread != NULL) + { + uint safe_events_statements_count= pfs_thread->m_events_statements_count; + + if (safe_events_statements_count == 0) + { + /* Display the last top level statement, when completed */ + if (m_pos.m_index_2 >= 1) + continue; + } + else + { + /* Display all pending statements, when in progress */ + if (m_pos.m_index_2 >= safe_events_statements_count) + continue; + } + + statement= &pfs_thread->m_statement_stack[m_pos.m_index_2]; + + make_row(pfs_thread, statement); + m_next_pos.set_after(&m_pos); + return 0; + } + } + + return HA_ERR_END_OF_FILE; +} + +int table_events_statements_current::rnd_pos(const void *pos) +{ + PFS_thread *pfs_thread; + PFS_events_statements *statement; + + set_position(pos); + + pfs_thread= global_thread_container.get(m_pos.m_index_1); + if (pfs_thread != NULL) + { + uint safe_events_statements_count= pfs_thread->m_events_statements_count; + + if (safe_events_statements_count == 0) + { + /* Display the last top level statement, when completed */ + if (m_pos.m_index_2 >= 1) + return HA_ERR_RECORD_DELETED; + } + else + { + /* Display all pending statements, when in progress */ + if (m_pos.m_index_2 >= safe_events_statements_count) + return HA_ERR_RECORD_DELETED; + } + + assert(m_pos.m_index_2 < statement_stack_max); + + statement= &pfs_thread->m_statement_stack[m_pos.m_index_2]; + + if (statement->m_event.m_class) + { + make_row(pfs_thread, statement); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_events_statements_current::make_row(PFS_thread *pfs_thread, + PFS_events_statements *statement) +{ + sql_digest_storage digest; + pfs_optimistic_state lock; + pfs_optimistic_state stmt_lock; + + digest.reset(m_token_array, MAX_DIGEST_STORAGE_SIZE); + /* Protect this reader against thread termination. */ + pfs_thread->m_lock.begin_optimistic_lock(&lock); + /* Protect this reader against writing on statement information. */ + pfs_thread->m_stmt_lock.begin_optimistic_lock(&stmt_lock); + + table_events_statements_common::make_row_part_1(statement, &digest); + + if (!pfs_thread->m_stmt_lock.end_optimistic_lock(&stmt_lock) || + !pfs_thread->m_lock.end_optimistic_lock(&lock)) + { + m_row_exists= false; + return; + } + table_events_statements_common::make_row_part_2(&digest); + return; +} + +int table_events_statements_current::delete_all_rows(void) +{ + reset_events_statements_current(); + return 0; +} + +ha_rows +table_events_statements_current::get_row_count(void) +{ + return global_thread_container.get_row_count() * statement_stack_max; +} + +PFS_engine_table* table_events_statements_history::create(void) +{ + return new table_events_statements_history(); +} + +table_events_statements_history::table_events_statements_history() + : table_events_statements_common(&m_share, &m_pos), + m_pos(), m_next_pos() +{} + +void table_events_statements_history::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_events_statements_history::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(statement_timer); + return 0; +} + +int table_events_statements_history::rnd_next(void) +{ + PFS_thread *pfs_thread; + PFS_events_statements *statement; + bool has_more_thread= true; + + if (events_statements_history_per_thread == 0) + return HA_ERR_END_OF_FILE; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + pfs_thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread); + if (pfs_thread != NULL) + { + if (m_pos.m_index_2 >= events_statements_history_per_thread) + { + /* This thread does not have more (full) history */ + continue; + } + + if ( ! pfs_thread->m_statements_history_full && + (m_pos.m_index_2 >= pfs_thread->m_statements_history_index)) + { + /* This thread does not have more (not full) history */ + continue; + } + + statement= &pfs_thread->m_statements_history[m_pos.m_index_2]; + + if (statement->m_event.m_class) + { + make_row(pfs_thread, statement); + /* 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_statements_history::rnd_pos(const void *pos) +{ + PFS_thread *pfs_thread; + PFS_events_statements *statement; + + assert(events_statements_history_per_thread != 0); + set_position(pos); + + pfs_thread= global_thread_container.get(m_pos.m_index_1); + if (pfs_thread != NULL) + { + assert(m_pos.m_index_2 < events_statements_history_per_thread); + + if ( ! pfs_thread->m_statements_history_full && + (m_pos.m_index_2 >= pfs_thread->m_statements_history_index)) + return HA_ERR_RECORD_DELETED; + + statement= &pfs_thread->m_statements_history[m_pos.m_index_2]; + if (statement->m_event.m_class) + { + make_row(pfs_thread, statement); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_events_statements_history::make_row(PFS_thread *pfs_thread, + PFS_events_statements *statement) +{ + sql_digest_storage digest; + pfs_optimistic_state lock; + + digest.reset(m_token_array, MAX_DIGEST_STORAGE_SIZE); + /* Protect this reader against thread termination. */ + pfs_thread->m_lock.begin_optimistic_lock(&lock); + + table_events_statements_common::make_row_part_1(statement, &digest); + + if (!pfs_thread->m_lock.end_optimistic_lock(&lock)) + { + m_row_exists= false; + return; + } + table_events_statements_common::make_row_part_2(&digest); + return; +} + + +int table_events_statements_history::delete_all_rows(void) +{ + reset_events_statements_history(); + return 0; +} + +ha_rows +table_events_statements_history::get_row_count(void) +{ + return events_statements_history_per_thread * global_thread_container.get_row_count(); +} + +PFS_engine_table* table_events_statements_history_long::create(void) +{ + return new table_events_statements_history_long(); +} + +table_events_statements_history_long::table_events_statements_history_long() + : table_events_statements_common(&m_share, &m_pos), + m_pos(0), m_next_pos(0) +{} + +void table_events_statements_history_long::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_events_statements_history_long::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(statement_timer); + return 0; +} + +int table_events_statements_history_long::rnd_next(void) +{ + PFS_events_statements *statement; + uint limit; + + if (events_statements_history_long_size == 0) + return HA_ERR_END_OF_FILE; + + if (events_statements_history_long_full) + limit= static_cast<uint>(events_statements_history_long_size); + else + limit= events_statements_history_long_index.m_u32 % events_statements_history_long_size; + + for (m_pos.set_at(&m_next_pos); m_pos.m_index < limit; m_pos.next()) + { + statement= &events_statements_history_long_array[m_pos.m_index]; + + if (statement->m_event.m_class) + { + make_row(statement); + /* 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_statements_history_long::rnd_pos(const void *pos) +{ + PFS_events_statements *statement; + uint limit; + + if (events_statements_history_long_size == 0) + return HA_ERR_RECORD_DELETED; + + set_position(pos); + + if (events_statements_history_long_full) + limit= static_cast<uint>(events_statements_history_long_size); + else + limit= events_statements_history_long_index.m_u32 % events_statements_history_long_size; + + if (m_pos.m_index >= limit) + return HA_ERR_RECORD_DELETED; + + statement= &events_statements_history_long_array[m_pos.m_index]; + + if (!statement->m_event.m_class) + return HA_ERR_RECORD_DELETED; + + make_row(statement); + return 0; +} + +void table_events_statements_history_long::make_row(PFS_events_statements *statement) +{ + sql_digest_storage digest; + + digest.reset(m_token_array, MAX_DIGEST_STORAGE_SIZE); + table_events_statements_common::make_row_part_1(statement, &digest); + + table_events_statements_common::make_row_part_2(&digest); + return; +} + +int table_events_statements_history_long::delete_all_rows(void) +{ + reset_events_statements_history_long(); + return 0; +} + +ha_rows +table_events_statements_history_long::get_row_count(void) +{ + return events_statements_history_long_size; +} + diff --git a/storage/perfschema/table_events_statements.h b/storage/perfschema/table_events_statements.h new file mode 100644 index 00000000..6acad7bb --- /dev/null +++ b/storage/perfschema/table_events_statements.h @@ -0,0 +1,313 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_EVENTS_STATEMENTS_H +#define TABLE_EVENTS_STATEMENTS_H + +/** + @file storage/perfschema/table_events_statements.h + Table EVENTS_STATEMENTS_xxx (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_events_statements.h" +#include "table_helper.h" + +struct PFS_thread; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of table_events_statements_common. */ +struct row_events_statements +{ + /** Column THREAD_ID. */ + ulonglong m_thread_internal_id; + /** Column EVENT_ID. */ + ulonglong m_event_id; + /** Column END_EVENT_ID. */ + ulonglong m_end_event_id; + /** Column NESTING_EVENT_ID. */ + ulonglong m_nesting_event_id; + /** Column NESTING_EVENT_TYPE. */ + enum_event_type m_nesting_event_type; + /** Column NESTING_EVENT_LEVEL. */ + uint m_nesting_event_level; + /** Column EVENT_NAME. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** Column TIMER_START. */ + ulonglong m_timer_start; + /** Column TIMER_END. */ + ulonglong m_timer_end; + /** Column TIMER_WAIT. */ + ulonglong m_timer_wait; + /** Column LOCK_TIME. */ + ulonglong m_lock_time; + /** Column SOURCE. */ + char m_source[COL_SOURCE_SIZE]; + /** Length in bytes of @c m_source. */ + uint m_source_length; + /** Column SQL_TEXT. */ + String m_sqltext; + /** Column DIGEST and DIGEST_TEXT. */ + PFS_digest_row m_digest; + /** Column CURRENT_SCHEMA. */ + char m_current_schema_name[NAME_LEN]; + /** Length in bytes of @c m_current_schema_name. */ + uint m_current_schema_name_length; + + /** Column OBJECT_TYPE. */ + enum_object_type m_object_type; + /** Column OBJECT_SCHEMA. */ + char m_schema_name[NAME_LEN]; + /** Length in bytes of @c m_schema_name. */ + uint m_schema_name_length; + /** Column OBJECT_NAME. */ + char m_object_name[NAME_LEN]; + /** Length in bytes of @c m_object_name. */ + uint m_object_name_length; + + + /** Column MESSAGE_TEXT. */ + char m_message_text[MYSQL_ERRMSG_SIZE+1]; + /** Column MYSQL_ERRNO. */ + uint m_sql_errno; + /** Column RETURNED_SQLSTATE. */ + char m_sqlstate[SQLSTATE_LENGTH]; + /** Column ERRORS. */ + uint m_error_count; + /** Column WARNINGS. */ + uint m_warning_count; + /** Column ROWS_AFFECTED. */ + ulonglong m_rows_affected; + /** Column ROWS_SENT. */ + ulonglong m_rows_sent; + /** Column ROWS_EXAMINED. */ + ulonglong m_rows_examined; + /** Column CREATED_TMP_DISK_TABLES. */ + ulonglong m_created_tmp_disk_tables; + /** Column CREATED_TMP_TABLES. */ + ulonglong m_created_tmp_tables; + /** Column SELECT_FULL_JOIN. */ + ulonglong m_select_full_join; + /** Column SELECT_FULL_RANGE_JOIN. */ + ulonglong m_select_full_range_join; + /** Column SELECT_RANGE. */ + ulonglong m_select_range; + /** Column SELECT_RANGE_CHECK. */ + ulonglong m_select_range_check; + /** Column SELECT_SCAN. */ + ulonglong m_select_scan; + /** Column SORT_MERGE_PASSES. */ + ulonglong m_sort_merge_passes; + /** Column SORT_RANGE. */ + ulonglong m_sort_range; + /** Column SORT_ROWS. */ + ulonglong m_sort_rows; + /** Column SORT_SCAN. */ + ulonglong m_sort_scan; + /** Column NO_INDEX_USED. */ + ulonglong m_no_index_used; + /** Column NO_GOOD_INDEX_USED. */ + ulonglong m_no_good_index_used; +}; + +/** Position of a cursor on PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_CURRENT. */ +struct pos_events_statements_current : public PFS_double_index +{ + pos_events_statements_current() + : PFS_double_index(0, 0) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** Position of a cursor on PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_HISTORY. */ +struct pos_events_statements_history : public PFS_double_index +{ + pos_events_statements_history() + : PFS_double_index(0, 0) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** + Adapter, for table sharing the structure of + PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_CURRENT. +*/ +class table_events_statements_common : public PFS_engine_table +{ +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_events_statements_common(const PFS_engine_table_share *share, void *pos); + + ~table_events_statements_common() = default; + + void make_row_part_1(PFS_events_statements *statement, + sql_digest_storage *digest); + + void make_row_part_2(const sql_digest_storage *digest); + + /** Current row. */ + row_events_statements m_row; + /** True if the current row exists. */ + bool m_row_exists; + unsigned char m_token_array[MAX_DIGEST_STORAGE_SIZE]; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_CURRENT. */ +class table_events_statements_current : public table_events_statements_common +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + table_events_statements_current(); + +public: + ~table_events_statements_current() = default; + +private: + friend class table_events_statements_history; + friend class table_events_statements_history_long; + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + void make_row(PFS_thread* pfs_thread, PFS_events_statements *statement); + + /** Current position. */ + pos_events_statements_current m_pos; + /** Next position. */ + pos_events_statements_current m_next_pos; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_HISTORY. */ +class table_events_statements_history : public table_events_statements_common +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + table_events_statements_history(); + +public: + ~table_events_statements_history() = default; + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + void make_row(PFS_thread* pfs_thread, PFS_events_statements *statement); + + /** Current position. */ + pos_events_statements_history m_pos; + /** Next position. */ + pos_events_statements_history m_next_pos; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_HISTORY_LONG. */ +class table_events_statements_history_long : public table_events_statements_common +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + table_events_statements_history_long(); + +public: + ~table_events_statements_history_long() = default; + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + void make_row(PFS_events_statements *statement); + + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_events_transactions.cc b/storage/perfschema/table_events_transactions.cc new file mode 100644 index 00000000..0dea61cb --- /dev/null +++ b/storage/perfschema/table_events_transactions.cc @@ -0,0 +1,739 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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_transactions.cc + Table EVENTS_TRANSACTIONS_xxx (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "table_events_transactions.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_events_transactions.h" +#include "pfs_timer.h" +#include "table_helper.h" +#include "pfs_buffer_container.h" +#include "field.h" +//#include "xa.h" + +THR_LOCK table_events_transactions_current::m_table_lock; + +PFS_engine_table_share_state +table_events_transactions_current::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_events_transactions_current::m_share= +{ + { C_STRING_WITH_LEN("events_transactions_current") }, + &pfs_truncatable_acl, + table_events_transactions_current::create, + NULL, /* write_row */ + table_events_transactions_current::delete_all_rows, + table_events_transactions_current::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_transactions_current(" + "THREAD_ID BIGINT unsigned not null comment 'The thread associated with the event.'," + "EVENT_ID BIGINT unsigned not null comment 'The event id associated with the event.'," + "END_EVENT_ID BIGINT unsigned comment 'This column is set to NULL when the event starts and updated to the thread current event number when the event ends.'," + "EVENT_NAME VARCHAR(128) not null comment 'The name of the instrument from which the event was collected. This is a NAME value from the setup_instruments table.'," + "STATE ENUM('ACTIVE', 'COMMITTED', 'ROLLED BACK') comment 'The current transaction state. The value is ACTIVE (after START TRANSACTION or BEGIN), COMMITTED (after COMMIT), or ROLLED BACK (after ROLLBACK).'," + "TRX_ID BIGINT unsigned comment 'Unused.'," + "GTID VARCHAR(64) comment 'Transaction GTID, using the format DOMAIN-SERVER_ID-SEQUENCE_NO.'," + "XID_FORMAT_ID INTEGER comment 'XA transaction format ID for GTRID and BQUAL values.'," + "XID_GTRID VARCHAR(130) comment 'XA global transaction ID.'," + "XID_BQUAL VARCHAR(130) comment 'XA transaction branch qualifier.'," + "XA_STATE VARCHAR(64) comment 'The state of the XA transaction. The value is ACTIVE (after XA START), IDLE (after XA END), PREPARED (after XA PREPARE), ROLLED BACK (after XA ROLLBACK), or COMMITTED (after XA COMMIT).'," + "SOURCE VARCHAR(64) comment 'The name of the source file containing the instrumented code that produced the event and the line number in the file at which the instrumentation occurs.'," + "TIMER_START BIGINT unsigned comment 'The unit is picoseconds. When event timing started. NULL if event has no timing information.'," + "TIMER_END BIGINT unsigned comment 'The unit is picoseconds. When event timing ended. NULL if event has no timing information.'," + "TIMER_WAIT BIGINT unsigned comment 'The unit is picoseconds. Event duration. NULL if event has not timing information.'," + "ACCESS_MODE ENUM('READ ONLY', 'READ WRITE') comment 'Transaction access mode.'," + "ISOLATION_LEVEL VARCHAR(64) comment 'Transaction isolation level. One of: REPEATABLE READ, READ COMMITTED, READ UNCOMMITTED, or SERIALIZABLE.'," + "AUTOCOMMIT ENUM('YES','NO') not null comment 'Whether autcommit mode was enabled when the transaction started.'," + "NUMBER_OF_SAVEPOINTS BIGINT unsigned comment 'The number of SAVEPOINT statements issued during the transaction.'," + "NUMBER_OF_ROLLBACK_TO_SAVEPOINT BIGINT unsigned comment 'The number of ROLLBACK_TO_SAVEPOINT statements issued during the transaction.'," + "NUMBER_OF_RELEASE_SAVEPOINT BIGINT unsigned comment 'The number of RELEASE_SAVEPOINT statements issued during the transaction.'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned comment 'Unused.'," + "NESTING_EVENT_ID BIGINT unsigned comment 'The EVENT_ID value of the event within which this event is nested.'," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT') comment 'The nesting event type.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +THR_LOCK table_events_transactions_history::m_table_lock; + +PFS_engine_table_share_state +table_events_transactions_history::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_events_transactions_history::m_share= +{ + { C_STRING_WITH_LEN("events_transactions_history") }, + &pfs_truncatable_acl, + table_events_transactions_history::create, + NULL, /* write_row */ + table_events_transactions_history::delete_all_rows, + table_events_transactions_history::get_row_count, + sizeof(pos_events_transactions_history), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_transactions_history(" + "THREAD_ID BIGINT unsigned not null comment 'The thread associated with the event.'," + "EVENT_ID BIGINT unsigned not null comment 'The event id associated with the event.'," + "END_EVENT_ID BIGINT unsigned comment 'This column is set to NULL when the event starts and updated to the thread current event number when the event ends.'," + "EVENT_NAME VARCHAR(128) not null comment 'The name of the instrument from which the event was collected. This is a NAME value from the setup_instruments table.'," + "STATE ENUM('ACTIVE', 'COMMITTED', 'ROLLED BACK') comment 'The current transaction state. The value is ACTIVE (after START TRANSACTION or BEGIN), COMMITTED (after COMMIT), or ROLLED BACK (after ROLLBACK).'," + "TRX_ID BIGINT unsigned comment 'Unused.'," + "GTID VARCHAR(64) comment 'Transaction GTID, using the format DOMAIN-SERVER_ID-SEQUENCE_NO.'," + "XID_FORMAT_ID INTEGER comment 'XA transaction format ID for GTRID and BQUAL values.'," + "XID_GTRID VARCHAR(130) comment 'XA global transaction ID.'," + "XID_BQUAL VARCHAR(130) comment 'XA transaction branch qualifier.'," + "XA_STATE VARCHAR(64) comment 'The state of the XA transaction. The value is ACTIVE (after XA START), IDLE (after XA END), PREPARED (after XA PREPARE), ROLLED BACK (after XA ROLLBACK), or COMMITTED (after XA COMMIT).'," + "SOURCE VARCHAR(64) comment 'The name of the source file containing the instrumented code that produced the event and the line number in the file at which the instrumentation occurs.'," + "TIMER_START BIGINT unsigned comment 'The unit is picoseconds. When event timing started. NULL if event has no timing information.'," + "TIMER_END BIGINT unsigned comment 'The unit is picoseconds. When event timing ended. NULL if event has no timing information.'," + "TIMER_WAIT BIGINT unsigned comment 'The unit is picoseconds. Event duration. NULL if event has not timing information.'," + "ACCESS_MODE ENUM('READ ONLY', 'READ WRITE') comment 'Transaction access mode.'," + "ISOLATION_LEVEL VARCHAR(64) comment 'Transaction isolation level. One of: REPEATABLE READ, READ COMMITTED, READ UNCOMMITTED, or SERIALIZABLE.'," + "AUTOCOMMIT ENUM('YES','NO') not null comment 'Whether autcommit mode was enabled when the transaction started.'," + "NUMBER_OF_SAVEPOINTS BIGINT unsigned comment 'The number of SAVEPOINT statements issued during the transaction.'," + "NUMBER_OF_ROLLBACK_TO_SAVEPOINT BIGINT unsigned comment 'The number of ROLLBACK_TO_SAVEPOINT statements issued during the transaction.'," + "NUMBER_OF_RELEASE_SAVEPOINT BIGINT unsigned comment 'The number of RELEASE_SAVEPOINT statements issued during the transaction.'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned comment 'Unused.'," + "NESTING_EVENT_ID BIGINT unsigned comment 'The EVENT_ID value of the event within which this event is nested.'," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT') comment 'The nesting event type.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +THR_LOCK table_events_transactions_history_long::m_table_lock; + +PFS_engine_table_share_state +table_events_transactions_history_long::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_events_transactions_history_long::m_share= +{ + { C_STRING_WITH_LEN("events_transactions_history_long") }, + &pfs_truncatable_acl, + table_events_transactions_history_long::create, + NULL, /* write_row */ + table_events_transactions_history_long::delete_all_rows, + table_events_transactions_history_long::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_transactions_history_long(" + "THREAD_ID BIGINT unsigned not null comment 'The thread associated with the event.'," + "EVENT_ID BIGINT unsigned not null comment 'The event id associated with the event.'," + "END_EVENT_ID BIGINT unsigned comment 'This column is set to NULL when the event starts and updated to the thread current event number when the event ends.'," + "EVENT_NAME VARCHAR(128) not null comment 'The name of the instrument from which the event was collected. This is a NAME value from the setup_instruments table.'," + "STATE ENUM('ACTIVE', 'COMMITTED', 'ROLLED BACK') comment 'The current transaction state. The value is ACTIVE (after START TRANSACTION or BEGIN), COMMITTED (after COMMIT), or ROLLED BACK (after ROLLBACK).'," + "TRX_ID BIGINT unsigned comment 'Unused.'," + "GTID VARCHAR(64) comment 'Transaction GTID, using the format DOMAIN-SERVER_ID-SEQUENCE_NO.'," + "XID_FORMAT_ID INTEGER comment 'XA transaction format ID for GTRID and BQUAL values.'," + "XID_GTRID VARCHAR(130) comment 'XA global transaction ID.'," + "XID_BQUAL VARCHAR(130) comment 'XA transaction branch qualifier.'," + "XA_STATE VARCHAR(64) comment 'The state of the XA transaction. The value is ACTIVE (after XA START), IDLE (after XA END), PREPARED (after XA PREPARE), ROLLED BACK (after XA ROLLBACK), or COMMITTED (after XA COMMIT).'," + "SOURCE VARCHAR(64) comment 'The name of the source file containing the instrumented code that produced the event and the line number in the file at which the instrumentation occurs.'," + "TIMER_START BIGINT unsigned comment 'The unit is picoseconds. When event timing started. NULL if event has no timing information.'," + "TIMER_END BIGINT unsigned comment 'The unit is picoseconds. When event timing ended. NULL if event has no timing information.'," + "TIMER_WAIT BIGINT unsigned comment 'The unit is picoseconds. Event duration. NULL if event has not timing information.'," + "ACCESS_MODE ENUM('READ ONLY', 'READ WRITE') comment 'Transaction access mode.'," + "ISOLATION_LEVEL VARCHAR(64) comment 'Transaction isolation level. One of: REPEATABLE READ, READ COMMITTED, READ UNCOMMITTED, or SERIALIZABLE.'," + "AUTOCOMMIT ENUM('YES','NO') not null comment 'Whether autcommit mode was enabled when the transaction started.'," + "NUMBER_OF_SAVEPOINTS BIGINT unsigned comment 'The number of SAVEPOINT statements issued during the transaction.'," + "NUMBER_OF_ROLLBACK_TO_SAVEPOINT BIGINT unsigned comment 'The number of ROLLBACK_TO_SAVEPOINT statements issued during the transaction.'," + "NUMBER_OF_RELEASE_SAVEPOINT BIGINT unsigned comment 'The number of RELEASE_SAVEPOINT statements issued during the transaction.'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned comment 'Unused.'," + "NESTING_EVENT_ID BIGINT unsigned comment 'The EVENT_ID value of the event within which this event is nested.'," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT') comment 'The nesting event type.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +table_events_transactions_common::table_events_transactions_common +(const PFS_engine_table_share *share, void *pos) + : PFS_engine_table(share, pos), + m_row_exists(false) +{} + +/** + Build a row. + @param transaction the transaction the cursor is reading +*/ +void table_events_transactions_common::make_row(PFS_events_transactions *transaction) +{ + ulonglong timer_end; + + m_row_exists= false; + + PFS_transaction_class *unsafe= (PFS_transaction_class*) transaction->m_class; + PFS_transaction_class *klass= sanitize_transaction_class(unsafe); + if (unlikely(klass == NULL)) + return; + + m_row.m_thread_internal_id= transaction->m_thread_internal_id; + m_row.m_event_id= transaction->m_event_id; + m_row.m_end_event_id= transaction->m_end_event_id; + m_row.m_nesting_event_id= transaction->m_nesting_event_id; + m_row.m_nesting_event_type= transaction->m_nesting_event_type; + + if (m_row.m_end_event_id == 0) + { + timer_end= get_timer_raw_value(transaction_timer); + } + else + { + timer_end= transaction->m_timer_end; + } + + m_normalizer->to_pico(transaction->m_timer_start, timer_end, + &m_row.m_timer_start, &m_row.m_timer_end, &m_row.m_timer_wait); + m_row.m_name= klass->m_name; + m_row.m_name_length= klass->m_name_length; + + /* Disable source file and line to avoid stale __FILE__ pointers. */ + m_row.m_source_length= 0; + + /* A GTID consists of the SID (source id) and GNO (transaction number). + The SID is stored in transaction->m_sid and the GNO is stored in + transaction->m_gtid_spec.gno. + + On a master, the GTID is assigned when the transaction commit. + On a slave, the GTID is assigned before the transaction starts. + If GTID_MODE = OFF, all transactions have the special GTID + 'ANONYMOUS'. + + Therefore, a transaction can be in three different states wrt GTIDs: + - Before the GTID has been assigned, the state is 'AUTOMATIC'. + On a master, this is the state until the transaction commits. + On a slave, this state does not appear. + - If GTID_MODE = ON, and a GTID is assigned, the GTID is a string + of the form 'UUID:NUMBER'. + - If GTID_MODE = OFF, and a GTID is assigned, the GTID is a string + of the form 'ANONYMOUS'. + + The Gtid_specification contains the GNO, as well as a type code + that specifies which of the three modes is currently in effect. + Given a SID, it can generate the textual representation of the + GTID. + */ + //rpl_sid *sid= &transaction->m_sid; + Gtid_specification *gtid_spec= &transaction->m_gtid_spec; + m_row.m_gtid_length= static_cast<uint>(gtid_spec->to_string(m_row.m_gtid)); + + m_row.m_xid= transaction->m_xid; + m_row.m_isolation_level= transaction->m_isolation_level; + m_row.m_read_only= transaction->m_read_only; + m_row.m_trxid= transaction->m_trxid; + m_row.m_state= transaction->m_state; + m_row.m_xa_state= transaction->m_xa_state; + m_row.m_xa= transaction->m_xa; + m_row.m_autocommit= transaction->m_autocommit; + m_row.m_savepoint_count= transaction->m_savepoint_count; + m_row.m_rollback_to_savepoint_count= transaction->m_rollback_to_savepoint_count; + m_row.m_release_savepoint_count= transaction->m_release_savepoint_count; + m_row_exists= true; + return; +} + +/** Size of XID converted to null-terminated hex string prefixed with 0x. */ +static const ulong XID_BUFFER_SIZE= XIDDATASIZE*2 + 2 + 1; + +/** + Convert the XID to HEX string prefixed by '0x' + + @param[out] buf output hex string buffer, null-terminated + @param buf_len size of buffer, must be at least @c XID_BUFFER_SIZE + @param xid XID structure + @param offset offset into XID.data[] + @param length number of bytes to process + @return number of bytes in hex string +*/ +static size_t xid_to_hex(char *buf, size_t buf_len, PSI_xid *xid, size_t offset, size_t length) +{ + assert(buf_len >= XID_BUFFER_SIZE); + assert(offset + length <= XIDDATASIZE); + *buf++= '0'; + *buf++= 'x'; + return bin_to_hex_str(buf, buf_len-2, (char*)(xid->data + offset), length) + 2; +} + +/** + Store the XID in printable format if possible, otherwise convert + to a string of hex digits. + + @param field Record field + @param xid XID structure + @param offset offset into XID.data[] + @param length number of bytes to process +*/ +static void xid_store(Field *field, PSI_xid *xid, size_t offset, size_t length) +{ + assert(!xid->is_null()); + if (xid_printable(xid, offset, length)) + { + field->store(xid->data + offset, length, &my_charset_bin); + } + else + { + /* + xid_buf contains enough space for 0x followed by hex representation of + the binary XID data and one null termination character. + */ + char xid_buf[XID_BUFFER_SIZE]; + + size_t xid_str_len= xid_to_hex(xid_buf, sizeof(xid_buf), xid, offset, length); + field->store(xid_buf, xid_str_len, &my_charset_bin); + } +} + +static void xid_store_bqual(Field *field, PSI_xid *xid) +{ + xid_store(field, xid, xid->gtrid_length, xid->bqual_length); +} + +static void xid_store_gtrid(Field *field, PSI_xid *xid) +{ + xid_store(field, xid, 0, xid->gtrid_length); +} + +int table_events_transactions_common::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 3); + buf[0]= 0; + buf[1]= 0; + buf[2]= 0; + + 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: /* STATE */ + set_field_enum(f, m_row.m_state); + break; + case 5: /* TRX_ID */ + if (m_row.m_trxid != 0) + set_field_ulonglong(f, m_row.m_trxid); + else + f->set_null(); + break; + case 6: /* GTID */ + set_field_varchar_utf8(f, m_row.m_gtid, m_row.m_gtid_length); + break; + case 7: /* XID_FORMAT_ID */ + if (!m_row.m_xa || m_row.m_xid.is_null()) + f->set_null(); + else + set_field_long(f, m_row.m_xid.formatID); + break; + case 8: /* XID_GTRID */ + if (!m_row.m_xa || m_row.m_xid.is_null() || m_row.m_xid.gtrid_length <= 0) + f->set_null(); + else + xid_store_gtrid(f, &m_row.m_xid); + break; + case 9: /* XID_BQUAL */ + if (!m_row.m_xa || m_row.m_xid.is_null() || m_row.m_xid.bqual_length <= 0) + f->set_null(); + else + xid_store_bqual(f, &m_row.m_xid); + break; + case 10: /* XA STATE */ + if (!m_row.m_xa || m_row.m_xid.is_null()) + f->set_null(); + else + set_field_xa_state(f, m_row.m_xa_state); + break; + case 11: /* SOURCE */ + set_field_varchar_utf8(f, m_row.m_source, m_row.m_source_length); + break; + case 12: /* TIMER_START */ + if (m_row.m_timer_start != 0) + set_field_ulonglong(f, m_row.m_timer_start); + else + f->set_null(); + break; + case 13: /* TIMER_END */ + if (m_row.m_timer_end != 0) + set_field_ulonglong(f, m_row.m_timer_end); + else + f->set_null(); + break; + case 14: /* TIMER_WAIT */ + if (m_row.m_timer_wait != 0) + set_field_ulonglong(f, m_row.m_timer_wait); + else + f->set_null(); + break; + case 15: /* ACCESS_MODE */ + set_field_enum(f, m_row.m_read_only ? TRANS_MODE_READ_ONLY + : TRANS_MODE_READ_WRITE); + break; + case 16: /* ISOLATION_LEVEL */ + set_field_isolation_level(f, m_row.m_isolation_level); + break; + case 17: /* AUTOCOMMIT */ + set_field_enum(f, m_row.m_autocommit ? ENUM_YES : ENUM_NO); + break; + case 18: /* NUMBER_OF_SAVEPOINTS */ + set_field_ulonglong(f, m_row.m_savepoint_count); + break; + case 19: /* NUMBER_OF_ROLLBACK_TO_SAVEPOINT */ + set_field_ulonglong(f, m_row.m_rollback_to_savepoint_count); + break; + case 20: /* NUMBER_OF_RELEASE_SAVEPOINT */ + set_field_ulonglong(f, m_row.m_release_savepoint_count); + break; + case 21: /* OBJECT_INSTANCE_BEGIN */ + f->set_null(); + break; + case 22: /* 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 23: /* 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; + default: + assert(false); + } + } + } + return 0; +} + +PFS_engine_table* table_events_transactions_current::create(void) +{ + return new table_events_transactions_current(); +} + +table_events_transactions_current::table_events_transactions_current() + : table_events_transactions_common(&m_share, &m_pos), + m_pos(0), m_next_pos(0) +{} + +void table_events_transactions_current::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_events_transactions_current::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(transaction_timer); + return 0; +} + +int table_events_transactions_current::rnd_next(void) +{ + PFS_thread *pfs_thread; + PFS_events_transactions *transaction; + bool has_more_thread= true; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next()) + { + pfs_thread= global_thread_container.get(m_pos.m_index, & has_more_thread); + if (pfs_thread != NULL) + { + transaction= &pfs_thread->m_transaction_current; + make_row(transaction); + m_next_pos.set_after(&m_pos); + return 0; + } + } + + return HA_ERR_END_OF_FILE; +} + +int table_events_transactions_current::rnd_pos(const void *pos) +{ + PFS_thread *pfs_thread; + PFS_events_transactions *transaction; + + set_position(pos); + + pfs_thread= global_thread_container.get(m_pos.m_index); + if (pfs_thread != NULL) + { + transaction= &pfs_thread->m_transaction_current; + if (transaction->m_class != NULL) + { + make_row(transaction); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +int table_events_transactions_current::delete_all_rows(void) +{ + reset_events_transactions_current(); + return 0; +} + +ha_rows +table_events_transactions_current::get_row_count(void) +{ + return global_thread_container.get_row_count(); +} + +PFS_engine_table* table_events_transactions_history::create(void) +{ + return new table_events_transactions_history(); +} + +table_events_transactions_history::table_events_transactions_history() + : table_events_transactions_common(&m_share, &m_pos), + m_pos(), m_next_pos() +{} + +void table_events_transactions_history::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_events_transactions_history::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(transaction_timer); + return 0; +} + +int table_events_transactions_history::rnd_next(void) +{ + PFS_thread *pfs_thread; + PFS_events_transactions *transaction; + bool has_more_thread= true; + + if (events_transactions_history_per_thread == 0) + return HA_ERR_END_OF_FILE; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + pfs_thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread); + if (pfs_thread != NULL) + { + if (m_pos.m_index_2 >= events_transactions_history_per_thread) + { + /* This thread does not have more (full) history */ + continue; + } + + if ( ! pfs_thread->m_transactions_history_full && + (m_pos.m_index_2 >= pfs_thread->m_transactions_history_index)) + { + /* This thread does not have more (not full) history */ + continue; + } + + transaction= &pfs_thread->m_transactions_history[m_pos.m_index_2]; + if (transaction->m_class != NULL) + { + make_row(transaction); + /* 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_transactions_history::rnd_pos(const void *pos) +{ + PFS_thread *pfs_thread; + PFS_events_transactions *transaction; + + assert(events_transactions_history_per_thread != 0); + set_position(pos); + + assert(m_pos.m_index_2 < events_transactions_history_per_thread); + + pfs_thread= global_thread_container.get(m_pos.m_index_1); + if (pfs_thread != NULL) + { + if ( ! pfs_thread->m_transactions_history_full && + (m_pos.m_index_2 >= pfs_thread->m_transactions_history_index)) + return HA_ERR_RECORD_DELETED; + + transaction= &pfs_thread->m_transactions_history[m_pos.m_index_2]; + if (transaction->m_class != NULL) + { + make_row(transaction); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +int table_events_transactions_history::delete_all_rows(void) +{ + reset_events_transactions_history(); + return 0; +} + +ha_rows +table_events_transactions_history::get_row_count(void) +{ + return events_transactions_history_per_thread * global_thread_container.get_row_count(); +} + +PFS_engine_table* table_events_transactions_history_long::create(void) +{ + return new table_events_transactions_history_long(); +} + +table_events_transactions_history_long::table_events_transactions_history_long() + : table_events_transactions_common(&m_share, &m_pos), + m_pos(0), m_next_pos(0) +{} + +void table_events_transactions_history_long::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_events_transactions_history_long::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(transaction_timer); + return 0; +} + +int table_events_transactions_history_long::rnd_next(void) +{ + PFS_events_transactions *transaction; + uint limit; + + if (events_transactions_history_long_size == 0) + return HA_ERR_END_OF_FILE; + + if (events_transactions_history_long_full) + limit= events_transactions_history_long_size; + else + limit= events_transactions_history_long_index.m_u32 % events_transactions_history_long_size; + + for (m_pos.set_at(&m_next_pos); m_pos.m_index < limit; m_pos.next()) + { + transaction= &events_transactions_history_long_array[m_pos.m_index]; + + if (transaction->m_class != NULL) + { + make_row(transaction); + /* 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_transactions_history_long::rnd_pos(const void *pos) +{ + PFS_events_transactions *transaction; + uint limit; + + if (events_transactions_history_long_size == 0) + return HA_ERR_RECORD_DELETED; + + set_position(pos); + + if (events_transactions_history_long_full) + limit= events_transactions_history_long_size; + else + limit= events_transactions_history_long_index.m_u32 % events_transactions_history_long_size; + + if (m_pos.m_index >= limit) + return HA_ERR_RECORD_DELETED; + + transaction= &events_transactions_history_long_array[m_pos.m_index]; + + if (transaction->m_class == NULL) + return HA_ERR_RECORD_DELETED; + + make_row(transaction); + return 0; +} + +int table_events_transactions_history_long::delete_all_rows(void) +{ + reset_events_transactions_history_long(); + return 0; +} + +ha_rows +table_events_transactions_history_long::get_row_count(void) +{ + return events_transactions_history_long_size; +} + diff --git a/storage/perfschema/table_events_transactions.h b/storage/perfschema/table_events_transactions.h new file mode 100644 index 00000000..f6024c63 --- /dev/null +++ b/storage/perfschema/table_events_transactions.h @@ -0,0 +1,257 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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 TABLE_EVENTS_TRANSACTIONS_H +#define TABLE_EVENTS_TRANSACTIONS_H + +/** + @file storage/perfschema/table_events_HA_ERR_WRONG_COMMAND.h + Table EVENTS_TRANSACTIONS_xxx (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_events_transactions.h" +#include "table_helper.h" +#include "rpl_gtid.h" + +struct PFS_thread; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of table_events_transactions_common. */ +struct row_events_transactions +{ + /** Column THREAD_ID. */ + ulonglong m_thread_internal_id; + /** Column EVENT_ID. */ + ulonglong m_event_id; + /** Column END_EVENT_ID. */ + ulonglong m_end_event_id; + /** Column NESTING_EVENT_ID. */ + ulonglong m_nesting_event_id; + /** Column NESTING_EVENT_TYPE. */ + enum_event_type m_nesting_event_type; + /** Column EVENT_NAME. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** Column TIMER_START. */ + ulonglong m_timer_start; + /** Column TIMER_END. */ + ulonglong m_timer_end; + /** Column TIMER_WAIT. */ + ulonglong m_timer_wait; + /** Column SOURCE. */ + char m_source[COL_SOURCE_SIZE]; + /** Length in bytes of @c m_source. */ + uint m_source_length; + /** InnoDB transaction id. */ + ulonglong m_trxid; + /** Transaction state. */ + enum_transaction_state m_state; + /** Global Transaction ID. */ + char m_gtid[GTID_MAX_STR_LENGTH + 1]; + /** GTID length in bytes*/ + int m_gtid_length; + /** XA transaction ID. */ + PSI_xid m_xid; + /** XA transaction state. */ + enum_xa_transaction_state m_xa_state; + /** True if XA transaction. */ + bool m_xa; + /** True if autocommit transaction. */ + bool m_autocommit; + /** Isolation level. */ + enum_isolation_level m_isolation_level; + /** True if read-only, read-write otherwise. */ + bool m_read_only; + /** Column NUMBER_OF_SAVEPOINTS. */ + ulonglong m_savepoint_count; + /** Column NUMBER_OF_ROLLBACK_TO_SAVEPOINT. */ + ulonglong m_rollback_to_savepoint_count; + /** Column NUMBER_OF_RELEASE_SAVEPOINT. */ + ulonglong m_release_savepoint_count; +}; + +/** + Position of a cursor on PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_HISTORY. + Index 1 on thread (0 based) + Index 2 on transaction event record in thread history (0 based) +*/ +struct pos_events_transactions_history : public PFS_double_index +{ + pos_events_transactions_history() + : PFS_double_index(0, 0) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** + Adapter, for table sharing the structure of + PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_CURRENT. +*/ +class table_events_transactions_common : public PFS_engine_table +{ +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_events_transactions_common(const PFS_engine_table_share *share, void *pos); + + ~table_events_transactions_common() + {} + + void make_row(PFS_events_transactions *statement); + + /** Current row. */ + row_events_transactions m_row; + /** True if the current row exists. */ + bool m_row_exists; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_CURRENT. */ +class table_events_transactions_current : public table_events_transactions_common +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + table_events_transactions_current(); + +public: + ~table_events_transactions_current() + {} + +private: + friend class table_events_transactions_history; + friend class table_events_transactions_history_long; + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** + Fields definition. + Also used by table_events_transactions_history + and table_events_transactions_history_long. + */ + static TABLE_FIELD_DEF m_field_def; + + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_HISTORY. */ +class table_events_transactions_history : public table_events_transactions_common +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + table_events_transactions_history(); + +public: + ~table_events_transactions_history() + {} + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current position. */ + pos_events_transactions_history m_pos; + /** Next position. */ + pos_events_transactions_history m_next_pos; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_TRANSACTIONS_HISTORY_LONG. */ +class table_events_transactions_history_long : public table_events_transactions_common +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + table_events_transactions_history_long(); + +public: + ~table_events_transactions_history_long() + {} + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_events_waits.cc b/storage/perfschema/table_events_waits.cc new file mode 100644 index 00000000..59b99dab --- /dev/null +++ b/storage/perfschema/table_events_waits.cc @@ -0,0 +1,1164 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_events_waits.cc + Table EVENTS_WAITS_xxx (implementation). +*/ + +#include "my_global.h" +#include "my_thread.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" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_events_waits_current::m_table_lock; + +PFS_engine_table_share_state +table_events_waits_current::m_share_state = { + false /* m_checked */ +}; + +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, + table_events_waits_current::get_row_count, + 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 comment 'Thread associated with the event. Together with EVENT_ID uniquely identifies the row.'," + "EVENT_ID BIGINT unsigned not null comment 'Thread''s current event number at the start of the event. Together with THREAD_ID uniquely identifies the row.'," + "END_EVENT_ID BIGINT unsigned comment 'NULL when the event starts, set to the thread''s current event number at the end of the event.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event instrument name and a NAME from the setup_instruments table'," + "SOURCE VARCHAR(64) comment 'Name and line number of the source file containing the instrumented code that produced the event.'," + "TIMER_START BIGINT unsigned comment 'Value in picoseconds when the event timing started or NULL if timing is not collected.'," + "TIMER_END BIGINT unsigned comment 'Value in picoseconds when the event timing ended, or NULL if the event has not ended or timing is not collected.'," + "TIMER_WAIT BIGINT unsigned comment 'Value in picoseconds of the event''s duration or NULL if the event has not ended or timing is not collected.'," + "SPINS INTEGER unsigned comment 'Number of spin rounds for a mutex, or NULL if spin rounds are not used, or spinning is not instrumented.'," + "OBJECT_SCHEMA VARCHAR(64) comment 'Name of the schema that contains the table for table I/O objects, otherwise NULL for file I/O and synchronization objects.'," + "OBJECT_NAME VARCHAR(512) comment 'File name for file I/O objects, table name for table I/O objects, the socket''s IP:PORT value for a socket object or NULL for a synchronization object.'," + "INDEX_NAME VARCHAR(64) comment 'Name of the index, PRIMARY for the primary key, or NULL for no index used.'," + "OBJECT_TYPE VARCHAR(64) comment 'FILE for a file object, TABLE or TEMPORARY TABLE for a table object, or NULL for a synchronization object.'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null comment 'Address in memory of the object.'," + "NESTING_EVENT_ID BIGINT unsigned comment 'EVENT_ID of event within which this event nests.'," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT') comment 'Nesting event type. Either statement, stage or wait.'," + "OPERATION VARCHAR(32) not null comment 'Operation type, for example read, write or lock'," + "NUMBER_OF_BYTES BIGINT comment 'Number of bytes that the operation read or wrote, or NULL for table I/O waits.'," + "FLAGS INTEGER unsigned comment 'Reserved for use in the future.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +THR_LOCK table_events_waits_history::m_table_lock; + +PFS_engine_table_share_state +table_events_waits_history::m_share_state = { + false /* m_checked */ +}; + +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, + table_events_waits_history::get_row_count, + 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 comment 'Thread associated with the event. Together with EVENT_ID uniquely identifies the row.'," + "EVENT_ID BIGINT unsigned not null comment 'Thread''s current event number at the start of the event. Together with THREAD_ID uniquely identifies the row.'," + "END_EVENT_ID BIGINT unsigned comment 'NULL when the event starts, set to the thread''s current event number at the end of the event.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event instrument name and a NAME from the setup_instruments table'," + "SOURCE VARCHAR(64) comment 'Name and line number of the source file containing the instrumented code that produced the event.'," + "TIMER_START BIGINT unsigned comment 'Value in picoseconds when the event timing started or NULL if timing is not collected.'," + "TIMER_END BIGINT unsigned comment 'Value in picoseconds when the event timing ended, or NULL if the event has not ended or timing is not collected.'," + "TIMER_WAIT BIGINT unsigned comment 'Value in picoseconds of the event''s duration or NULL if the event has not ended or timing is not collected.'," + "SPINS INTEGER unsigned comment 'Number of spin rounds for a mutex, or NULL if spin rounds are not used, or spinning is not instrumented.'," + "OBJECT_SCHEMA VARCHAR(64) comment 'Name of the schema that contains the table for table I/O objects, otherwise NULL for file I/O and synchronization objects.'," + "OBJECT_NAME VARCHAR(512) comment 'File name for file I/O objects, table name for table I/O objects, the socket''s IP:PORT value for a socket object or NULL for a synchronization object.'," + "INDEX_NAME VARCHAR(64) comment 'Name of the index, PRIMARY for the primary key, or NULL for no index used.'," + "OBJECT_TYPE VARCHAR(64) comment 'FILE for a file object, TABLE or TEMPORARY TABLE for a table object, or NULL for a synchronization object.'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null comment 'Address in memory of the object.'," + "NESTING_EVENT_ID BIGINT unsigned comment 'EVENT_ID of event within which this event nests.'," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT') comment 'Nesting event type. Either statement, stage or wait.'," + "OPERATION VARCHAR(32) not null comment 'Operation type, for example read, write or lock'," + "NUMBER_OF_BYTES BIGINT comment 'Number of bytes that the operation read or wrote, or NULL for table I/O waits.'," + "FLAGS INTEGER unsigned comment 'Reserved for use in the future.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +THR_LOCK table_events_waits_history_long::m_table_lock; + +PFS_engine_table_share_state +table_events_waits_history_long::m_share_state = { + false /* m_checked */ +}; + +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, + table_events_waits_history_long::get_row_count, + 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 comment 'Thread associated with the event. Together with EVENT_ID uniquely identifies the row.'," + "EVENT_ID BIGINT unsigned not null comment 'Thread''s current event number at the start of the event. Together with THREAD_ID uniquely identifies the row.'," + "END_EVENT_ID BIGINT unsigned comment 'NULL when the event starts, set to the thread''s current event number at the end of the event.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event instrument name and a NAME from the setup_instruments table'," + "SOURCE VARCHAR(64) comment 'Name and line number of the source file containing the instrumented code that produced the event.'," + "TIMER_START BIGINT unsigned comment 'Value in picoseconds when the event timing started or NULL if timing is not collected.'," + "TIMER_END BIGINT unsigned comment 'Value in picoseconds when the event timing ended, or NULL if the event has not ended or timing is not collected.'," + "TIMER_WAIT BIGINT unsigned comment 'Value in picoseconds of the event''s duration or NULL if the event has not ended or timing is not collected.'," + "SPINS INTEGER unsigned comment 'Number of spin rounds for a mutex, or NULL if spin rounds are not used, or spinning is not instrumented.'," + "OBJECT_SCHEMA VARCHAR(64) comment 'Name of the schema that contains the table for table I/O objects, otherwise NULL for file I/O and synchronization objects.'," + "OBJECT_NAME VARCHAR(512) comment 'File name for file I/O objects, table name for table I/O objects, the socket''s IP:PORT value for a socket object or NULL for a synchronization object.'," + "INDEX_NAME VARCHAR(64) comment 'Name of the index, PRIMARY for the primary key, or NULL for no index used.'," + "OBJECT_TYPE VARCHAR(64) comment 'FILE for a file object, TABLE or TEMPORARY TABLE for a table object, or NULL for a synchronization object.'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null comment 'Address in memory of the object.'," + "NESTING_EVENT_ID BIGINT unsigned comment 'EVENT_ID of event within which this event nests.'," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT') comment 'Nesting event type. Either statement, stage or wait.'," + "OPERATION VARCHAR(32) not null comment 'Operation type, for example read, write or lock'," + "NUMBER_OF_BYTES BIGINT comment 'Number of bytes that the operation read or wrote, or NULL for table I/O waits.'," + "FLAGS INTEGER unsigned comment 'Reserved for use in the future.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +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_length= 0; + m_row.m_object_schema_length= 0; + m_row.m_object_name_length= 0; + m_row.m_index_name_length= 0; +} + +int table_events_waits_common::make_table_object_columns(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_share_index *index_stat; + index_stat= safe_table_share->find_index_stat(safe_index); + + if (index_stat != NULL) + { + m_row.m_index_name_length= index_stat->m_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, index_stat->m_key.m_name, m_row.m_index_name_length); + } + else + { + m_row.m_index_name_length= 0; + } + } + 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(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(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 ':') */ + size_t port_len= int10_to_str(port, (port_str+1), 10) - port_str + 1; + + /* OBJECT NAME */ + m_row.m_object_name_length= ip_length + static_cast<uint>(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; +} + +int table_events_waits_common::make_metadata_lock_object_columns(PFS_events_waits *wait) +{ + PFS_metadata_lock *safe_metadata_lock; + + safe_metadata_lock= sanitize_metadata_lock(wait->m_weak_metadata_lock); + if (unlikely(safe_metadata_lock == NULL)) + return 1; + + if (safe_metadata_lock->get_version() == wait->m_weak_version) + { + MDL_key *mdl= & safe_metadata_lock->m_mdl_key; + MDL_key user_lock_workaround; + + switch(mdl->mdl_namespace()) + { + case MDL_key::BACKUP: + m_row.m_object_type= "BACKUP"; + m_row.m_object_type_length= 6; + m_row.m_object_schema_length= 0; + m_row.m_object_name_length= 0; + break; + case MDL_key::SCHEMA: + m_row.m_object_type= "SCHEMA"; + m_row.m_object_type_length= 6; + m_row.m_object_schema_length= mdl->db_name_length(); + m_row.m_object_name_length= 0; + break; + case MDL_key::TABLE: + m_row.m_object_type= "TABLE"; + m_row.m_object_type_length= 5; + m_row.m_object_schema_length= mdl->db_name_length(); + m_row.m_object_name_length= mdl->name_length(); + break; + case MDL_key::FUNCTION: + m_row.m_object_type= "FUNCTION"; + m_row.m_object_type_length= 8; + m_row.m_object_schema_length= mdl->db_name_length(); + m_row.m_object_name_length= mdl->name_length(); + break; + case MDL_key::PROCEDURE: + m_row.m_object_type= "PROCEDURE"; + m_row.m_object_type_length= 9; + m_row.m_object_schema_length= mdl->db_name_length(); + m_row.m_object_name_length= mdl->name_length(); + break; + case MDL_key::PACKAGE_BODY: + m_row.m_object_type= "PACKAGE_BODY"; + m_row.m_object_type_length= 12; + m_row.m_object_schema_length= mdl->db_name_length(); + m_row.m_object_name_length= mdl->name_length(); + break; + case MDL_key::TRIGGER: + m_row.m_object_type= "TRIGGER"; + m_row.m_object_type_length= 7; + m_row.m_object_schema_length= mdl->db_name_length(); + m_row.m_object_name_length= mdl->name_length(); + break; + case MDL_key::EVENT: + m_row.m_object_type= "EVENT"; + m_row.m_object_type_length= 5; + m_row.m_object_schema_length= mdl->db_name_length(); + m_row.m_object_name_length= mdl->name_length(); + break; + case MDL_key::USER_LOCK: + m_row.m_object_type= "USER LEVEL LOCK"; + m_row.m_object_type_length= 15; + user_lock_workaround.mdl_key_init(MDL_key::USER_LOCK, "", mdl->db_name()); + mdl=& user_lock_workaround; + m_row.m_object_schema_length= 0; + m_row.m_object_name_length= mdl->name_length(); + break; + case MDL_key::NAMESPACE_END: + default: + m_row.m_object_type_length= 0; + m_row.m_object_schema_length= 0; + m_row.m_object_name_length= 0; + break; + } + + if (m_row.m_object_schema_length > sizeof(m_row.m_object_schema)) + return 1; + if (m_row.m_object_schema_length > 0) + memcpy(m_row.m_object_schema, mdl->db_name(), m_row.m_object_schema_length); + + if (m_row.m_object_name_length > sizeof(m_row.m_object_name)) + return 1; + if (m_row.m_object_name_length > 0) + memcpy(m_row.m_object_name, mdl->name(), m_row.m_object_name_length); + + m_row.m_object_instance_addr= (intptr) wait->m_object_instance_addr; + } + else + { + m_row.m_object_type_length= 0; + m_row.m_object_schema_length= 0; + m_row.m_object_name_length= 0; + m_row.m_object_instance_addr= 0; + } + + /* INDEX NAME */ + m_row.m_index_name_length= 0; + + return 0; +} + +/** + Build a row. + @param wait the wait the cursor is reading +*/ +void table_events_waits_common::make_row(PFS_events_waits *wait) +{ + PFS_instr_class *safe_class; + enum_timer_name timer_name= wait_timer; + ulonglong timer_end; + + m_row_exists= false; + + /* + 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_METADATA: + if (make_metadata_lock_object_columns(wait)) + return; + safe_class= sanitize_metadata_class(wait->m_class); + break; + case WAIT_CLASS_IDLE: + clear_object_columns(); + m_row.m_object_instance_addr= 0; + safe_class= sanitize_idle_class(wait->m_class); + timer_name= idle_timer; + break; + case WAIT_CLASS_MUTEX: + clear_object_columns(); + m_row.m_object_instance_addr= (intptr) wait->m_object_instance_addr; + safe_class= sanitize_mutex_class((PFS_mutex_class*) wait->m_class); + break; + case WAIT_CLASS_RWLOCK: + clear_object_columns(); + m_row.m_object_instance_addr= (intptr) wait->m_object_instance_addr; + safe_class= sanitize_rwlock_class((PFS_rwlock_class*) wait->m_class); + break; + case WAIT_CLASS_COND: + clear_object_columns(); + m_row.m_object_instance_addr= (intptr) wait->m_object_instance_addr; + 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= wait->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; + + /* Disable source file and line to avoid stale __FILE__ pointers. */ + m_row.m_source_length= 0; + + 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; + + 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 (RW-lock) */ + { 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") }, + + /* RWLock operations (SX-lock) */ + { C_STRING_WITH_LEN("shared_lock") }, + { C_STRING_WITH_LEN("shared_exclusive_lock") }, + { C_STRING_WITH_LEN("exclusive_lock") }, + { C_STRING_WITH_LEN("try_shared_lock") }, + { C_STRING_WITH_LEN("try_shared_exclusive_lock") }, + { C_STRING_WITH_LEN("try_exclusive_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") }, + + /* Medatada lock operations */ + { C_STRING_WITH_LEN("metadata lock") } +}; + + +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 */ + 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_length > 0) + { + 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, (uint)operation->length); + break; + case 17: /* NUMBER_OF_BYTES (also used for ROWS) */ + 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) || + (m_row.m_operation == OPERATION_TYPE_TABLE_FETCH) || + (m_row.m_operation == OPERATION_TYPE_TABLE_WRITE_ROW) || + (m_row.m_operation == OPERATION_TYPE_TABLE_UPDATE_ROW) || + (m_row.m_operation == OPERATION_TYPE_TABLE_DELETE_ROW)) + set_field_ulonglong(f, m_row.m_number_of_bytes); + else + f->set_null(); + break; + case 18: /* FLAGS */ + f->set_null(); + break; + default: + 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; + bool has_more_thread= true; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + pfs_thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread); + if (pfs_thread != NULL) + { + /* + 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(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); + + pfs_thread= global_thread_container.get(m_pos.m_index_1); + if (pfs_thread != NULL) + { +#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 + + assert(m_pos.m_index_2 < WAIT_STACK_LOGICAL_SIZE); + + if (wait->m_wait_class != NO_WAIT_CLASS) + { + make_row(pfs_thread, wait); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_events_waits_current::make_row(PFS_thread *thread, PFS_events_waits *wait) +{ + pfs_optimistic_state lock; + + /* Protect this reader against a thread termination */ + thread->m_lock.begin_optimistic_lock(&lock); + + table_events_waits_common::make_row(wait); + + if (! thread->m_lock.end_optimistic_lock(&lock)) + m_row_exists= false; +} + +int table_events_waits_current::delete_all_rows(void) +{ + reset_events_waits_current(); + return 0; +} + +ha_rows +table_events_waits_current::get_row_count(void) +{ + return WAIT_STACK_SIZE * global_thread_container.get_row_count(); +} + +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; + bool has_more_thread= true; + + if (events_waits_history_per_thread == 0) + return HA_ERR_END_OF_FILE; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + pfs_thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread); + if (pfs_thread != NULL) + { + 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; + } + + wait= &pfs_thread->m_waits_history[m_pos.m_index_2]; + if (wait->m_wait_class != NO_WAIT_CLASS) + { + make_row(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; + + assert(events_waits_history_per_thread != 0); + set_position(pos); + + pfs_thread= global_thread_container.get(m_pos.m_index_1); + if (pfs_thread != NULL) + { + 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) + { + make_row(pfs_thread, wait); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_events_waits_history::make_row(PFS_thread *thread, PFS_events_waits *wait) +{ + pfs_optimistic_state lock; + + /* Protect this reader against a thread termination */ + thread->m_lock.begin_optimistic_lock(&lock); + + table_events_waits_common::make_row(wait); + + if (! thread->m_lock.end_optimistic_lock(&lock)) + m_row_exists= false; +} + +int table_events_waits_history::delete_all_rows(void) +{ + reset_events_waits_history(); + return 0; +} + +ha_rows +table_events_waits_history::get_row_count(void) +{ + return events_waits_history_per_thread * global_thread_container.get_row_count(); +} + +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.m_u32 % 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(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.m_u32 % 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(wait); + return 0; +} + +int table_events_waits_history_long::delete_all_rows(void) +{ + reset_events_waits_history_long(); + return 0; +} + +ha_rows +table_events_waits_history_long::get_row_count(void) +{ + return events_waits_history_long_size; +} + diff --git a/storage/perfschema/table_events_waits.h b/storage/perfschema/table_events_waits.h new file mode 100644 index 00000000..62df9ae2 --- /dev/null +++ b/storage/perfschema/table_events_waits.h @@ -0,0 +1,266 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_EVENTS_WAITS_H +#define TABLE_EVENTS_WAITS_H + +/** + @file storage/perfschema/table_events_waits.h + Table EVENTS_WAITS_xxx (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_events_waits.h" + +struct PFS_thread; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of table_events_waits_common. */ +struct row_events_waits +{ + /** Column THREAD_ID. */ + ulonglong m_thread_internal_id; + /** Column EVENT_ID. */ + ulonglong m_event_id; + /** Column END_EVENT_ID. */ + ulonglong m_end_event_id; + /** Column NESTING_EVENT_ID. */ + ulonglong m_nesting_event_id; + /** Column NESTING_EVENT_TYPE. */ + enum_event_type m_nesting_event_type; + /** Column EVENT_NAME. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** Column TIMER_START. */ + ulonglong m_timer_start; + /** Column TIMER_END. */ + ulonglong m_timer_end; + /** Column TIMER_WAIT. */ + ulonglong m_timer_wait; + /** Column OBJECT_TYPE. */ + const char *m_object_type; + /** Length in bytes of @c m_object_type. */ + uint m_object_type_length; + /** Column OBJECT_SCHEMA. */ + char m_object_schema[COL_OBJECT_SCHEMA_SIZE]; + /** Length in bytes of @c m_object_schema. */ + uint m_object_schema_length; + /** Column OBJECT_NAME. */ + char m_object_name[COL_OBJECT_NAME_EXTENDED_SIZE]; + /** Length in bytes of @c m_object_name. */ + uint m_object_name_length; + /** Column INDEX_NAME. */ + char m_index_name[COL_INDEX_NAME_SIZE]; + /** Length in bytes of @c m_index_name. */ + uint m_index_name_length; + /** Column OBJECT_INSTANCE_BEGIN. */ + intptr m_object_instance_addr; + /** Column SOURCE. */ + char m_source[COL_SOURCE_SIZE]; + /** Length in bytes of @c m_source. */ + uint m_source_length; + /** Column OPERATION. */ + enum_operation_type m_operation; + /** Column NUMBER_OF_BYTES. */ + ulonglong m_number_of_bytes; + /** Column FLAGS. */ + uint m_flags; +}; + +/** Position of a cursor on PERFORMANCE_SCHEMA.EVENTS_WAITS_CURRENT. */ +struct pos_events_waits_current : public PFS_double_index +{ + pos_events_waits_current() + : PFS_double_index(0, 0) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** Position of a cursor on PERFORMANCE_SCHEMA.EVENTS_WAITS_HISTORY. */ +struct pos_events_waits_history : public PFS_double_index +{ + pos_events_waits_history() + : PFS_double_index(0, 0) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** + Adapter, for table sharing the structure of + PERFORMANCE_SCHEMA.EVENTS_WAITS_CURRENT. +*/ +class table_events_waits_common : public PFS_engine_table +{ +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_events_waits_common(const PFS_engine_table_share *share, void *pos); + + ~table_events_waits_common() = default; + + void clear_object_columns(); + int make_table_object_columns(PFS_events_waits *wait); + int make_file_object_columns(PFS_events_waits *wait); + int make_socket_object_columns(PFS_events_waits *wait); + int make_metadata_lock_object_columns(PFS_events_waits *wait); + + void make_row(PFS_events_waits *wait); + + /** Current row. */ + row_events_waits m_row; + /** True if the current row exists. */ + bool m_row_exists; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_WAITS_CURRENT. */ +class table_events_waits_current : public table_events_waits_common +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + table_events_waits_current(); + +public: + ~table_events_waits_current() = default; + +private: + friend class table_events_waits_history; + friend class table_events_waits_history_long; + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + void make_row(PFS_thread *thread, PFS_events_waits *wait); + + /** Current position. */ + pos_events_waits_current m_pos; + /** Next position. */ + pos_events_waits_current m_next_pos; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_WAITS_HISTORY. */ +class table_events_waits_history : public table_events_waits_common +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + table_events_waits_history(); + +public: + ~table_events_waits_history() = default; + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + void make_row(PFS_thread *thread, PFS_events_waits *wait); + + /** Current position. */ + pos_events_waits_history m_pos; + /** Next position. */ + pos_events_waits_history m_next_pos; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_WAITS_HISTORY_LONG. */ +class table_events_waits_history_long : public table_events_waits_common +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + table_events_waits_history_long(); + +public: + ~table_events_waits_history_long() = default; + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_events_waits_summary.cc b/storage/perfschema/table_events_waits_summary.cc new file mode 100644 index 00000000..c848ebf4 --- /dev/null +++ b/storage/perfschema/table_events_waits_summary.cc @@ -0,0 +1,243 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_events_waits_summary.cc + Table EVENTS_WAITS_SUMMARY_BY_xxx (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_events_waits_summary.h" +#include "pfs_global.h" +#include "field.h" + +THR_LOCK table_events_waits_summary_by_instance::m_table_lock; + +PFS_engine_table_share_state +table_events_waits_summary_by_instance::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_events_waits_summary_by_instance::m_share= +{ + { C_STRING_WITH_LEN("events_waits_summary_by_instance") }, + &pfs_truncatable_acl, + table_events_waits_summary_by_instance::create, + NULL, /* write_row */ + table_events_waits_summary_by_instance::delete_all_rows, + table_all_instr::get_row_count, + sizeof(pos_all_instr), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_waits_summary_by_instance(" + "EVENT_NAME VARCHAR(128) not null comment 'Event name. Used together with OBJECT_INSTANCE_BEGIN for grouping events.'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null comment 'If an instrument creates multiple instances, each instance has a unique OBJECT_INSTANCE_BEGIN value to allow for grouping by instance.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_events_waits_summary_by_instance::create(void) +{ + return new table_events_waits_summary_by_instance(); +} + +int table_events_waits_summary_by_instance::delete_all_rows(void) +{ + reset_events_waits_by_instance(); + return 0; +} + +table_events_waits_summary_by_instance +::table_events_waits_summary_by_instance() + : table_all_instr(&m_share), m_row_exists(false) +{} + +void table_events_waits_summary_by_instance +::make_instr_row(PFS_instr *pfs, PFS_instr_class *klass, + const void *object_instance_begin, + PFS_single_stat *pfs_stat) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + /* + Protect this reader against a mutex/rwlock/cond destroy, + file delete, table drop. + */ + pfs->m_lock.begin_optimistic_lock(&lock); + + m_row.m_name= klass->m_name; + m_row.m_name_length= klass->m_name_length; + m_row.m_object_instance_addr= (intptr) object_instance_begin; + + get_normalizer(klass); + m_row.m_stat.set(m_normalizer, pfs_stat); + + if (pfs->m_lock.end_optimistic_lock(&lock)) + m_row_exists= true; +} + +/** + Build a row, for mutex statistics in a thread. + @param pfs the mutex this cursor is reading +*/ +void table_events_waits_summary_by_instance::make_mutex_row(PFS_mutex *pfs) +{ + PFS_mutex_class *safe_class; + safe_class= sanitize_mutex_class(pfs->m_class); + if (unlikely(safe_class == NULL)) + return; + + make_instr_row(pfs, safe_class, pfs->m_identity, &pfs->m_mutex_stat.m_wait_stat); +} + +/** + Build a row, for rwlock statistics in a thread. + @param pfs the rwlock this cursor is reading +*/ +void table_events_waits_summary_by_instance::make_rwlock_row(PFS_rwlock *pfs) +{ + PFS_rwlock_class *safe_class; + safe_class= sanitize_rwlock_class(pfs->m_class); + if (unlikely(safe_class == NULL)) + return; + + make_instr_row(pfs, safe_class, pfs->m_identity, &pfs->m_rwlock_stat.m_wait_stat); +} + +/** + Build a row, for condition statistics in a thread. + @param pfs the condition this cursor is reading +*/ +void table_events_waits_summary_by_instance::make_cond_row(PFS_cond *pfs) +{ + PFS_cond_class *safe_class; + safe_class= sanitize_cond_class(pfs->m_class); + if (unlikely(safe_class == NULL)) + return; + + make_instr_row(pfs, safe_class, pfs->m_identity, &pfs->m_cond_stat.m_wait_stat); +} + +/** + Build a row, for file statistics in a thread. + @param pfs the file this cursor is reading +*/ +void table_events_waits_summary_by_instance::make_file_row(PFS_file *pfs) +{ + PFS_file_class *safe_class; + safe_class= sanitize_file_class(pfs->m_class); + if (unlikely(safe_class == NULL)) + return; + + PFS_single_stat sum; + pfs->m_file_stat.m_io_stat.sum_waits(& sum); + /* + Files don't have a in memory structure associated to it, + so we use the address of the PFS_file buffer as object_instance_begin + */ + make_instr_row(pfs, safe_class, pfs, & sum); +} + +/** + Build a row, for socket statistics in a thread. + @param pfs the socket this cursor is reading +*/ +void table_events_waits_summary_by_instance::make_socket_row(PFS_socket *pfs) +{ + PFS_socket_class *safe_class; + safe_class= sanitize_socket_class(pfs->m_class); + if (unlikely(safe_class == NULL)) + return; + + /* + Consolidate wait times and byte counts for individual operations. This is + done by the consumer in order to reduce overhead on the socket instrument. + */ + PFS_byte_stat pfs_stat; + pfs->m_socket_stat.m_io_stat.sum(&pfs_stat); + + /* + Sockets don't have an associated in-memory structure, so use the address of + the PFS_socket buffer as object_instance_begin. + */ + make_instr_row(pfs, safe_class, pfs, &pfs_stat); +} + +int table_events_waits_summary_by_instance +::read_row_values(TABLE *table, unsigned char *, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* NAME */ + set_field_varchar_utf8(f, m_row.m_name, m_row.m_name_length); + break; + case 1: /* OBJECT_INSTANCE */ + set_field_ulonglong(f, m_row.m_object_instance_addr); + break; + case 2: /* COUNT */ + set_field_ulonglong(f, m_row.m_stat.m_count); + break; + case 3: /* SUM */ + set_field_ulonglong(f, m_row.m_stat.m_sum); + break; + case 4: /* MIN */ + set_field_ulonglong(f, m_row.m_stat.m_min); + break; + case 5: /* AVG */ + set_field_ulonglong(f, m_row.m_stat.m_avg); + break; + case 6: /* MAX */ + set_field_ulonglong(f, m_row.m_stat.m_max); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_events_waits_summary.h b/storage/perfschema/table_events_waits_summary.h new file mode 100644 index 00000000..d84a79ef --- /dev/null +++ b/storage/perfschema/table_events_waits_summary.h @@ -0,0 +1,97 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_EVENTS_WAITS_SUMMARY_H +#define TABLE_EVENTS_WAITS_SUMMARY_H + +/** + @file storage/perfschema/table_events_waits_summary.h + Table EVENTS_WAITS_SUMMARY_BY_xxx (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_all_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_INSTANCE. */ +struct row_events_waits_summary_by_instance +{ + /** Column EVENT_NAME. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** Column OBJECT_INSTANCE_BEGIN. */ + intptr m_object_instance_addr; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_stat_row m_stat; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_INSTANCE. */ +class table_events_waits_summary_by_instance : public table_all_instr +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + +protected: + void make_instr_row(PFS_instr *pfs, PFS_instr_class *klass, + const void *object_instance_begin, + PFS_single_stat *pfs_stat); + virtual void make_mutex_row(PFS_mutex *pfs); + virtual void make_rwlock_row(PFS_rwlock *pfs); + virtual void make_cond_row(PFS_cond *pfs); + virtual void make_file_row(PFS_file *pfs); + virtual void make_socket_row(PFS_socket *pfs); + + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_events_waits_summary_by_instance(); + +public: + ~table_events_waits_summary_by_instance() = default; + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_events_waits_summary_by_instance m_row; + /** True if the current row exists. */ + bool m_row_exists; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_ews_by_account_by_event_name.cc b/storage/perfschema/table_ews_by_account_by_event_name.cc new file mode 100644 index 00000000..bec37022 --- /dev/null +++ b/storage/perfschema/table_ews_by_account_by_event_name.cc @@ -0,0 +1,278 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_ews_by_account_by_event_name.cc + Table EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_ews_by_account_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_ews_by_account_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_ews_by_account_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_ews_by_account_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_waits_summary_by_account_by_event_name") }, + &pfs_truncatable_acl, + table_ews_by_account_by_event_name::create, + NULL, /* write_row */ + table_ews_by_account_by_event_name::delete_all_rows, + table_ews_by_account_by_event_name::get_row_count, + sizeof(pos_ews_by_account_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_waits_summary_by_account_by_event_name(" + "USER CHAR(" USERNAME_CHAR_LENGTH_STR ") collate utf8_bin default null comment 'User. Used together with HOST and EVENT_NAME for grouping events.'," + "HOST CHAR(" HOSTNAME_LENGTH_STR ") collate utf8_bin default null comment 'Host. Used together with USER and EVENT_NAME for grouping events.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name. Used together with USER and HOST for grouping events.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_ews_by_account_by_event_name::create(void) +{ + return new table_ews_by_account_by_event_name(); +} + +int +table_ews_by_account_by_event_name::delete_all_rows(void) +{ + reset_events_waits_by_thread(); + reset_events_waits_by_account(); + return 0; +} + +ha_rows +table_ews_by_account_by_event_name::get_row_count(void) +{ + return global_account_container.get_row_count() * wait_class_max; +} + +table_ews_by_account_by_event_name::table_ews_by_account_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_ews_by_account_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_ews_by_account_by_event_name::rnd_next(void) +{ + PFS_account *account; + PFS_instr_class *instr_class; + bool has_more_account= true; + + for (m_pos.set_at(&m_next_pos); + has_more_account; + m_pos.next_account()) + { + account= global_account_container.get(m_pos.m_index_1, & has_more_account); + if (account != NULL) + { + for ( ; + m_pos.has_more_view(); + m_pos.next_view()) + { + switch (m_pos.m_index_2) + { + case pos_ews_by_account_by_event_name::VIEW_MUTEX: + instr_class= find_mutex_class(m_pos.m_index_3); + break; + case pos_ews_by_account_by_event_name::VIEW_RWLOCK: + instr_class= find_rwlock_class(m_pos.m_index_3); + break; + case pos_ews_by_account_by_event_name::VIEW_COND: + instr_class= find_cond_class(m_pos.m_index_3); + break; + case pos_ews_by_account_by_event_name::VIEW_FILE: + instr_class= find_file_class(m_pos.m_index_3); + break; + case pos_ews_by_account_by_event_name::VIEW_TABLE: + instr_class= find_table_class(m_pos.m_index_3); + break; + case pos_ews_by_account_by_event_name::VIEW_SOCKET: + instr_class= find_socket_class(m_pos.m_index_3); + break; + case pos_ews_by_account_by_event_name::VIEW_IDLE: + instr_class= find_idle_class(m_pos.m_index_3); + break; + case pos_ews_by_account_by_event_name::VIEW_METADATA: + instr_class= find_metadata_class(m_pos.m_index_3); + break; + default: + instr_class= NULL; + assert(false); + break; + } + + if (instr_class) + { + make_row(account, instr_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_ews_by_account_by_event_name::rnd_pos(const void *pos) +{ + PFS_account *account; + PFS_instr_class *instr_class; + + set_position(pos); + + account= global_account_container.get(m_pos.m_index_1); + if (account == NULL) + return HA_ERR_RECORD_DELETED; + + switch (m_pos.m_index_2) + { + case pos_ews_by_account_by_event_name::VIEW_MUTEX: + instr_class= find_mutex_class(m_pos.m_index_3); + break; + case pos_ews_by_account_by_event_name::VIEW_RWLOCK: + instr_class= find_rwlock_class(m_pos.m_index_3); + break; + case pos_ews_by_account_by_event_name::VIEW_COND: + instr_class= find_cond_class(m_pos.m_index_3); + break; + case pos_ews_by_account_by_event_name::VIEW_FILE: + instr_class= find_file_class(m_pos.m_index_3); + break; + case pos_ews_by_account_by_event_name::VIEW_TABLE: + instr_class= find_table_class(m_pos.m_index_3); + break; + case pos_ews_by_account_by_event_name::VIEW_SOCKET: + instr_class= find_socket_class(m_pos.m_index_3); + break; + case pos_ews_by_account_by_event_name::VIEW_IDLE: + instr_class= find_idle_class(m_pos.m_index_3); + break; + case pos_ews_by_account_by_event_name::VIEW_METADATA: + instr_class= find_metadata_class(m_pos.m_index_3); + break; + default: + instr_class= NULL; + assert(false); + } + if (instr_class) + { + make_row(account, instr_class); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_ews_by_account_by_event_name +::make_row(PFS_account *account, PFS_instr_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + account->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_account.make_row(account)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_wait_visitor visitor(klass); + PFS_connection_iterator::visit_account(account, + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! account->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + + get_normalizer(klass); + m_row.m_stat.set(m_normalizer, & visitor.m_stat); +} + +int table_ews_by_account_by_event_name +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* USER */ + case 1: /* HOST */ + m_row.m_account.set_field(f->field_index, f); + break; + case 2: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 3, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 3, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_ews_by_account_by_event_name.h b/storage/perfschema/table_ews_by_account_by_event_name.h new file mode 100644 index 00000000..df72f258 --- /dev/null +++ b/storage/perfschema/table_ews_by_account_by_event_name.h @@ -0,0 +1,139 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_EWS_BY_ACCOUNT_BY_EVENT_NAME_H +#define TABLE_EWS_BY_ACCOUNT_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_ews_by_account_by_event_name.h + Table EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_account.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. +*/ +struct row_ews_by_account_by_event_name +{ + /** Column USER, HOST. */ + PFS_account_row m_account; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. + Index 1 on account (0 based) + Index 2 on instrument view + Index 3 on instrument class (1 based) +*/ +struct pos_ews_by_account_by_event_name +: public PFS_triple_index, public PFS_instrument_view_constants +{ + pos_ews_by_account_by_event_name() + : PFS_triple_index(0, FIRST_VIEW, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= VIEW_MUTEX; + m_index_3= 1; + } + + inline void next_account(void) + { + m_index_1++; + m_index_2= FIRST_VIEW; + m_index_3= 1; + } + + inline bool has_more_view(void) + { return (m_index_2 <= LAST_VIEW); } + + inline void next_view(void) + { + m_index_2++; + m_index_3= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. */ +class table_ews_by_account_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_ews_by_account_by_event_name(); + +public: + ~table_ews_by_account_by_event_name() = default; + +protected: + void make_row(PFS_account *account, PFS_instr_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_ews_by_account_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_ews_by_account_by_event_name m_pos; + /** Next position. */ + pos_ews_by_account_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_ews_by_host_by_event_name.cc b/storage/perfschema/table_ews_by_host_by_event_name.cc new file mode 100644 index 00000000..d6b702e0 --- /dev/null +++ b/storage/perfschema/table_ews_by_host_by_event_name.cc @@ -0,0 +1,280 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_ews_by_host_by_event_name.cc + Table EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_ews_by_host_by_event_name.h" +#include "pfs_global.h" +#include "pfs_account.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_ews_by_host_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_ews_by_host_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_ews_by_host_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_waits_summary_by_host_by_event_name") }, + &pfs_truncatable_acl, + table_ews_by_host_by_event_name::create, + NULL, /* write_row */ + table_ews_by_host_by_event_name::delete_all_rows, + table_ews_by_host_by_event_name::get_row_count, + sizeof(pos_ews_by_host_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_waits_summary_by_host_by_event_name(" + "HOST CHAR(" STRINGIFY_ARG(HOSTNAME_LENGTH) ") collate utf8_bin default null comment 'Host. Used together with EVENT_NAME for grouping events.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name. Used together with USER and HOST for grouping events.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.' )") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_ews_by_host_by_event_name::create(void) +{ + return new table_ews_by_host_by_event_name(); +} + +int +table_ews_by_host_by_event_name::delete_all_rows(void) +{ + reset_events_waits_by_thread(); + reset_events_waits_by_account(); + reset_events_waits_by_host(); + return 0; +} + +ha_rows +table_ews_by_host_by_event_name::get_row_count(void) +{ + return global_host_container.get_row_count() * wait_class_max; +} + +table_ews_by_host_by_event_name::table_ews_by_host_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_ews_by_host_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_ews_by_host_by_event_name::rnd_next(void) +{ + PFS_host *host; + PFS_instr_class *instr_class; + bool has_more_host= true; + + for (m_pos.set_at(&m_next_pos); + has_more_host; + m_pos.next_host()) + { + host= global_host_container.get(m_pos.m_index_1, & has_more_host); + if (host != NULL) + { + for ( ; + m_pos.has_more_view(); + m_pos.next_view()) + { + switch (m_pos.m_index_2) + { + case pos_ews_by_host_by_event_name::VIEW_MUTEX: + instr_class= find_mutex_class(m_pos.m_index_3); + break; + case pos_ews_by_host_by_event_name::VIEW_RWLOCK: + instr_class= find_rwlock_class(m_pos.m_index_3); + break; + case pos_ews_by_host_by_event_name::VIEW_COND: + instr_class= find_cond_class(m_pos.m_index_3); + break; + case pos_ews_by_host_by_event_name::VIEW_FILE: + instr_class= find_file_class(m_pos.m_index_3); + break; + case pos_ews_by_host_by_event_name::VIEW_TABLE: + instr_class= find_table_class(m_pos.m_index_3); + break; + case pos_ews_by_host_by_event_name::VIEW_SOCKET: + instr_class= find_socket_class(m_pos.m_index_3); + break; + case pos_ews_by_host_by_event_name::VIEW_IDLE: + instr_class= find_idle_class(m_pos.m_index_3); + break; + case pos_ews_by_host_by_event_name::VIEW_METADATA: + instr_class= find_metadata_class(m_pos.m_index_3); + break; + default: + instr_class= NULL; + assert(false); + break; + } + + if (instr_class) + { + make_row(host, instr_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_ews_by_host_by_event_name::rnd_pos(const void *pos) +{ + PFS_host *host; + PFS_instr_class *instr_class; + + set_position(pos); + + host= global_host_container.get(m_pos.m_index_1); + if (host == NULL) + return HA_ERR_RECORD_DELETED; + + switch (m_pos.m_index_2) + { + case pos_ews_by_host_by_event_name::VIEW_MUTEX: + instr_class= find_mutex_class(m_pos.m_index_3); + break; + case pos_ews_by_host_by_event_name::VIEW_RWLOCK: + instr_class= find_rwlock_class(m_pos.m_index_3); + break; + case pos_ews_by_host_by_event_name::VIEW_COND: + instr_class= find_cond_class(m_pos.m_index_3); + break; + case pos_ews_by_host_by_event_name::VIEW_FILE: + instr_class= find_file_class(m_pos.m_index_3); + break; + case pos_ews_by_host_by_event_name::VIEW_TABLE: + instr_class= find_table_class(m_pos.m_index_3); + break; + case pos_ews_by_host_by_event_name::VIEW_SOCKET: + instr_class= find_socket_class(m_pos.m_index_3); + break; + case pos_ews_by_host_by_event_name::VIEW_IDLE: + instr_class= find_idle_class(m_pos.m_index_3); + break; + case pos_ews_by_host_by_event_name::VIEW_METADATA: + instr_class= find_metadata_class(m_pos.m_index_3); + break; + default: + instr_class= NULL; + assert(false); + break; + } + if (instr_class) + { + make_row(host, instr_class); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_ews_by_host_by_event_name +::make_row(PFS_host *host, PFS_instr_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + host->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_host.make_row(host)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_wait_visitor visitor(klass); + PFS_connection_iterator::visit_host(host, + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! host->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + + get_normalizer(klass); + m_row.m_stat.set(m_normalizer, &visitor.m_stat); +} + +int table_ews_by_host_by_event_name +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* HOST */ + m_row.m_host.set_field(f); + break; + case 1: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 2, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_ews_by_host_by_event_name.h b/storage/perfschema/table_ews_by_host_by_event_name.h new file mode 100644 index 00000000..c88b3d77 --- /dev/null +++ b/storage/perfschema/table_ews_by_host_by_event_name.h @@ -0,0 +1,139 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_EWS_BY_HOST_BY_EVENT_NAME_H +#define TABLE_EWS_BY_HOST_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_ews_by_host_by_event_name.h + Table EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_host.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME. +*/ +struct row_ews_by_host_by_event_name +{ + /** Column HOST. */ + PFS_host_row m_host; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME. + Index 1 on host (0 based) + Index 2 on instrument view + Index 3 on instrument class (1 based) +*/ +struct pos_ews_by_host_by_event_name +: public PFS_triple_index, public PFS_instrument_view_constants +{ + pos_ews_by_host_by_event_name() + : PFS_triple_index(0, FIRST_VIEW, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= FIRST_VIEW; + m_index_3= 1; + } + + inline void next_host(void) + { + m_index_1++; + m_index_2= FIRST_VIEW; + m_index_3= 1; + } + + inline bool has_more_view(void) + { return (m_index_2 <= LAST_VIEW); } + + inline void next_view(void) + { + m_index_2++; + m_index_3= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME. */ +class table_ews_by_host_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_ews_by_host_by_event_name(); + +public: + ~table_ews_by_host_by_event_name() = default; + +protected: + void make_row(PFS_host *host, PFS_instr_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_ews_by_host_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_ews_by_host_by_event_name m_pos; + /** Next position. */ + pos_ews_by_host_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_ews_by_thread_by_event_name.cc b/storage/perfschema/table_ews_by_thread_by_event_name.cc new file mode 100644 index 00000000..7d84bda7 --- /dev/null +++ b/storage/perfschema/table_ews_by_thread_by_event_name.cc @@ -0,0 +1,286 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_ews_by_thread_by_event_name.cc + Table EVENTS_WAITS_SUMMARY_BY_HOST_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_ews_by_thread_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_ews_by_thread_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_ews_by_thread_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_ews_by_thread_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_waits_summary_by_thread_by_event_name") }, + &pfs_truncatable_acl, + table_ews_by_thread_by_event_name::create, + NULL, /* write_row */ + table_ews_by_thread_by_event_name::delete_all_rows, + table_ews_by_thread_by_event_name::get_row_count, + sizeof(pos_ews_by_thread_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_waits_summary_by_thread_by_event_name(" + "THREAD_ID BIGINT unsigned not null comment 'Thread associated with the event. Together with EVENT_NAME uniquely identifies the row.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name. Used together with THREAD_ID for grouping events.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_ews_by_thread_by_event_name::create(void) +{ + return new table_ews_by_thread_by_event_name(); +} + +int +table_ews_by_thread_by_event_name::delete_all_rows(void) +{ + reset_events_waits_by_thread(); + return 0; +} + +ha_rows +table_ews_by_thread_by_event_name::get_row_count(void) +{ + return global_thread_container.get_row_count() * wait_class_max; +} + +table_ews_by_thread_by_event_name::table_ews_by_thread_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_ews_by_thread_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_ews_by_thread_by_event_name::rnd_next(void) +{ + PFS_thread *thread; + PFS_instr_class *instr_class; + bool has_more_thread= true; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread); + if (thread != NULL) + { + for ( ; + m_pos.has_more_view(); + m_pos.next_view()) + { + switch (m_pos.m_index_2) + { + case pos_ews_by_thread_by_event_name::VIEW_MUTEX: + instr_class= find_mutex_class(m_pos.m_index_3); + break; + case pos_ews_by_thread_by_event_name::VIEW_RWLOCK: + instr_class= find_rwlock_class(m_pos.m_index_3); + break; + case pos_ews_by_thread_by_event_name::VIEW_COND: + instr_class= find_cond_class(m_pos.m_index_3); + break; + case pos_ews_by_thread_by_event_name::VIEW_FILE: + instr_class= find_file_class(m_pos.m_index_3); + break; + case pos_ews_by_thread_by_event_name::VIEW_TABLE: + instr_class= find_table_class(m_pos.m_index_3); + break; + case pos_ews_by_thread_by_event_name::VIEW_SOCKET: + instr_class= find_socket_class(m_pos.m_index_3); + break; + case pos_ews_by_thread_by_event_name::VIEW_IDLE: + instr_class= find_idle_class(m_pos.m_index_3); + break; + case pos_ews_by_thread_by_event_name::VIEW_METADATA: + instr_class= find_metadata_class(m_pos.m_index_3); + break; + default: + assert(false); + instr_class= NULL; + break; + } + + if (instr_class != NULL) + { + make_row(thread, instr_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_ews_by_thread_by_event_name::rnd_pos(const void *pos) +{ + PFS_thread *thread; + PFS_instr_class *instr_class; + + set_position(pos); + + thread= global_thread_container.get(m_pos.m_index_1); + if (thread != NULL) + { + switch (m_pos.m_index_2) + { + case pos_ews_by_thread_by_event_name::VIEW_MUTEX: + instr_class= find_mutex_class(m_pos.m_index_3); + break; + case pos_ews_by_thread_by_event_name::VIEW_RWLOCK: + instr_class= find_rwlock_class(m_pos.m_index_3); + break; + case pos_ews_by_thread_by_event_name::VIEW_COND: + instr_class= find_cond_class(m_pos.m_index_3); + break; + case pos_ews_by_thread_by_event_name::VIEW_FILE: + instr_class= find_file_class(m_pos.m_index_3); + break; + case pos_ews_by_thread_by_event_name::VIEW_TABLE: + instr_class= find_table_class(m_pos.m_index_3); + break; + case pos_ews_by_thread_by_event_name::VIEW_SOCKET: + instr_class= find_socket_class(m_pos.m_index_3); + break; + case pos_ews_by_thread_by_event_name::VIEW_IDLE: + instr_class= find_idle_class(m_pos.m_index_3); + break; + case pos_ews_by_thread_by_event_name::VIEW_METADATA: + instr_class= find_metadata_class(m_pos.m_index_3); + break; + default: + assert(false); + instr_class= NULL; + } + + if (instr_class) + { + make_row(thread, instr_class); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_ews_by_thread_by_event_name +::make_row(PFS_thread *thread, PFS_instr_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + /* Protect this reader against a thread termination */ + thread->m_lock.begin_optimistic_lock(&lock); + + m_row.m_thread_internal_id= thread->m_thread_internal_id; + + m_row.m_event_name.make_row(klass); + + PFS_connection_wait_visitor visitor(klass); + PFS_connection_iterator::visit_thread(thread, &visitor); + + /* + If the aggregation for this class is deferred, then we must pull the + current wait stats from the instances associated with this thread. + */ + if (klass->is_deferred()) + { + /* Visit instances owned by this thread. Do not visit the class. */ + PFS_instance_wait_visitor inst_visitor; + PFS_instance_iterator::visit_instances(klass, &inst_visitor, + thread, false); + /* Combine the deferred stats and global stats */ + visitor.m_stat.aggregate(&inst_visitor.m_stat); + } + + if (! thread->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + + get_normalizer(klass); + m_row.m_stat.set(m_normalizer, & visitor.m_stat); +} + +int table_ews_by_thread_by_event_name +::read_row_values(TABLE *table, unsigned char *, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + 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_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 2, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_ews_by_thread_by_event_name.h b/storage/perfschema/table_ews_by_thread_by_event_name.h new file mode 100644 index 00000000..7ac6638a --- /dev/null +++ b/storage/perfschema/table_ews_by_thread_by_event_name.h @@ -0,0 +1,138 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_EWS_BY_THREAD_BY_EVENT_NAME_H +#define TABLE_EWS_BY_THREAD_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_ews_by_thread_by_event_name.h + Table EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME. +*/ +struct row_ews_by_thread_by_event_name +{ + /** Column THREAD_ID. */ + ulonglong m_thread_internal_id; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME. + Index 1 on thread (0 based) + Index 2 on instrument view + Index 3 on instrument class (1 based) +*/ +struct pos_ews_by_thread_by_event_name +: public PFS_triple_index, public PFS_instrument_view_constants +{ + pos_ews_by_thread_by_event_name() + : PFS_triple_index(0, FIRST_VIEW, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= FIRST_VIEW; + m_index_3= 1; + } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= FIRST_VIEW; + m_index_3= 1; + } + + inline bool has_more_view(void) + { return (m_index_2 <= LAST_VIEW); } + + inline void next_view(void) + { + m_index_2++; + m_index_3= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME. */ +class table_ews_by_thread_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_ews_by_thread_by_event_name(); + +public: + ~table_ews_by_thread_by_event_name() = default; + +protected: + void make_row(PFS_thread *thread, PFS_instr_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_ews_by_thread_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_ews_by_thread_by_event_name m_pos; + /** Next position. */ + pos_ews_by_thread_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_ews_by_user_by_event_name.cc b/storage/perfschema/table_ews_by_user_by_event_name.cc new file mode 100644 index 00000000..e299ed04 --- /dev/null +++ b/storage/perfschema/table_ews_by_user_by_event_name.cc @@ -0,0 +1,279 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_ews_by_user_by_event_name.cc + Table EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_ews_by_user_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_ews_by_user_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_ews_by_user_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_ews_by_user_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_waits_summary_by_user_by_event_name") }, + &pfs_truncatable_acl, + table_ews_by_user_by_event_name::create, + NULL, /* write_row */ + table_ews_by_user_by_event_name::delete_all_rows, + table_ews_by_user_by_event_name::get_row_count, + sizeof(pos_ews_by_user_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_waits_summary_by_user_by_event_name(" + "USER CHAR(" USERNAME_CHAR_LENGTH_STR ") collate utf8_bin default null comment 'User. Used together with EVENT_NAME for grouping events.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name. Used together with USER for grouping events.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_ews_by_user_by_event_name::create(void) +{ + return new table_ews_by_user_by_event_name(); +} + +int +table_ews_by_user_by_event_name::delete_all_rows(void) +{ + reset_events_waits_by_thread(); + reset_events_waits_by_account(); + reset_events_waits_by_user(); + return 0; +} + +ha_rows +table_ews_by_user_by_event_name::get_row_count(void) +{ + return global_user_container.get_row_count() * wait_class_max; +} + +table_ews_by_user_by_event_name::table_ews_by_user_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_ews_by_user_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_ews_by_user_by_event_name::rnd_next(void) +{ + PFS_user *user; + PFS_instr_class *instr_class; + bool has_more_user= true; + + for (m_pos.set_at(&m_next_pos); + has_more_user; + m_pos.next_user()) + { + user= global_user_container.get(m_pos.m_index_1, & has_more_user); + if (user != NULL) + { + for ( ; + m_pos.has_more_view(); + m_pos.next_view()) + { + switch (m_pos.m_index_2) + { + case pos_ews_by_user_by_event_name::VIEW_MUTEX: + instr_class= find_mutex_class(m_pos.m_index_3); + break; + case pos_ews_by_user_by_event_name::VIEW_RWLOCK: + instr_class= find_rwlock_class(m_pos.m_index_3); + break; + case pos_ews_by_user_by_event_name::VIEW_COND: + instr_class= find_cond_class(m_pos.m_index_3); + break; + case pos_ews_by_user_by_event_name::VIEW_FILE: + instr_class= find_file_class(m_pos.m_index_3); + break; + case pos_ews_by_user_by_event_name::VIEW_TABLE: + instr_class= find_table_class(m_pos.m_index_3); + break; + case pos_ews_by_user_by_event_name::VIEW_SOCKET: + instr_class= find_socket_class(m_pos.m_index_3); + break; + case pos_ews_by_user_by_event_name::VIEW_IDLE: + instr_class= find_idle_class(m_pos.m_index_3); + break; + case pos_ews_by_user_by_event_name::VIEW_METADATA: + instr_class= find_metadata_class(m_pos.m_index_3); + break; + default: + instr_class= NULL; + assert(false); + break; + } + + if (instr_class) + { + make_row(user, instr_class); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_ews_by_user_by_event_name::rnd_pos(const void *pos) +{ + PFS_user *user; + PFS_instr_class *instr_class; + + set_position(pos); + + user= global_user_container.get(m_pos.m_index_1); + if (user == NULL) + return HA_ERR_RECORD_DELETED; + + switch (m_pos.m_index_2) + { + case pos_ews_by_user_by_event_name::VIEW_MUTEX: + instr_class= find_mutex_class(m_pos.m_index_3); + break; + case pos_ews_by_user_by_event_name::VIEW_RWLOCK: + instr_class= find_rwlock_class(m_pos.m_index_3); + break; + case pos_ews_by_user_by_event_name::VIEW_COND: + instr_class= find_cond_class(m_pos.m_index_3); + break; + case pos_ews_by_user_by_event_name::VIEW_FILE: + instr_class= find_file_class(m_pos.m_index_3); + break; + case pos_ews_by_user_by_event_name::VIEW_TABLE: + instr_class= find_table_class(m_pos.m_index_3); + break; + case pos_ews_by_user_by_event_name::VIEW_SOCKET: + instr_class= find_socket_class(m_pos.m_index_3); + break; + case pos_ews_by_user_by_event_name::VIEW_IDLE: + instr_class= find_idle_class(m_pos.m_index_3); + break; + case pos_ews_by_user_by_event_name::VIEW_METADATA: + instr_class= find_metadata_class(m_pos.m_index_3); + break; + default: + instr_class= NULL; + assert(false); + break; + } + if (instr_class) + { + make_row(user, instr_class); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_ews_by_user_by_event_name +::make_row(PFS_user *user, PFS_instr_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + user->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_user.make_row(user)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_wait_visitor visitor(klass); + PFS_connection_iterator::visit_user(user, + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! user->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + + get_normalizer(klass); + m_row.m_stat.set(m_normalizer, &visitor.m_stat); +} + +int table_ews_by_user_by_event_name +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* USER */ + m_row.m_user.set_field(f); + break; + case 1: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 2, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_ews_by_user_by_event_name.h b/storage/perfschema/table_ews_by_user_by_event_name.h new file mode 100644 index 00000000..82c7319c --- /dev/null +++ b/storage/perfschema/table_ews_by_user_by_event_name.h @@ -0,0 +1,139 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_EWS_BY_USER_BY_EVENT_NAME_H +#define TABLE_EWS_BY_USER_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_ews_by_user_by_event_name.h + Table EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_user.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME. +*/ +struct row_ews_by_user_by_event_name +{ + /** Column USER. */ + PFS_user_row m_user; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME. + Index 1 on user (0 based) + Index 2 on instrument view + Index 3 on instrument class (1 based) +*/ +struct pos_ews_by_user_by_event_name +: public PFS_triple_index, public PFS_instrument_view_constants +{ + pos_ews_by_user_by_event_name() + : PFS_triple_index(0, FIRST_VIEW, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= FIRST_VIEW; + m_index_3= 1; + } + + inline void next_user(void) + { + m_index_1++; + m_index_2= FIRST_VIEW; + m_index_3= 1; + } + + inline bool has_more_view(void) + { return (m_index_2 <= LAST_VIEW); } + + inline void next_view(void) + { + m_index_2++; + m_index_3= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_USER_BY_EVENT_NAME. */ +class table_ews_by_user_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_ews_by_user_by_event_name(); + +public: + ~table_ews_by_user_by_event_name() = default; + +protected: + void make_row(PFS_user *user, PFS_instr_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_ews_by_user_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_ews_by_user_by_event_name m_pos; + /** Next position. */ + pos_ews_by_user_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_ews_global_by_event_name.cc b/storage/perfschema/table_ews_global_by_event_name.cc new file mode 100644 index 00000000..c5d134c9 --- /dev/null +++ b/storage/perfschema/table_ews_global_by_event_name.cc @@ -0,0 +1,443 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_ews_global_by_event_name.cc + Table EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_ews_global_by_event_name.h" +#include "pfs_global.h" +#include "pfs_instr.h" +#include "pfs_timer.h" +#include "pfs_visitor.h" +#include "field.h" + +THR_LOCK table_ews_global_by_event_name::m_table_lock; +PFS_engine_table_share_state +table_ews_global_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_ews_global_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("events_waits_summary_global_by_event_name") }, + &pfs_truncatable_acl, + table_ews_global_by_event_name::create, + NULL, /* write_row */ + table_ews_global_by_event_name::delete_all_rows, + table_ews_global_by_event_name::get_row_count, + sizeof(pos_ews_global_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE events_waits_summary_global_by_event_name(" + "EVENT_NAME VARCHAR(128) not null comment 'Event name.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_ews_global_by_event_name::create(void) +{ + return new table_ews_global_by_event_name(); +} + +int +table_ews_global_by_event_name::delete_all_rows(void) +{ + reset_events_waits_by_instance(); + reset_table_waits_by_table_handle(); + reset_table_waits_by_table(); + reset_events_waits_by_class(); + return 0; +} + +ha_rows +table_ews_global_by_event_name::get_row_count(void) +{ + return wait_class_max; +} + +table_ews_global_by_event_name::table_ews_global_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_ews_global_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_ews_global_by_event_name::rnd_next(void) +{ + PFS_mutex_class *mutex_class; + PFS_rwlock_class *rwlock_class; + PFS_cond_class *cond_class; + PFS_file_class *file_class; + PFS_socket_class *socket_class; + PFS_instr_class *instr_class; + + for (m_pos.set_at(&m_next_pos); + m_pos.has_more_view(); + m_pos.next_view()) + { + switch (m_pos.m_index_1) + { + case pos_ews_global_by_event_name::VIEW_MUTEX: + mutex_class= find_mutex_class(m_pos.m_index_2); + if (mutex_class) + { + make_mutex_row(mutex_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_ews_global_by_event_name::VIEW_RWLOCK: + rwlock_class= find_rwlock_class(m_pos.m_index_2); + if (rwlock_class) + { + make_rwlock_row(rwlock_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_ews_global_by_event_name::VIEW_COND: + cond_class= find_cond_class(m_pos.m_index_2); + if (cond_class) + { + make_cond_row(cond_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_ews_global_by_event_name::VIEW_FILE: + file_class= find_file_class(m_pos.m_index_2); + if (file_class) + { + make_file_row(file_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_ews_global_by_event_name::VIEW_TABLE: + if (m_pos.m_index_2 == 1) + { + make_table_io_row(&global_table_io_class); + m_next_pos.set_after(&m_pos); + return 0; + } + if (m_pos.m_index_2 == 2) + { + make_table_lock_row(&global_table_lock_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_ews_global_by_event_name::VIEW_SOCKET: + socket_class= find_socket_class(m_pos.m_index_2); + if (socket_class) + { + make_socket_row(socket_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_ews_global_by_event_name::VIEW_IDLE: + instr_class= find_idle_class(m_pos.m_index_2); + if (instr_class) + { + make_idle_row(instr_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_ews_global_by_event_name::VIEW_METADATA: + instr_class= find_metadata_class(m_pos.m_index_2); + if (instr_class) + { + make_metadata_row(instr_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + default: + break; + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_ews_global_by_event_name::rnd_pos(const void *pos) +{ + PFS_mutex_class *mutex_class; + PFS_rwlock_class *rwlock_class; + PFS_cond_class *cond_class; + PFS_file_class *file_class; + PFS_socket_class *socket_class; + PFS_instr_class *instr_class; + + set_position(pos); + + switch (m_pos.m_index_1) + { + case pos_ews_global_by_event_name::VIEW_MUTEX: + mutex_class= find_mutex_class(m_pos.m_index_2); + if (mutex_class) + { + make_mutex_row(mutex_class); + return 0; + } + break; + case pos_ews_global_by_event_name::VIEW_RWLOCK: + rwlock_class= find_rwlock_class(m_pos.m_index_2); + if (rwlock_class) + { + make_rwlock_row(rwlock_class); + return 0; + } + break; + case pos_ews_global_by_event_name::VIEW_COND: + cond_class= find_cond_class(m_pos.m_index_2); + if (cond_class) + { + make_cond_row(cond_class); + return 0; + } + break; + case pos_ews_global_by_event_name::VIEW_FILE: + file_class= find_file_class(m_pos.m_index_2); + if (file_class) + { + make_file_row(file_class); + return 0; + } + break; + case pos_ews_global_by_event_name::VIEW_TABLE: + assert(m_pos.m_index_2 >= 1); + assert(m_pos.m_index_2 <= 2); + if (m_pos.m_index_2 == 1) + make_table_io_row(&global_table_io_class); + else + make_table_lock_row(&global_table_lock_class); + break; + case pos_ews_global_by_event_name::VIEW_SOCKET: + socket_class= find_socket_class(m_pos.m_index_2); + if (socket_class) + { + make_socket_row(socket_class); + return 0; + } + break; + case pos_ews_global_by_event_name::VIEW_IDLE: + instr_class= find_idle_class(m_pos.m_index_2); + if (instr_class) + { + make_idle_row(instr_class); + return 0; + } + break; + case pos_ews_global_by_event_name::VIEW_METADATA: + instr_class= find_metadata_class(m_pos.m_index_2); + if (instr_class) + { + make_metadata_row(instr_class); + return 0; + } + break; + default: + assert(false); + break; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_ews_global_by_event_name +::make_mutex_row(PFS_mutex_class *klass) +{ + m_row.m_event_name.make_row(klass); + + PFS_instance_wait_visitor visitor; + PFS_instance_iterator::visit_mutex_instances(klass, & visitor); + + get_normalizer(klass); + m_row.m_stat.set(m_normalizer, & visitor.m_stat); + m_row_exists= true; +} + +void table_ews_global_by_event_name +::make_rwlock_row(PFS_rwlock_class *klass) +{ + m_row.m_event_name.make_row(klass); + + PFS_instance_wait_visitor visitor; + PFS_instance_iterator::visit_rwlock_instances(klass, & visitor); + + get_normalizer(klass); + m_row.m_stat.set(m_normalizer, & visitor.m_stat); + m_row_exists= true; +} + +void table_ews_global_by_event_name +::make_cond_row(PFS_cond_class *klass) +{ + m_row.m_event_name.make_row(klass); + + PFS_instance_wait_visitor visitor; + PFS_instance_iterator::visit_cond_instances(klass, & visitor); + + get_normalizer(klass); + m_row.m_stat.set(m_normalizer, & visitor.m_stat); + m_row_exists= true; +} + +void table_ews_global_by_event_name +::make_file_row(PFS_file_class *klass) +{ + m_row.m_event_name.make_row(klass); + + PFS_instance_wait_visitor visitor; + PFS_instance_iterator::visit_file_instances(klass, & visitor); + + get_normalizer(klass); + m_row.m_stat.set(m_normalizer, & visitor.m_stat); + m_row_exists= true; +} + +void table_ews_global_by_event_name +::make_table_io_row(PFS_instr_class *klass) +{ + m_row.m_event_name.make_row(klass); + + PFS_table_io_wait_visitor visitor; + PFS_object_iterator::visit_all_tables(& visitor); + + get_normalizer(klass); + m_row.m_stat.set(m_normalizer, & visitor.m_stat); + m_row_exists= true; +} + +void table_ews_global_by_event_name +::make_table_lock_row(PFS_instr_class *klass) +{ + m_row.m_event_name.make_row(klass); + + PFS_table_lock_wait_visitor visitor; + PFS_object_iterator::visit_all_tables(& visitor); + + get_normalizer(klass); + m_row.m_stat.set(m_normalizer, & visitor.m_stat); + m_row_exists= true; +} + +void table_ews_global_by_event_name +::make_socket_row(PFS_socket_class *klass) +{ + m_row.m_event_name.make_row(klass); + + PFS_instance_wait_visitor visitor; + PFS_instance_iterator::visit_socket_instances(klass, &visitor); + + get_normalizer(klass); + m_row.m_stat.set(m_normalizer, &visitor.m_stat); + m_row_exists= true; +} + +void table_ews_global_by_event_name +::make_idle_row(PFS_instr_class *klass) +{ + m_row.m_event_name.make_row(klass); + + PFS_connection_wait_visitor visitor(klass); + PFS_connection_iterator::visit_global(false, /* hosts */ + false, /* users */ + false, /* accounts */ + true, /* threads */ + false, /* THDs */ + &visitor); + get_normalizer(klass); + m_row.m_stat.set(m_normalizer, &visitor.m_stat); + m_row_exists= true; +} + +void table_ews_global_by_event_name +::make_metadata_row(PFS_instr_class *klass) +{ + m_row.m_event_name.make_row(klass); + + PFS_connection_wait_visitor visitor(klass); + PFS_connection_iterator::visit_global(false, /* hosts */ + true, /* users */ + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + &visitor); + get_normalizer(klass); + m_row.m_stat.set(m_normalizer, &visitor.m_stat); + m_row_exists= true; +} + +int table_ews_global_by_event_name +::read_row_values(TABLE *table, unsigned char *, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 1, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_stat.set_field(f->field_index - 1, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_ews_global_by_event_name.h b/storage/perfschema/table_ews_global_by_event_name.h new file mode 100644 index 00000000..84d8a742 --- /dev/null +++ b/storage/perfschema/table_ews_global_by_event_name.h @@ -0,0 +1,135 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_EWS_GLOBAL_BY_EVENT_NAME_H +#define TABLE_EWS_GLOBAL_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_ews_global_by_event_name.h + Table EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME. +*/ +struct row_ews_global_by_event_name +{ + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME. + Index 1 on instrument view + Index 2 on instrument class (1 based) +*/ +struct pos_ews_global_by_event_name +: public PFS_double_index, public PFS_instrument_view_constants +{ + pos_ews_global_by_event_name() + : PFS_double_index(FIRST_VIEW, 1) + {} + + inline void reset(void) + { + m_index_1= FIRST_VIEW; + m_index_2= 1; + } + + inline bool has_more_view(void) + { return (m_index_1 <= LAST_VIEW); } + + inline void next_view(void) + { + m_index_1++; + m_index_2= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_GLOBAL_BY_EVENT_NAME. */ +class table_ews_global_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_ews_global_by_event_name(); + +public: + ~table_ews_global_by_event_name() = default; + +protected: + void make_mutex_row(PFS_mutex_class *klass); + void make_rwlock_row(PFS_rwlock_class *klass); + void make_cond_row(PFS_cond_class *klass); + void make_file_row(PFS_file_class *klass); + void make_table_io_row(PFS_instr_class *klass); + void make_table_lock_row(PFS_instr_class *klass); + void make_socket_row(PFS_socket_class *klass); + void make_idle_row(PFS_instr_class *klass); + void make_metadata_row(PFS_instr_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_ews_global_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_ews_global_by_event_name m_pos; + /** Next position. */ + pos_ews_global_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_file_instances.cc b/storage/perfschema/table_file_instances.cc new file mode 100644 index 00000000..5af6f055 --- /dev/null +++ b/storage/perfschema/table_file_instances.cc @@ -0,0 +1,180 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_file_instances.cc + Table FILE_INSTANCES (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_file_instances.h" +#include "pfs_global.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_file_instances::m_table_lock; +PFS_engine_table_share_state +table_file_instances::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_file_instances::m_share= +{ + { C_STRING_WITH_LEN("file_instances") }, + &pfs_readonly_acl, + table_file_instances::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_file_instances::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE file_instances(" + "FILE_NAME VARCHAR(512) not null comment 'File name.'," + "EVENT_NAME VARCHAR(128) not null comment 'Instrument name associated with the file.'," + "OPEN_COUNT INTEGER unsigned not null comment 'Open handles on the file. A value of greater than zero means that the file is currently open.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_file_instances::create(void) +{ + return new table_file_instances(); +} + +ha_rows +table_file_instances::get_row_count(void) +{ + return global_file_container.get_row_count(); +} + +table_file_instances::table_file_instances() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_file_instances::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_file_instances::rnd_next(void) +{ + PFS_file *pfs; + + m_pos.set_at(&m_next_pos); + PFS_file_iterator it= global_file_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_file_instances::rnd_pos(const void *pos) +{ + PFS_file *pfs; + + set_position(pos); + + pfs= global_file_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_file_instances::make_row(PFS_file *pfs) +{ + pfs_optimistic_state lock; + PFS_file_class *safe_class; + + m_row_exists= false; + + /* Protect this reader against a file delete */ + pfs->m_lock.begin_optimistic_lock(&lock); + + safe_class= sanitize_file_class(pfs->m_class); + if (unlikely(safe_class == NULL)) + return; + + m_row.m_filename= pfs->m_filename; + m_row.m_filename_length= pfs->m_filename_length; + m_row.m_event_name= safe_class->m_name; + m_row.m_event_name_length= safe_class->m_name_length; + m_row.m_open_count= pfs->m_file_stat.m_open_count; + + if (pfs->m_lock.end_optimistic_lock(&lock)) + m_row_exists= true; +} + +int table_file_instances::read_row_values(TABLE *table, + unsigned char *, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* FILENAME */ + set_field_varchar_utf8(f, m_row.m_filename, m_row.m_filename_length); + break; + case 1: /* EVENT_NAME */ + set_field_varchar_utf8(f, m_row.m_event_name, + m_row.m_event_name_length); + break; + case 2: /* OPEN_COUNT */ + set_field_ulong(f, m_row.m_open_count); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_file_instances.h b/storage/perfschema/table_file_instances.h new file mode 100644 index 00000000..5b1b9016 --- /dev/null +++ b/storage/perfschema/table_file_instances.h @@ -0,0 +1,96 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_FILE_INSTANCES_H +#define TABLE_FILE_INSTANCES_H + +/** + @file storage/perfschema/table_file_instances.h + Table FILE_INSTANCES (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.FILE_INSTANCES. */ +struct row_file_instances +{ + /** Column FILE_NAME. */ + const char *m_filename; + /** Length in bytes of @c m_filename. */ + uint m_filename_length; + /** Column EVENT_NAME. */ + const char *m_event_name; + /** Length in bytes of @c m_event_name. */ + uint m_event_name_length; + /** Column OPEN_COUNT. */ + uint m_open_count; +}; + +/** Table PERFORMANCE_SCHEMA.FILE_INSTANCES. */ +class table_file_instances : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_file_instances(); + +public: + ~table_file_instances() = default; + +private: + void make_row(PFS_file *pfs); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_file_instances m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_file_summary_by_event_name.cc b/storage/perfschema/table_file_summary_by_event_name.cc new file mode 100644 index 00000000..fc9006c2 --- /dev/null +++ b/storage/perfschema/table_file_summary_by_event_name.cc @@ -0,0 +1,265 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_file_summary_by_event_name.cc + Table FILE_SUMMARY_BY_EVENT_NAME(implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_file_summary_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "field.h" + +THR_LOCK table_file_summary_by_event_name::m_table_lock; +PFS_engine_table_share_state +table_file_summary_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_file_summary_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("file_summary_by_event_name") }, + &pfs_truncatable_acl, + table_file_summary_by_event_name::create, + NULL, /* write_row */ + table_file_summary_by_event_name::delete_all_rows, + table_file_summary_by_event_name::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE file_summary_by_event_name(" + "EVENT_NAME VARCHAR(128) not null comment 'Event name.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.'," + "COUNT_READ BIGINT unsigned not null comment 'Number of all read operations, including FGETS, FGETC, FREAD, and READ.'," + "SUM_TIMER_READ BIGINT unsigned not null comment 'Total wait time of all read operations that are timed.'," + "MIN_TIMER_READ BIGINT unsigned not null comment 'Minimum wait time of all read operations that are timed.'," + "AVG_TIMER_READ BIGINT unsigned not null comment 'Average wait time of all read operations that are timed.'," + "MAX_TIMER_READ BIGINT unsigned not null comment 'Maximum wait time of all read operations that are timed.'," + "SUM_NUMBER_OF_BYTES_READ BIGINT not null comment 'Bytes read by read operations.'," + "COUNT_WRITE BIGINT unsigned not null comment 'Number of all write operations, including FPUTS, FPUTC, FPRINTF, VFPRINTF, FWRITE, and PWRITE.'," + "SUM_TIMER_WRITE BIGINT unsigned not null comment 'Total wait time of all write operations that are timed.'," + "MIN_TIMER_WRITE BIGINT unsigned not null comment 'Minimum wait time of all write operations that are timed.'," + "AVG_TIMER_WRITE BIGINT unsigned not null comment 'Average wait time of all write operations that are timed.'," + "MAX_TIMER_WRITE BIGINT unsigned not null comment 'Maximum wait time of all write operations that are timed.'," + "SUM_NUMBER_OF_BYTES_WRITE BIGINT not null comment 'Bytes written by write operations.'," + "COUNT_MISC BIGINT unsigned not null comment 'Number of all miscellaneous operations not counted above, including CREATE, DELETE, OPEN, CLOSE, STREAM_OPEN, STREAM_CLOSE, SEEK, TELL, FLUSH, STAT, FSTAT, CHSIZE, RENAME, and SYNC.'," + "SUM_TIMER_MISC BIGINT unsigned not null comment 'Total wait time of all miscellaneous operations that are timed.'," + "MIN_TIMER_MISC BIGINT unsigned not null comment 'Minimum wait time of all miscellaneous operations that are timed.'," + "AVG_TIMER_MISC BIGINT unsigned not null comment 'Average wait time of all miscellaneous operations that are timed.'," + "MAX_TIMER_MISC BIGINT unsigned not null comment 'Maximum wait time of all miscellaneous operations that are timed.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_file_summary_by_event_name::create(void) +{ + return new table_file_summary_by_event_name(); +} + +int table_file_summary_by_event_name::delete_all_rows(void) +{ + reset_file_instance_io(); + reset_file_class_io(); + return 0; +} + +ha_rows +table_file_summary_by_event_name::get_row_count(void) +{ + return file_class_max; +} + +table_file_summary_by_event_name::table_file_summary_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_pos(1), m_next_pos(1) +{} + +void table_file_summary_by_event_name::reset_position(void) +{ + m_pos.m_index= 1; + m_next_pos.m_index= 1; +} + +int table_file_summary_by_event_name::rnd_next(void) +{ + PFS_file_class *file_class; + + m_pos.set_at(&m_next_pos); + + file_class= find_file_class(m_pos.m_index); + if (file_class) + { + make_row(file_class); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_file_summary_by_event_name::rnd_pos(const void *pos) +{ + PFS_file_class *file_class; + + set_position(pos); + + file_class= find_file_class(m_pos.m_index); + if (file_class) + { + make_row(file_class); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +/** + Build a row. + @param file_class the file class the cursor is reading +*/ +void table_file_summary_by_event_name::make_row(PFS_file_class *file_class) +{ + m_row.m_event_name.make_row(file_class); + + PFS_instance_file_io_stat_visitor visitor; + PFS_instance_iterator::visit_file_instances(file_class, &visitor); + + time_normalizer *normalizer= time_normalizer::get(wait_timer); + + /* Collect timer and byte count stats */ + m_row.m_io_stat.set(normalizer, &visitor.m_file_io_stat); + m_row_exists= true; + +} + +int table_file_summary_by_event_name::read_row_values(TABLE *table, + unsigned char *, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(!m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + case 1: /* COUNT_STAR */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_count); + break; + case 2: /* SUM_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_sum); + break; + case 3: /* MIN_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_min); + break; + case 4: /* AVG_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_avg); + break; + case 5: /* MAX_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_max); + break; + + case 6: /* COUNT_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_count); + break; + case 7: /* SUM_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_sum); + break; + case 8: /* MIN_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_min); + break; + case 9: /* AVG_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_avg); + break; + case 10: /* MAX_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_max); + break; + case 11: /* SUM_NUMBER_OF_BYTES_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_bytes); + break; + + case 12: /* COUNT_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_count); + break; + case 13: /* SUM_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_sum); + break; + case 14: /* MIN_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_min); + break; + case 15: /* AVG_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_avg); + break; + case 16: /* MAX_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_max); + break; + case 17: /* SUM_NUMBER_OF_BYTES_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_bytes); + break; + + case 18: /* COUNT_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_count); + break; + case 19: /* SUM_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_sum); + break; + case 20: /* MIN_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_min); + break; + case 21: /* AVG_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_avg); + break; + case 22: /* MAX_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_max); + break; + + default: + assert(false); + break; + } + } // if + } // for + + return 0; +} diff --git a/storage/perfschema/table_file_summary_by_event_name.h b/storage/perfschema/table_file_summary_by_event_name.h new file mode 100644 index 00000000..80b08773 --- /dev/null +++ b/storage/perfschema/table_file_summary_by_event_name.h @@ -0,0 +1,97 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_FILE_SUMMARY_H +#define TABLE_FILE_SUMMARY_H + +/** + @file storage/perfschema/table_file_summary_by_event_name.h + Table FILE_SUMMARY_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.FILE_SUMMARY_BY_EVENT_NAME. */ +struct row_file_summary_by_event_name +{ + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER and NUMBER_OF_BYTES + for READ, WRITE and MISC operation types. + */ + PFS_file_io_stat_row m_io_stat; +}; + +/** Table PERFORMANCE_SCHEMA.FILE_SUMMARY_BY_EVENT_NAME. */ +class table_file_summary_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_file_summary_by_event_name(); + +public: + ~table_file_summary_by_event_name() = default; + +private: + void make_row(PFS_file_class *klass); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_file_summary_by_event_name m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_file_summary_by_instance.cc b/storage/perfschema/table_file_summary_by_instance.cc new file mode 100644 index 00000000..89e08d49 --- /dev/null +++ b/storage/perfschema/table_file_summary_by_instance.cc @@ -0,0 +1,286 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_file_summary_by_instance.cc + Table FILE_SUMMARY_BY_INSTANCE (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_file_summary_by_instance.h" +#include "pfs_global.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_file_summary_by_instance::m_table_lock; + +PFS_engine_table_share_state +table_file_summary_by_instance::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_file_summary_by_instance::m_share= +{ + { C_STRING_WITH_LEN("file_summary_by_instance") }, + &pfs_truncatable_acl, + table_file_summary_by_instance::create, + NULL, /* write_row */ + table_file_summary_by_instance::delete_all_rows, + table_file_summary_by_instance::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE file_summary_by_instance(" + "FILE_NAME VARCHAR(512) not null comment 'File name.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name.'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null comment 'Address in memory. Together with FILE_NAME and EVENT_NAME uniquely identifies a row.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.'," + "COUNT_READ BIGINT unsigned not null comment 'Number of all read operations, including FGETS, FGETC, FREAD, and READ.'," + "SUM_TIMER_READ BIGINT unsigned not null comment 'Total wait time of all read operations that are timed.'," + "MIN_TIMER_READ BIGINT unsigned not null comment 'Minimum wait time of all read operations that are timed.'," + "AVG_TIMER_READ BIGINT unsigned not null comment 'Average wait time of all read operations that are timed.'," + "MAX_TIMER_READ BIGINT unsigned not null comment 'Maximum wait time of all read operations that are timed.'," + "SUM_NUMBER_OF_BYTES_READ BIGINT not null comment 'Bytes read by read operations.'," + "COUNT_WRITE BIGINT unsigned not null comment 'Number of all write operations, including FPUTS, FPUTC, FPRINTF, VFPRINTF, FWRITE, and PWRITE.'," + "SUM_TIMER_WRITE BIGINT unsigned not null comment 'Total wait time of all write operations that are timed.'," + "MIN_TIMER_WRITE BIGINT unsigned not null comment 'Minimum wait time of all write operations that are timed.'," + "AVG_TIMER_WRITE BIGINT unsigned not null comment 'Average wait time of all write operations that are timed.'," + "MAX_TIMER_WRITE BIGINT unsigned not null comment 'Maximum wait time of all write operations that are timed.'," + "SUM_NUMBER_OF_BYTES_WRITE BIGINT not null comment 'Bytes written by write operations.'," + "COUNT_MISC BIGINT unsigned not null comment 'Number of all miscellaneous operations not counted above, including CREATE, DELETE, OPEN, CLOSE, STREAM_OPEN, STREAM_CLOSE, SEEK, TELL, FLUSH, STAT, FSTAT, CHSIZE, RENAME, and SYNC.'," + "SUM_TIMER_MISC BIGINT unsigned not null comment 'Total wait time of all miscellaneous operations that are timed.'," + "MIN_TIMER_MISC BIGINT unsigned not null comment 'Minimum wait time of all miscellaneous operations that are timed.'," + "AVG_TIMER_MISC BIGINT unsigned not null comment 'Average wait time of all miscellaneous operations that are timed.'," + "MAX_TIMER_MISC BIGINT unsigned not null comment 'Maximum wait time of all miscellaneous operations that are timed.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_file_summary_by_instance::create(void) +{ + return new table_file_summary_by_instance(); +} + +int table_file_summary_by_instance::delete_all_rows(void) +{ + reset_file_instance_io(); + return 0; +} + +ha_rows +table_file_summary_by_instance::get_row_count(void) +{ + return global_file_container.get_row_count(); +} + +table_file_summary_by_instance::table_file_summary_by_instance() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_file_summary_by_instance::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_file_summary_by_instance::rnd_next(void) +{ + PFS_file *pfs; + + m_pos.set_at(&m_next_pos); + PFS_file_iterator it= global_file_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_file_summary_by_instance::rnd_pos(const void *pos) +{ + PFS_file *pfs; + + set_position(pos); + + pfs= global_file_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +/** + Build a row. + @param pfs the file the cursor is reading +*/ +void table_file_summary_by_instance::make_row(PFS_file *pfs) +{ + pfs_optimistic_state lock; + PFS_file_class *safe_class; + + m_row_exists= false; + + /* Protect this reader against a file delete */ + pfs->m_lock.begin_optimistic_lock(&lock); + + safe_class= sanitize_file_class(pfs->m_class); + if (unlikely(safe_class == NULL)) + return; + + m_row.m_filename= pfs->m_filename; + m_row.m_filename_length= pfs->m_filename_length; + m_row.m_event_name.make_row(safe_class); + m_row.m_identity= pfs->m_identity; + + time_normalizer *normalizer= time_normalizer::get(wait_timer); + + /* Collect timer and byte count stats */ + m_row.m_io_stat.set(normalizer, &pfs->m_file_stat.m_io_stat); + + if (pfs->m_lock.end_optimistic_lock(&lock)) + m_row_exists= true; +} + +int table_file_summary_by_instance::read_row_values(TABLE *table, + unsigned char *, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* FILE_NAME */ + set_field_varchar_utf8(f, m_row.m_filename, m_row.m_filename_length); + break; + case 1: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + case 2: /* OBJECT_INSTANCE */ + set_field_ulonglong(f, (ulonglong)m_row.m_identity); + break; + + case 3:/* COUNT_STAR */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_count); + break; + case 4:/* SUM_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_sum); + break; + case 5: /* MIN_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_min); + break; + case 6: /* AVG_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_avg); + break; + case 7: /* MAX_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_max); + break; + + case 8: /* COUNT_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_count); + break; + case 9: /* SUM_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_sum); + break; + case 10: /* MIN_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_min); + break; + case 11: /* AVG_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_avg); + break; + case 12: /* MAX_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_max); + break; + case 13: /* SUM_NUMBER_OF_BYTES_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_bytes); + break; + + case 14: /* COUNT_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_count); + break; + case 15: /* SUM_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_sum); + break; + case 16: /* MIN_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_min); + break; + case 17: /* AVG_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_avg); + break; + case 18: /* MAX_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_max); + break; + case 19: /* SUM_NUMBER_OF_BYTES_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_bytes); + break; + + case 20: /* COUNT_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_count); + break; + case 21: /* SUM_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_sum); + break; + case 22: /* MIN_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_min); + break; + case 23: /* AVG_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_avg); + break; + case 24: /* MAX_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_max); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_file_summary_by_instance.h b/storage/perfschema/table_file_summary_by_instance.h new file mode 100644 index 00000000..750ba904 --- /dev/null +++ b/storage/perfschema/table_file_summary_by_instance.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_FILE_SUMMARY_BY_INSTANCE_H +#define TABLE_FILE_SUMMARY_BY_INSTANCE_H + +/** + @file storage/perfschema/table_file_summary_by_instance.h + Table FILE_SUMMARY_BY_INSTANCE (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.FILE_SUMMARY_BY_INSTANCE. */ +struct row_file_summary_by_instance +{ + /** Column FILE_NAME. */ + const char *m_filename; + /** Length in bytes of @c m_filename. */ + uint m_filename_length; + + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + + /** Column OBJECT_INSTANCE_BEGIN */ + const void *m_identity; + /** + Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER and NUMBER_OF_BYTES for READ, + WRITE and MISC operation types. + */ + PFS_file_io_stat_row m_io_stat; +}; + +/** Table PERFORMANCE_SCHEMA.FILE_SUMMARY_BY_INSTANCE. */ +class table_file_summary_by_instance : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_file_summary_by_instance(); + +public: + ~table_file_summary_by_instance() = default; + +private: + void make_row(PFS_file *pfs); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_file_summary_by_instance m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_global_status.cc b/storage/perfschema/table_global_status.cc new file mode 100644 index 00000000..08531b55 --- /dev/null +++ b/storage/perfschema/table_global_status.cc @@ -0,0 +1,197 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_global_status.cc + Table global_status (implementation). +*/ + +#include "my_global.h" +#include "table_global_status.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "pfs_global.h" + +THR_LOCK table_global_status::m_table_lock; + +PFS_engine_table_share_state +table_global_status::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_global_status::m_share= +{ + { C_STRING_WITH_LEN("global_status") }, + &pfs_truncatable_world_acl, + table_global_status::create, + NULL, /* write_row */ + table_global_status::delete_all_rows, + table_global_status::get_row_count, + sizeof(pos_t), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE global_status(" + "VARIABLE_NAME VARCHAR(64) not null comment 'The global status variable name.'," + "VARIABLE_VALUE VARCHAR(1024) comment 'The global status variable value.')") }, + true, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_global_status::create(void) +{ + return new table_global_status(); +} + +int table_global_status::delete_all_rows(void) +{ + mysql_mutex_lock(&LOCK_status); + reset_status_by_thread(); + reset_status_by_account(); + reset_status_by_user(); + reset_status_by_host(); + reset_global_status(); + mysql_mutex_unlock(&LOCK_status); + return 0; +} + +ha_rows table_global_status::get_row_count(void) +{ + mysql_mutex_lock(&LOCK_status); + ha_rows status_var_count= all_status_vars.elements; + mysql_mutex_unlock(&LOCK_status); + return status_var_count; +} + +table_global_status::table_global_status() + : PFS_engine_table(&m_share, &m_pos), + m_status_cache(false), m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_global_status::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_global_status::rnd_init(bool scan) +{ + /* Build a cache of all global status variables. Sum across threads. */ + m_status_cache.materialize_global(); + + /* Record the current number of status variables to detect subsequent changes. */ + ulonglong status_version= m_status_cache.get_status_array_version(); + + /* + The table context holds the current version of the global status array. + If scan == true, then allocate a new context from mem_root and store in TLS. + If scan == false, then restore from TLS. + */ + m_context= (table_global_status_context *)current_thd->alloc(sizeof(table_global_status_context)); + new(m_context) table_global_status_context(status_version, !scan); + return 0; +} + +int table_global_status::rnd_next(void) +{ + for (m_pos.set_at(&m_next_pos); + m_pos.m_index < m_status_cache.size(); + m_pos.next()) + { + const Status_variable *status_var= m_status_cache.get(m_pos.m_index); + if (status_var != NULL) + { + make_row(status_var); + m_next_pos.set_after(&m_pos); + return 0; + } + } + return HA_ERR_END_OF_FILE; +} + +int table_global_status::rnd_pos(const void *pos) +{ + /* If global status array has changed, do nothing. */ // TODO: Issue warning + if (!m_context->versions_match()) + return HA_ERR_RECORD_DELETED; + + set_position(pos); + const Status_variable *status_var= m_status_cache.get(m_pos.m_index); + if (status_var != NULL) + { + make_row(status_var); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_global_status +::make_row(const Status_variable *status_var) +{ + m_row_exists= false; + if (status_var->is_null()) + return; + m_row.m_variable_name.make_row(status_var->m_name, status_var->m_name_length); + m_row.m_variable_value.make_row(status_var); + m_row_exists= true; +} + +int table_global_status +::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* VARIABLE_NAME */ + set_field_varchar_utf8(f, m_row.m_variable_name.m_str, m_row.m_variable_name.m_length); + break; + case 1: /* VARIABLE_VALUE */ + m_row.m_variable_value.set_field(f); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_global_status.h b/storage/perfschema/table_global_status.h new file mode 100644 index 00000000..5b5f6e60 --- /dev/null +++ b/storage/perfschema/table_global_status.h @@ -0,0 +1,119 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef TABLE_GLOBAL_STATUS_H +#define TABLE_GLOBAL_STATUS_H + +/** + @file storage/perfschema/table_global_status.h + Table global_status (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_variable.h" +#include "table_helper.h" +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.GLOBAL_STATUS. +*/ +struct row_global_status +{ + /** Column VARIABLE_NAME. */ + PFS_variable_name_row m_variable_name; + /** Column VARIABLE_VALUE. */ + PFS_variable_value_row m_variable_value; +}; + +/** + Store and retrieve table state information for queries that reinstantiate + the table object. +*/ +class table_global_status_context : public PFS_table_context +{ +public: + table_global_status_context(ulonglong current_version, bool restore) : + PFS_table_context(current_version, restore, THR_PFS_SG) { } +}; + +/** Table PERFORMANCE_SCHEMA.GLOBAL_STATUS. */ +class table_global_status : public PFS_engine_table +{ + typedef PFS_simple_index pos_t; + +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + table_global_status(); + +public: + ~table_global_status() + {} + +protected: + void make_row(const Status_variable *system_var); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current THD variables. */ + PFS_status_variable_cache m_status_cache; + /** Current row. */ + row_global_status m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + + /** Table context with global status array version. */ + table_global_status_context *m_context; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_global_variables.cc b/storage/perfschema/table_global_variables.cc new file mode 100644 index 00000000..39bf2243 --- /dev/null +++ b/storage/perfschema/table_global_variables.cc @@ -0,0 +1,190 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_global_variables.cc + Table GLOBAL_VARIABLES (implementation). +*/ + +#include "my_global.h" +#include "table_global_variables.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "pfs_global.h" + +THR_LOCK table_global_variables::m_table_lock; + +PFS_engine_table_share_state +table_global_variables::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_global_variables::m_share= +{ + { C_STRING_WITH_LEN("global_variables") }, + &pfs_readonly_world_acl, + table_global_variables::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_global_variables::get_row_count, + sizeof(pos_t), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE global_variables(" + "VARIABLE_NAME VARCHAR(64) not null," + "VARIABLE_VALUE VARCHAR(1024))") }, + true, /* m_perpetual */ + &m_share_state +}; + +PFS_engine_table* +table_global_variables::create(void) +{ + return new table_global_variables(); +} + +ha_rows table_global_variables::get_row_count(void) +{ + mysql_mutex_lock(&LOCK_plugin_delete); + mysql_prlock_rdlock(&LOCK_system_variables_hash); + ha_rows system_var_count= get_system_variable_hash_records(); + mysql_prlock_unlock(&LOCK_system_variables_hash); + mysql_mutex_unlock(&LOCK_plugin_delete); + return system_var_count; +} + +table_global_variables::table_global_variables() + : PFS_engine_table(&m_share, &m_pos), + m_sysvar_cache(false), m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_global_variables::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_global_variables::rnd_init(bool scan) +{ + /* + Build a list of system variables from the global system variable hash. + Filter by scope. + */ + m_sysvar_cache.materialize_global(); + + /* Record the version of the system variable hash. */ + ulonglong hash_version= m_sysvar_cache.get_sysvar_hash_version(); + + /* + The table context holds the current version of the system variable hash. + If scan == true, then allocate a new context from mem_root and store in TLS. + If scan == false, then restore from TLS. + */ + m_context= (table_global_variables_context *)current_thd->alloc(sizeof(table_global_variables_context)); + new(m_context) table_global_variables_context(hash_version, !scan); + return 0; +} + +int table_global_variables::rnd_next(void) +{ + for (m_pos.set_at(&m_next_pos); + m_pos.m_index < m_sysvar_cache.size(); + m_pos.next()) + { + const System_variable *system_var= m_sysvar_cache.get(m_pos.m_index); + if (system_var != NULL) + { + make_row(system_var); + m_next_pos.set_after(&m_pos); + return 0; + } + } + return HA_ERR_END_OF_FILE; +} + +int table_global_variables::rnd_pos(const void *pos) +{ + /* If system variable hash changes, do nothing. */ // TODO: Issue warning + if (!m_context->versions_match()) + return HA_ERR_RECORD_DELETED; + + set_position(pos); + assert(m_pos.m_index < m_sysvar_cache.size()); + + const System_variable *system_var= m_sysvar_cache.get(m_pos.m_index); + if (system_var != NULL) + { + make_row(system_var); + return 0; + } + return HA_ERR_RECORD_DELETED; +} + +void table_global_variables +::make_row(const System_variable *system_var) +{ + m_row_exists= false; + if (system_var->is_null() || system_var->is_ignored()) + return; + m_row.m_variable_name.make_row(system_var->m_name, system_var->m_name_length); + m_row.m_variable_value.make_row(system_var); + m_row_exists= true; +} + +int table_global_variables +::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* VARIABLE_NAME */ + set_field_varchar_utf8(f, m_row.m_variable_name.m_str, m_row.m_variable_name.m_length); + break; + case 1: /* VARIABLE_VALUE */ + m_row.m_variable_value.set_field(f); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_global_variables.h b/storage/perfschema/table_global_variables.h new file mode 100644 index 00000000..49083e63 --- /dev/null +++ b/storage/perfschema/table_global_variables.h @@ -0,0 +1,118 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef TABLE_GLOBAL_VARIABLES_H +#define TABLE_GLOBAL_VARIABLES_H + +/** + @file storage/perfschema/table_global_variables.h + Table GLOBAL_VARIABLES (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_variable.h" +#include "table_helper.h" +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + Store and retrieve table state information during queries that reinstantiate + the table object. +*/ +class table_global_variables_context : public PFS_table_context +{ +public: + table_global_variables_context(ulonglong hash_version, bool restore) : + PFS_table_context(hash_version, restore, THR_PFS_VG) {} +}; + +/** + A row of table + PERFORMANCE_SCHEMA.GLOBAL_VARIABLES. +*/ +struct row_global_variables +{ + /** Column VARIABLE_NAME. */ + PFS_variable_name_row m_variable_name; + /** Column VARIABLE_VALUE. */ + PFS_variable_value_row m_variable_value; +}; + +/** Table PERFORMANCE_SCHEMA.GLOBAL_VARIABLES. */ +class table_global_variables : public PFS_engine_table +{ + typedef PFS_simple_index pos_t; + +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + table_global_variables(); + +public: + ~table_global_variables() + {} + +protected: + void make_row(const System_variable *system_var); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current THD variables. */ + PFS_system_variable_cache m_sysvar_cache; + /** Current row. */ + row_global_variables m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + + /** Table context with system variable hash version. */ + table_global_variables_context *m_context; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_helper.cc b/storage/perfschema/table_helper.cc new file mode 100644 index 00000000..dd5a765f --- /dev/null +++ b/storage/perfschema/table_helper.cc @@ -0,0 +1,896 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_helper.cc + Performance schema table helpers (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_engine_table.h" +#include "table_helper.h" +#include "pfs_host.h" +#include "pfs_user.h" +#include "pfs_account.h" +#include "pfs_instr.h" +#include "pfs_program.h" +#include "field.h" +#include "pfs_variable.h" + +int PFS_host_row::make_row(PFS_host *pfs) +{ + m_hostname_length= pfs->m_hostname_length; + if (m_hostname_length > sizeof(m_hostname)) + return 1; + if (m_hostname_length > 0) + memcpy(m_hostname, pfs->m_hostname, sizeof(m_hostname)); + return 0; +} + +void PFS_host_row::set_field(Field *f) +{ + if (m_hostname_length > 0) + PFS_engine_table::set_field_char_utf8(f, m_hostname, m_hostname_length); + else + f->set_null(); +} + +int PFS_user_row::make_row(PFS_user *pfs) +{ + m_username_length= pfs->m_username_length; + if (m_username_length > sizeof(m_username)) + return 1; + if (m_username_length > 0) + memcpy(m_username, pfs->m_username, sizeof(m_username)); + return 0; +} + +void PFS_user_row::set_field(Field *f) +{ + if (m_username_length > 0) + PFS_engine_table::set_field_char_utf8(f, m_username, m_username_length); + else + f->set_null(); +} + +int PFS_account_row::make_row(PFS_account *pfs) +{ + m_username_length= pfs->m_username_length; + if (m_username_length > sizeof(m_username)) + return 1; + if (m_username_length > 0) + memcpy(m_username, pfs->m_username, sizeof(m_username)); + + m_hostname_length= pfs->m_hostname_length; + if (m_hostname_length > sizeof(m_hostname)) + return 1; + if (m_hostname_length > 0) + memcpy(m_hostname, pfs->m_hostname, sizeof(m_hostname)); + + return 0; +} + +void PFS_account_row::set_field(uint index, Field *f) +{ + switch (index) + { + case 0: /* USER */ + if (m_username_length > 0) + PFS_engine_table::set_field_char_utf8(f, m_username, m_username_length); + else + f->set_null(); + break; + case 1: /* HOST */ + if (m_hostname_length > 0) + PFS_engine_table::set_field_char_utf8(f, m_hostname, m_hostname_length); + else + f->set_null(); + break; + default: + assert(false); + break; + } +} + +int PFS_digest_row::make_row(PFS_statements_digest_stat* pfs) +{ + m_schema_name_length= pfs->m_digest_key.m_schema_name_length; + if (m_schema_name_length > sizeof(m_schema_name)) + m_schema_name_length= 0; + if (m_schema_name_length > 0) + memcpy(m_schema_name, pfs->m_digest_key.m_schema_name, m_schema_name_length); + + size_t safe_byte_count= pfs->m_digest_storage.m_byte_count; + if (safe_byte_count > pfs_max_digest_length) + safe_byte_count= 0; + + /* + "0" value for byte_count indicates special entry i.e. aggregated + stats at index 0 of statements_digest_stat_array. So do not calculate + digest/digest_text as it should always be "NULL". + */ + if (safe_byte_count > 0) + { + /* + Calculate digest from MD5 HASH collected to be shown as + DIGEST in this row. + */ + MD5_HASH_TO_STRING(pfs->m_digest_storage.m_md5, m_digest); + m_digest_length= MD5_HASH_TO_STRING_LENGTH; + + /* + Calculate digest_text information from the token array collected + to be shown as DIGEST_TEXT column. + */ + compute_digest_text(&pfs->m_digest_storage, &m_digest_text); + + if (m_digest_text.length() == 0) + m_digest_length= 0; + } + else + { + m_digest_length= 0; + } + + return 0; +} + +void PFS_digest_row::set_field(uint index, Field *f) +{ + switch (index) + { + case 0: /* SCHEMA_NAME */ + if (m_schema_name_length > 0) + PFS_engine_table::set_field_varchar_utf8(f, m_schema_name, + m_schema_name_length); + else + f->set_null(); + break; + case 1: /* DIGEST */ + if (m_digest_length > 0) + PFS_engine_table::set_field_varchar_utf8(f, m_digest, + m_digest_length); + else + f->set_null(); + break; + case 2: /* DIGEST_TEXT */ + if (m_digest_text.length() > 0) + PFS_engine_table::set_field_longtext_utf8(f, m_digest_text.ptr(), + m_digest_text.length()); + else + f->set_null(); + break; + default: + assert(false); + break; + } +} + +int PFS_object_row::make_row(PFS_table_share *pfs) +{ + m_object_type= pfs->get_object_type(); + + m_schema_name_length= pfs->m_schema_name_length; + if (m_schema_name_length > sizeof(m_schema_name)) + return 1; + if (m_schema_name_length > 0) + memcpy(m_schema_name, pfs->m_schema_name, sizeof(m_schema_name)); + + m_object_name_length= pfs->m_table_name_length; + if (m_object_name_length > sizeof(m_object_name)) + return 1; + if (m_object_name_length > 0) + memcpy(m_object_name, pfs->m_table_name, sizeof(m_object_name)); + + return 0; +} + +int PFS_object_row::make_row(PFS_program *pfs) +{ + m_object_type= pfs->m_type; + + m_schema_name_length= pfs->m_schema_name_length; + if (m_schema_name_length > sizeof(m_schema_name)) + return 1; + if (m_schema_name_length > 0) + memcpy(m_schema_name, pfs->m_schema_name, sizeof(m_schema_name)); + + m_object_name_length= pfs->m_object_name_length; + if (m_object_name_length > sizeof(m_object_name)) + return 1; + if (m_object_name_length > 0) + memcpy(m_object_name, pfs->m_object_name, sizeof(m_object_name)); + + return 0; +} + +int PFS_object_row::make_row(const MDL_key *mdl) +{ + MDL_key user_lock_workaround; + switch(mdl->mdl_namespace()) + { + case MDL_key::BACKUP: + m_object_type= OBJECT_TYPE_BACKUP; + m_schema_name_length= 0; + m_object_name_length= 0; + break; + case MDL_key::SCHEMA: + m_object_type= OBJECT_TYPE_SCHEMA; + m_schema_name_length= mdl->db_name_length(); + m_object_name_length= 0; + break; + case MDL_key::TABLE: + m_object_type= OBJECT_TYPE_TABLE; + m_schema_name_length= mdl->db_name_length(); + m_object_name_length= mdl->name_length(); + break; + case MDL_key::FUNCTION: + m_object_type= OBJECT_TYPE_FUNCTION; + m_schema_name_length= mdl->db_name_length(); + m_object_name_length= mdl->name_length(); + break; + case MDL_key::PROCEDURE: + m_object_type= OBJECT_TYPE_PROCEDURE; + m_schema_name_length= mdl->db_name_length(); + m_object_name_length= mdl->name_length(); + break; + case MDL_key::PACKAGE_BODY: + m_object_type= OBJECT_TYPE_PACKAGE_BODY; + m_schema_name_length= mdl->db_name_length(); + m_object_name_length= mdl->name_length(); + break; + case MDL_key::TRIGGER: + m_object_type= OBJECT_TYPE_TRIGGER; + m_schema_name_length= mdl->db_name_length(); + m_object_name_length= mdl->name_length(); + break; + case MDL_key::EVENT: + m_object_type= OBJECT_TYPE_EVENT; + m_schema_name_length= mdl->db_name_length(); + m_object_name_length= mdl->name_length(); + break; + case MDL_key::USER_LOCK: + m_object_type= OBJECT_TYPE_USER_LEVEL_LOCK; + user_lock_workaround.mdl_key_init(MDL_key::USER_LOCK, "", mdl->db_name()); + mdl=& user_lock_workaround; + m_schema_name_length= 0; + m_object_name_length= mdl->name_length(); + break; + case MDL_key::NAMESPACE_END: + default: + m_object_type= NO_OBJECT_TYPE; + m_schema_name_length= 0; + m_object_name_length= 0; + break; + } + + if (m_schema_name_length > sizeof(m_schema_name)) + return 1; + if (m_schema_name_length > 0) + memcpy(m_schema_name, mdl->db_name(), m_schema_name_length); + + if (m_object_name_length > sizeof(m_object_name)) + return 1; + if (m_object_name_length > 0) + memcpy(m_object_name, mdl->name(), m_object_name_length); + + return 0; +} + +void PFS_object_row::set_field(uint index, Field *f) +{ + switch(index) + { + case 0: /* OBJECT_TYPE */ + set_field_object_type(f, m_object_type); + break; + case 1: /* SCHEMA_NAME */ + PFS_engine_table::set_field_varchar_utf8(f, m_schema_name, m_schema_name_length); + break; + case 2: /* OBJECT_NAME */ + PFS_engine_table::set_field_varchar_utf8(f, m_object_name, m_object_name_length); + break; + default: + assert(false); + } +} + +void PFS_object_row::set_nullable_field(uint index, Field *f) +{ + switch(index) + { + case 0: /* OBJECT_TYPE */ + if (m_object_type != NO_OBJECT_TYPE) + set_field_object_type(f, m_object_type); + else + f->set_null(); + break; + case 1: /* SCHEMA_NAME */ + if (m_schema_name_length > 0) + PFS_engine_table::set_field_varchar_utf8(f, m_schema_name, m_schema_name_length); + else + f->set_null(); + break; + case 2: /* OBJECT_NAME */ + if (m_object_name_length > 0) + PFS_engine_table::set_field_varchar_utf8(f, m_object_name, m_object_name_length); + else + f->set_null(); + break; + default: + assert(false); + } +} + +int PFS_index_row::make_row(PFS_table_share *pfs, + PFS_table_share_index *pfs_index, + uint table_index) +{ + if (m_object_row.make_row(pfs)) + return 1; + + if (pfs_index == NULL) + { + if (table_index < MAX_INDEXES) + { + m_index_name_length= sprintf(m_index_name, "(index %d)", table_index); + } + else + { + m_index_name_length= 0; + } + return 0; + } + + if (table_index < MAX_INDEXES) + { + m_index_name_length= pfs_index->m_key.m_name_length; + if (m_index_name_length > sizeof(m_index_name)) + return 1; + + memcpy(m_index_name, pfs_index->m_key.m_name, sizeof(m_index_name)); + } + else + { + m_index_name_length= 0; + } + + return 0; +} + +void PFS_index_row::set_field(uint index, Field *f) +{ + switch(index) + { + case 0: /* OBJECT_TYPE */ + case 1: /* SCHEMA_NAME */ + case 2: /* OBJECT_NAME */ + m_object_row.set_field(index, f); + break; + case 3: /* INDEX_NAME */ + if (m_index_name_length > 0) + PFS_engine_table::set_field_varchar_utf8(f, m_index_name, m_index_name_length); + else + f->set_null(); + break; + default: + assert(false); + } +} + +void PFS_statement_stat_row::set_field(uint index, Field *f) +{ + switch (index) + { + case 0: /* COUNT_STAR */ + case 1: /* SUM_TIMER_WAIT */ + case 2: /* MIN_TIMER_WAIT */ + case 3: /* AVG_TIMER_WAIT */ + case 4: /* MAX_TIMER_WAIT */ + m_timer1_row.set_field(index, f); + break; + case 5: /* SUM_LOCK_TIME */ + PFS_engine_table::set_field_ulonglong(f, m_lock_time); + break; + case 6: /* SUM_ERRORS */ + PFS_engine_table::set_field_ulonglong(f, m_error_count); + break; + case 7: /* SUM_WARNINGS */ + PFS_engine_table::set_field_ulonglong(f, m_warning_count); + break; + case 8: /* SUM_ROWS_AFFECTED */ + PFS_engine_table::set_field_ulonglong(f, m_rows_affected); + break; + case 9: /* SUM_ROWS_SENT */ + PFS_engine_table::set_field_ulonglong(f, m_rows_sent); + break; + case 10: /* SUM_ROWS_EXAMINED */ + PFS_engine_table::set_field_ulonglong(f, m_rows_examined); + break; + case 11: /* SUM_CREATED_TMP_DISK_TABLES */ + PFS_engine_table::set_field_ulonglong(f, m_created_tmp_disk_tables); + break; + case 12: /* SUM_CREATED_TMP_TABLES */ + PFS_engine_table::set_field_ulonglong(f, m_created_tmp_tables); + break; + case 13: /* SUM_SELECT_FULL_JOIN */ + PFS_engine_table::set_field_ulonglong(f, m_select_full_join); + break; + case 14: /* SUM_SELECT_FULL_RANGE_JOIN */ + PFS_engine_table::set_field_ulonglong(f, m_select_full_range_join); + break; + case 15: /* SUM_SELECT_RANGE */ + PFS_engine_table::set_field_ulonglong(f, m_select_range); + break; + case 16: /* SUM_SELECT_RANGE_CHECK */ + PFS_engine_table::set_field_ulonglong(f, m_select_range_check); + break; + case 17: /* SUM_SELECT_SCAN */ + PFS_engine_table::set_field_ulonglong(f, m_select_scan); + break; + case 18: /* SUM_SORT_MERGE_PASSES */ + PFS_engine_table::set_field_ulonglong(f, m_sort_merge_passes); + break; + case 19: /* SUM_SORT_RANGE */ + PFS_engine_table::set_field_ulonglong(f, m_sort_range); + break; + case 20: /* SUM_SORT_ROWS */ + PFS_engine_table::set_field_ulonglong(f, m_sort_rows); + break; + case 21: /* SUM_SORT_SCAN */ + PFS_engine_table::set_field_ulonglong(f, m_sort_scan); + break; + case 22: /* SUM_NO_INDEX_USED */ + PFS_engine_table::set_field_ulonglong(f, m_no_index_used); + break; + case 23: /* SUM_NO_GOOD_INDEX_USED */ + PFS_engine_table::set_field_ulonglong(f, m_no_good_index_used); + break; + default: + assert(false); + break; + } +} + +void PFS_transaction_stat_row::set_field(uint index, Field *f) +{ + switch (index) + { + case 0: /* COUNT_STAR */ + case 1: /* SUM_TIMER_WAIT */ + case 2: /* MIN_TIMER_WAIT */ + case 3: /* AVG_TIMER_WAIT */ + case 4: /* MAX_TIMER_WAIT */ + m_timer1_row.set_field(index, f); + break; + case 5: /* COUNT_READ_WRITE */ + case 6: /* SUM_TIMER_READ_WRITE */ + case 7: /* MIN_TIMER_READ_WRITE */ + case 8: /* AVG_TIMER_READ_WRITE */ + case 9: /* MAX_TIMER_READ_WRITE */ + m_read_write_row.set_field(index-5, f); + break; + case 10: /* COUNT_READ_ONLY */ + case 11: /* SUM_TIMER_READ_ONLY */ + case 12: /* MIN_TIMER_READ_ONLY */ + case 13: /* AVG_TIMER_READ_ONLY */ + case 14: /* MAX_TIMER_READ_ONLY */ + m_read_only_row.set_field(index-10, f); + break; + default: + assert(false); + break; + } +} + +void PFS_connection_stat_row::set_field(uint index, Field *f) +{ + switch (index) + { + case 0: /* CURRENT_CONNECTIONS */ + PFS_engine_table::set_field_ulonglong(f, m_current_connections); + break; + case 1: /* TOTAL_CONNECTIONS */ + PFS_engine_table::set_field_ulonglong(f, m_total_connections); + break; + default: + assert(false); + break; + } +} + +void set_field_object_type(Field *f, enum_object_type object_type) +{ + switch (object_type) + { + case OBJECT_TYPE_EVENT: + PFS_engine_table::set_field_varchar_utf8(f, "EVENT", 5); + break; + case OBJECT_TYPE_FUNCTION: + PFS_engine_table::set_field_varchar_utf8(f, "FUNCTION", 8); + break; + case OBJECT_TYPE_PROCEDURE: + PFS_engine_table::set_field_varchar_utf8(f, "PROCEDURE", 9); + break; + case OBJECT_TYPE_TABLE: + PFS_engine_table::set_field_varchar_utf8(f, "TABLE", 5); + break; + case OBJECT_TYPE_TEMPORARY_TABLE: + PFS_engine_table::set_field_varchar_utf8(f, "TEMPORARY TABLE", 15); + break; + case OBJECT_TYPE_TRIGGER: + PFS_engine_table::set_field_varchar_utf8(f, "TRIGGER", 7); + break; + case OBJECT_TYPE_BACKUP: + PFS_engine_table::set_field_varchar_utf8(f, "BACKUP", 6); + break; + case OBJECT_TYPE_SCHEMA: + PFS_engine_table::set_field_varchar_utf8(f, "SCHEMA", 6); + break; + case OBJECT_TYPE_PACKAGE_BODY: + PFS_engine_table::set_field_varchar_utf8(f, "PACKAGE BODY", 12); + break; + case OBJECT_TYPE_USER_LEVEL_LOCK: + PFS_engine_table::set_field_varchar_utf8(f, "USER LEVEL LOCK", 15); + break; + case NO_OBJECT_TYPE: + default: + assert(false); + PFS_engine_table::set_field_varchar_utf8(f, "", 0); + break; + } +} + +void set_field_lock_type(Field *f, PFS_TL_LOCK_TYPE lock_type) +{ + switch (lock_type) + { + case PFS_TL_READ: + PFS_engine_table::set_field_varchar_utf8(f, "READ", 4); + break; + case PFS_TL_READ_WITH_SHARED_LOCKS: + PFS_engine_table::set_field_varchar_utf8(f, "READ WITH SHARED LOCKS", 22); + break; + case PFS_TL_READ_HIGH_PRIORITY: + PFS_engine_table::set_field_varchar_utf8(f, "READ HIGH PRIORITY", 18); + break; + case PFS_TL_READ_NO_INSERT: + PFS_engine_table::set_field_varchar_utf8(f, "READ NO INSERT", 14); + break; + case PFS_TL_WRITE_ALLOW_WRITE: + PFS_engine_table::set_field_varchar_utf8(f, "WRITE ALLOW WRITE", 17); + break; + case PFS_TL_WRITE_CONCURRENT_INSERT: + PFS_engine_table::set_field_varchar_utf8(f, "WRITE CONCURRENT INSERT", 23); + break; + case PFS_TL_WRITE_LOW_PRIORITY: + PFS_engine_table::set_field_varchar_utf8(f, "WRITE LOW PRIORITY", 18); + break; + case PFS_TL_WRITE: + PFS_engine_table::set_field_varchar_utf8(f, "WRITE", 5); + break; + case PFS_TL_READ_EXTERNAL: + PFS_engine_table::set_field_varchar_utf8(f, "READ EXTERNAL", 13); + break; + case PFS_TL_WRITE_EXTERNAL: + PFS_engine_table::set_field_varchar_utf8(f, "WRITE EXTERNAL", 14); + break; + case PFS_TL_NONE: + f->set_null(); + break; + default: + assert(false); + } +} + +void set_field_mdl_type(Field *f, opaque_mdl_type mdl_type, bool backup) +{ + if (backup) + { + switch (mdl_type) + { + case MDL_BACKUP_START: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_START")); + break; + case MDL_BACKUP_FLUSH: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_FLUSH")); + break; + case MDL_BACKUP_WAIT_FLUSH: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_WAIT_FLUSH")); + break; + case MDL_BACKUP_WAIT_DDL: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_WAIT_DDL")); + break; + case MDL_BACKUP_WAIT_COMMIT: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_WAIT_COMMIT")); + break; + case MDL_BACKUP_FTWRL1: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_FTWRL1")); + break; + case MDL_BACKUP_FTWRL2: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_FTWRL2")); + break; + case MDL_BACKUP_DML: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_DML")); + break; + case MDL_BACKUP_TRANS_DML: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_TRANS_DML")); + break; + case MDL_BACKUP_SYS_DML: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_SYS_DML")); + break; + case MDL_BACKUP_DDL: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_DDL")); + break; + case MDL_BACKUP_BLOCK_DDL: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_BLOCK_DDL")); + break; + case MDL_BACKUP_ALTER_COPY: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_ALTER_COPY")); + break; + case MDL_BACKUP_COMMIT: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_COMMIT")); + break; + case MDL_BACKUP_END: + PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_END")); + break; + default: + DBUG_ASSERT(false); + } + } + else + { + enum_mdl_type e= (enum_mdl_type) mdl_type; + switch (e) + { + case MDL_INTENTION_EXCLUSIVE: + PFS_engine_table::set_field_varchar_utf8(f, "INTENTION_EXCLUSIVE", 19); + break; + case MDL_SHARED: + PFS_engine_table::set_field_varchar_utf8(f, "SHARED", 6); + break; + case MDL_SHARED_HIGH_PRIO: + PFS_engine_table::set_field_varchar_utf8(f, "SHARED_HIGH_PRIO", 16); + break; + case MDL_SHARED_READ: + PFS_engine_table::set_field_varchar_utf8(f, "SHARED_READ", 11); + break; + case MDL_SHARED_WRITE: + PFS_engine_table::set_field_varchar_utf8(f, "SHARED_WRITE", 12); + break; + case MDL_SHARED_UPGRADABLE: + PFS_engine_table::set_field_varchar_utf8(f, "SHARED_UPGRADABLE", 17); + break; + case MDL_SHARED_NO_WRITE: + PFS_engine_table::set_field_varchar_utf8(f, "SHARED_NO_WRITE", 15); + break; + case MDL_SHARED_NO_READ_WRITE: + PFS_engine_table::set_field_varchar_utf8(f, "SHARED_NO_READ_WRITE", 20); + break; + case MDL_EXCLUSIVE: + PFS_engine_table::set_field_varchar_utf8(f, "EXCLUSIVE", 9); + break; + default: + DBUG_ASSERT(false); + } + } +} + +void set_field_mdl_duration(Field *f, opaque_mdl_duration mdl_duration) +{ + enum_mdl_duration e= (enum_mdl_duration) mdl_duration; + switch (e) + { + case MDL_STATEMENT: + PFS_engine_table::set_field_varchar_utf8(f, "STATEMENT", 9); + break; + case MDL_TRANSACTION: + PFS_engine_table::set_field_varchar_utf8(f, "TRANSACTION", 11); + break; + case MDL_EXPLICIT: + PFS_engine_table::set_field_varchar_utf8(f, "EXPLICIT", 8); + break; + case MDL_DURATION_END: + default: + assert(false); + } +} + +void set_field_mdl_status(Field *f, opaque_mdl_status mdl_status) +{ + MDL_ticket::enum_psi_status e= static_cast<MDL_ticket::enum_psi_status>(mdl_status); + switch (e) + { + case MDL_ticket::PENDING: + PFS_engine_table::set_field_varchar_utf8(f, "PENDING", 7); + break; + case MDL_ticket::GRANTED: + PFS_engine_table::set_field_varchar_utf8(f, "GRANTED", 7); + break; + case MDL_ticket::PRE_ACQUIRE_NOTIFY: + PFS_engine_table::set_field_varchar_utf8(f, "PRE_ACQUIRE_NOTIFY", 18); + break; + case MDL_ticket::POST_RELEASE_NOTIFY: + PFS_engine_table::set_field_varchar_utf8(f, "POST_RELEASE_NOTIFY", 19); + break; + default: + assert(false); + } +} + +void PFS_memory_stat_row::set_field(uint index, Field *f) +{ + ssize_t val; + + switch (index) + { + case 0: /* COUNT_ALLOC */ + PFS_engine_table::set_field_ulonglong(f, m_stat.m_alloc_count); + break; + case 1: /* COUNT_FREE */ + PFS_engine_table::set_field_ulonglong(f, m_stat.m_free_count); + break; + case 2: /* SUM_NUMBER_OF_BYTES_ALLOC */ + PFS_engine_table::set_field_ulonglong(f, m_stat.m_alloc_size); + break; + case 3: /* SUM_NUMBER_OF_BYTES_FREE */ + PFS_engine_table::set_field_ulonglong(f, m_stat.m_free_size); + break; + case 4: /* LOW_COUNT_USED */ + val= m_stat.m_alloc_count - m_stat.m_free_count - m_stat.m_free_count_capacity; + PFS_engine_table::set_field_longlong(f, val); + break; + case 5: /* CURRENT_COUNT_USED */ + val= m_stat.m_alloc_count - m_stat.m_free_count; + PFS_engine_table::set_field_longlong(f, val); + break; + case 6: /* HIGH_COUNT_USED */ + val= m_stat.m_alloc_count - m_stat.m_free_count + m_stat.m_alloc_count_capacity; + PFS_engine_table::set_field_longlong(f, val); + break; + case 7: /* LOW_NUMBER_OF_BYTES_USED */ + val= m_stat.m_alloc_size - m_stat.m_free_size - m_stat.m_free_size_capacity; + PFS_engine_table::set_field_longlong(f, val); + break; + case 8: /* CURRENT_NUMBER_OF_BYTES_USED */ + val= m_stat.m_alloc_size - m_stat.m_free_size; + PFS_engine_table::set_field_longlong(f, val); + break; + case 9: /* HIGH_NUMBER_OF_BYTES_USED */ + val= m_stat.m_alloc_size - m_stat.m_free_size + m_stat.m_alloc_size_capacity; + PFS_engine_table::set_field_longlong(f, val); + break; + default: + assert(false); + break; + } +} + +void set_field_isolation_level(Field *f, enum_isolation_level iso_level) +{ + switch (iso_level) + { + case TRANS_LEVEL_READ_UNCOMMITTED: + PFS_engine_table::set_field_varchar_utf8(f, "READ UNCOMMITTED", 16); + break; + case TRANS_LEVEL_READ_COMMITTED: + PFS_engine_table::set_field_varchar_utf8(f, "READ COMMITTED", 14); + break; + case TRANS_LEVEL_REPEATABLE_READ: + PFS_engine_table::set_field_varchar_utf8(f, "REPEATABLE READ", 15); + break; + case TRANS_LEVEL_SERIALIZABLE: + PFS_engine_table::set_field_varchar_utf8(f, "SERIALIZABLE", 12); + break; + default: + assert(false); + } +} + +void set_field_xa_state(Field *f, enum_xa_transaction_state xa_state) +{ + switch (xa_state) + { + case TRANS_STATE_XA_NOTR: + PFS_engine_table::set_field_varchar_utf8(f, "NOTR", 4); + break; + case TRANS_STATE_XA_ACTIVE: + PFS_engine_table::set_field_varchar_utf8(f, "ACTIVE", 6); + break; + case TRANS_STATE_XA_IDLE: + PFS_engine_table::set_field_varchar_utf8(f, "IDLE", 4); + break; + case TRANS_STATE_XA_PREPARED: + PFS_engine_table::set_field_varchar_utf8(f, "PREPARED", 8); + break; + case TRANS_STATE_XA_ROLLBACK_ONLY: + PFS_engine_table::set_field_varchar_utf8(f, "ROLLBACK ONLY", 13); + break; + case TRANS_STATE_XA_COMMITTED: + PFS_engine_table::set_field_varchar_utf8(f, "COMMITTED", 9); + break; + default: + assert(false); + } +} + +void PFS_variable_name_row::make_row(const char* str, size_t length) +{ + assert(length <= sizeof(m_str)); + assert(length <= NAME_CHAR_LEN); + + m_length= MY_MIN(static_cast<uint>(length), NAME_CHAR_LEN); /* enforce max name length */ + if (m_length > 0) + memcpy(m_str, str, length); + m_str[m_length]= '\0'; +} + +void PFS_variable_value_row::make_row(const Status_variable *var) +{ + make_row(var->m_charset, var->m_value_str, var->m_value_length); +} + +void PFS_variable_value_row::make_row(const System_variable *var) +{ + make_row(var->m_charset, var->m_value_str, var->m_value_length); +} + +void PFS_variable_value_row::make_row(const CHARSET_INFO *cs, const char* str, size_t length) +{ + assert(cs != NULL); + assert(length <= sizeof(m_str)); + if (length > 0) + { + memcpy(m_str, str, length); + } + m_length= static_cast<uint>(length); + m_charset= cs; +} + +void PFS_variable_value_row::set_field(Field *f) +{ + PFS_engine_table::set_field_varchar(f, m_charset, m_str, m_length); +} + +void PFS_user_variable_value_row::clear() +{ + my_free(m_value); + m_value= NULL; + m_value_length= 0; +} + +void PFS_user_variable_value_row::make_row(const char* val, size_t length) +{ + if (length > 0) + { + m_value= (char*) my_malloc(PSI_NOT_INSTRUMENTED, length, MYF(0)); + m_value_length= length; + memcpy(m_value, val, length); + } + else + { + m_value= NULL; + m_value_length= 0; + } +} + diff --git a/storage/perfschema/table_helper.h b/storage/perfschema/table_helper.h new file mode 100644 index 00000000..ddea4c08 --- /dev/null +++ b/storage/perfschema/table_helper.h @@ -0,0 +1,702 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_TABLE_HELPER_H +#define PFS_TABLE_HELPER_H + +#include "pfs_column_types.h" +#include "pfs_stat.h" +#include "pfs_timer.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_digest.h" + +/* + Write MD5 hash value in a string to be used + as DIGEST for the statement. +*/ +#define MD5_HASH_TO_STRING(_hash, _str) \ + sprintf(_str, "%02x%02x%02x%02x%02x%02x%02x%02x" \ + "%02x%02x%02x%02x%02x%02x%02x%02x", \ + _hash[0], _hash[1], _hash[2], _hash[3], \ + _hash[4], _hash[5], _hash[6], _hash[7], \ + _hash[8], _hash[9], _hash[10], _hash[11], \ + _hash[12], _hash[13], _hash[14], _hash[15]) + +#define MD5_HASH_TO_STRING_LENGTH 32 + +struct PFS_host; +struct PFS_user; +struct PFS_account; +struct PFS_object_name; +struct PFS_program; +class System_variable; +class Status_variable; + +/** + @file storage/perfschema/table_helper.h + Performance schema table helpers (declarations). +*/ + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** Namespace, internal views used within table setup_instruments. */ +struct PFS_instrument_view_constants +{ + static const uint FIRST_VIEW= 1; + static const uint VIEW_MUTEX= 1; + static const uint VIEW_RWLOCK= 2; + static const uint VIEW_COND= 3; + static const uint VIEW_FILE= 4; + static const uint VIEW_TABLE= 5; + static const uint VIEW_SOCKET= 6; + static const uint VIEW_IDLE= 7; + static const uint VIEW_METADATA= 8; + static const uint LAST_VIEW= 8; +}; + +/** Namespace, internal views used within object summaries. */ +struct PFS_object_view_constants +{ + static const uint FIRST_VIEW= 1; + static const uint VIEW_TABLE= 1; + static const uint VIEW_PROGRAM= 2; + static const uint LAST_VIEW= 2; +}; + +/** Row fragment for column HOST. */ +struct PFS_host_row +{ + /** Column HOST. */ + char m_hostname[HOSTNAME_LENGTH]; + /** Length in bytes of @c m_hostname. */ + uint m_hostname_length; + + /** Build a row from a memory buffer. */ + int make_row(PFS_host *pfs); + /** Set a table field from the row. */ + void set_field(Field *f); +}; + +/** Row fragment for column USER. */ +struct PFS_user_row +{ + /** Column USER. */ + char m_username[USERNAME_LENGTH]; + /** Length in bytes of @c m_username. */ + uint m_username_length; + + /** Build a row from a memory buffer. */ + int make_row(PFS_user *pfs); + /** Set a table field from the row. */ + void set_field(Field *f); +}; + +/** Row fragment for columns USER, HOST. */ +struct PFS_account_row +{ + /** Column USER. */ + char m_username[USERNAME_LENGTH]; + /** Length in bytes of @c m_username. */ + uint m_username_length; + /** Column HOST. */ + char m_hostname[HOSTNAME_LENGTH]; + /** Length in bytes of @c m_hostname. */ + uint m_hostname_length; + + /** Build a row from a memory buffer. */ + int make_row(PFS_account *pfs); + /** Set a table field from the row. */ + void set_field(uint index, Field *f); +}; + +/** Row fragment for columns DIGEST, DIGEST_TEXT. */ +struct PFS_digest_row +{ + /** Column SCHEMA_NAME. */ + char m_schema_name[NAME_LEN]; + /** Length in bytes of @c m_schema_name. */ + uint m_schema_name_length; + /** Column DIGEST. */ + char m_digest[COL_DIGEST_SIZE]; + /** Length in bytes of @c m_digest. */ + uint m_digest_length; + /** Column DIGEST_TEXT. */ + String m_digest_text; + + /** Build a row from a memory buffer. */ + int make_row(PFS_statements_digest_stat*); + /** Set a table field from the row. */ + void set_field(uint index, Field *f); +}; + +/** Row fragment for column EVENT_NAME. */ +struct PFS_event_name_row +{ + /** Column EVENT_NAME. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + + /** Build a row from a memory buffer. */ + inline void make_row(PFS_instr_class *pfs) + { + m_name= pfs->m_name; + m_name_length= pfs->m_name_length; + } + + /** Set a table field from the row. */ + inline void set_field(Field *f) + { + PFS_engine_table::set_field_varchar_utf8(f, m_name, m_name_length); + } +}; + +/** Row fragment for columns OBJECT_TYPE, SCHEMA_NAME, OBJECT_NAME. */ +struct PFS_object_row +{ + /** Column OBJECT_TYPE. */ + enum_object_type m_object_type; + /** Column SCHEMA_NAME. */ + char m_schema_name[NAME_LEN]; + /** Length in bytes of @c m_schema_name. */ + uint m_schema_name_length; + /** Column OBJECT_NAME. */ + char m_object_name[NAME_LEN]; + /** Length in bytes of @c m_object_name. */ + uint m_object_name_length; + + /** Build a row from a memory buffer. */ + int make_row(PFS_table_share *pfs); + int make_row(PFS_program *pfs); + int make_row(const MDL_key *pfs); + /** Set a table field from the row. */ + void set_field(uint index, Field *f); + void set_nullable_field(uint index, Field *f); +}; + +/** Row fragment for columns OBJECT_TYPE, SCHEMA_NAME, OBJECT_NAME, INDEX_NAME. */ +struct PFS_index_row +{ + PFS_object_row m_object_row; + /** Column INDEX_NAME. */ + char m_index_name[NAME_LEN]; + /** Length in bytes of @c m_index_name. */ + uint m_index_name_length; + + /** Build a row from a memory buffer. */ + int make_row(PFS_table_share *pfs, PFS_table_share_index *pfs_index, + uint table_index); + /** Set a table field from the row. */ + void set_field(uint index, Field *f); +}; + +/** Row fragment for single statistics columns (COUNT, SUM, MIN, AVG, MAX) */ +struct PFS_stat_row +{ + /** Column COUNT_STAR. */ + ulonglong m_count; + /** Column SUM_TIMER_WAIT. */ + ulonglong m_sum; + /** Column MIN_TIMER_WAIT. */ + ulonglong m_min; + /** Column AVG_TIMER_WAIT. */ + ulonglong m_avg; + /** Column MAX_TIMER_WAIT. */ + ulonglong m_max; + + inline void reset() + { + m_count= 0; + m_sum= 0; + m_min= 0; + m_avg= 0; + m_max= 0; + } + + /** Build a row with timer fields from a memory buffer. */ + inline void set(time_normalizer *normalizer, const PFS_single_stat *stat) + { + m_count= stat->m_count; + + if ((m_count != 0) && stat->has_timed_stats()) + { + m_sum= normalizer->wait_to_pico(stat->m_sum); + m_min= normalizer->wait_to_pico(stat->m_min); + m_max= normalizer->wait_to_pico(stat->m_max); + m_avg= normalizer->wait_to_pico(stat->m_sum / m_count); + } + else + { + m_sum= 0; + m_min= 0; + m_avg= 0; + m_max= 0; + } + } + + /** Set a table field from the row. */ + void set_field(uint index, Field *f) + { + switch (index) + { + case 0: /* COUNT */ + PFS_engine_table::set_field_ulonglong(f, m_count); + break; + case 1: /* SUM */ + PFS_engine_table::set_field_ulonglong(f, m_sum); + break; + case 2: /* MIN */ + PFS_engine_table::set_field_ulonglong(f, m_min); + break; + case 3: /* AVG */ + PFS_engine_table::set_field_ulonglong(f, m_avg); + break; + case 4: /* MAX */ + PFS_engine_table::set_field_ulonglong(f, m_max); + break; + default: + assert(false); + } + } +}; + +/** Row fragment for timer and byte count stats. Corresponds to PFS_byte_stat */ +struct PFS_byte_stat_row +{ + PFS_stat_row m_waits; + ulonglong m_bytes; + + /** Build a row with timer and byte count fields from a memory buffer. */ + inline void set(time_normalizer *normalizer, const PFS_byte_stat *stat) + { + m_waits.set(normalizer, stat); + m_bytes= stat->m_bytes; + } +}; + +/** Row fragment for table io statistics columns. */ +struct PFS_table_io_stat_row +{ + PFS_stat_row m_all; + PFS_stat_row m_all_read; + PFS_stat_row m_all_write; + PFS_stat_row m_fetch; + PFS_stat_row m_insert; + PFS_stat_row m_update; + PFS_stat_row m_delete; + + /** Build a row from a memory buffer. */ + inline void set(time_normalizer *normalizer, const PFS_table_io_stat *stat) + { + PFS_single_stat all_read; + PFS_single_stat all_write; + PFS_single_stat all; + + m_fetch.set(normalizer, & stat->m_fetch); + + all_read.aggregate(& stat->m_fetch); + + m_insert.set(normalizer, & stat->m_insert); + m_update.set(normalizer, & stat->m_update); + m_delete.set(normalizer, & stat->m_delete); + + all_write.aggregate(& stat->m_insert); + all_write.aggregate(& stat->m_update); + all_write.aggregate(& stat->m_delete); + + all.aggregate(& all_read); + all.aggregate(& all_write); + + m_all_read.set(normalizer, & all_read); + m_all_write.set(normalizer, & all_write); + m_all.set(normalizer, & all); + } +}; + +/** Row fragment for table lock statistics columns. */ +struct PFS_table_lock_stat_row +{ + PFS_stat_row m_all; + PFS_stat_row m_all_read; + PFS_stat_row m_all_write; + PFS_stat_row m_read_normal; + PFS_stat_row m_read_with_shared_locks; + PFS_stat_row m_read_high_priority; + PFS_stat_row m_read_no_insert; + PFS_stat_row m_read_external; + PFS_stat_row m_write_allow_write; + PFS_stat_row m_write_concurrent_insert; + PFS_stat_row m_write_delayed; + PFS_stat_row m_write_low_priority; + PFS_stat_row m_write_normal; + PFS_stat_row m_write_external; + + /** Build a row from a memory buffer. */ + inline void set(time_normalizer *normalizer, const PFS_table_lock_stat *stat) + { + PFS_single_stat all_read; + PFS_single_stat all_write; + PFS_single_stat all; + + m_read_normal.set(normalizer, & stat->m_stat[PFS_TL_READ]); + m_read_with_shared_locks.set(normalizer, & stat->m_stat[PFS_TL_READ_WITH_SHARED_LOCKS]); + m_read_high_priority.set(normalizer, & stat->m_stat[PFS_TL_READ_HIGH_PRIORITY]); + m_read_no_insert.set(normalizer, & stat->m_stat[PFS_TL_READ_NO_INSERT]); + m_read_external.set(normalizer, & stat->m_stat[PFS_TL_READ_EXTERNAL]); + + all_read.aggregate(& stat->m_stat[PFS_TL_READ]); + all_read.aggregate(& stat->m_stat[PFS_TL_READ_WITH_SHARED_LOCKS]); + all_read.aggregate(& stat->m_stat[PFS_TL_READ_HIGH_PRIORITY]); + all_read.aggregate(& stat->m_stat[PFS_TL_READ_NO_INSERT]); + all_read.aggregate(& stat->m_stat[PFS_TL_READ_EXTERNAL]); + + m_write_allow_write.set(normalizer, & stat->m_stat[PFS_TL_WRITE_ALLOW_WRITE]); + m_write_concurrent_insert.set(normalizer, & stat->m_stat[PFS_TL_WRITE_CONCURRENT_INSERT]); + m_write_delayed.set(normalizer, & stat->m_stat[PFS_TL_WRITE_DELAYED]); + m_write_low_priority.set(normalizer, & stat->m_stat[PFS_TL_WRITE_LOW_PRIORITY]); + m_write_normal.set(normalizer, & stat->m_stat[PFS_TL_WRITE]); + m_write_external.set(normalizer, & stat->m_stat[PFS_TL_WRITE_EXTERNAL]); + + all_write.aggregate(& stat->m_stat[PFS_TL_WRITE_ALLOW_WRITE]); + all_write.aggregate(& stat->m_stat[PFS_TL_WRITE_CONCURRENT_INSERT]); + all_write.aggregate(& stat->m_stat[PFS_TL_WRITE_DELAYED]); + all_write.aggregate(& stat->m_stat[PFS_TL_WRITE_LOW_PRIORITY]); + all_write.aggregate(& stat->m_stat[PFS_TL_WRITE]); + all_write.aggregate(& stat->m_stat[PFS_TL_WRITE_EXTERNAL]); + + all.aggregate(& all_read); + all.aggregate(& all_write); + + m_all_read.set(normalizer, & all_read); + m_all_write.set(normalizer, & all_write); + m_all.set(normalizer, & all); + } +}; + +/** Row fragment for stage statistics columns. */ +struct PFS_stage_stat_row +{ + PFS_stat_row m_timer1_row; + + /** Build a row from a memory buffer. */ + inline void set(time_normalizer *normalizer, const PFS_stage_stat *stat) + { + m_timer1_row.set(normalizer, & stat->m_timer1_stat); + } + + /** Set a table field from the row. */ + void set_field(uint index, Field *f) + { + m_timer1_row.set_field(index, f); + } +}; + +/** Row fragment for statement statistics columns. */ +struct PFS_statement_stat_row +{ + PFS_stat_row m_timer1_row; + ulonglong m_error_count; + ulonglong m_warning_count; + ulonglong m_rows_affected; + ulonglong m_lock_time; + ulonglong m_rows_sent; + ulonglong m_rows_examined; + ulonglong m_created_tmp_disk_tables; + ulonglong m_created_tmp_tables; + ulonglong m_select_full_join; + ulonglong m_select_full_range_join; + ulonglong m_select_range; + ulonglong m_select_range_check; + ulonglong m_select_scan; + ulonglong m_sort_merge_passes; + ulonglong m_sort_range; + ulonglong m_sort_rows; + ulonglong m_sort_scan; + ulonglong m_no_index_used; + ulonglong m_no_good_index_used; + + /** Build a row from a memory buffer. */ + inline void set(time_normalizer *normalizer, const PFS_statement_stat *stat) + { + if (stat->m_timer1_stat.m_count != 0) + { + m_timer1_row.set(normalizer, & stat->m_timer1_stat); + + m_error_count= stat->m_error_count; + m_warning_count= stat->m_warning_count; + m_lock_time= stat->m_lock_time * MICROSEC_TO_PICOSEC; + m_rows_affected= stat->m_rows_affected; + m_rows_sent= stat->m_rows_sent; + m_rows_examined= stat->m_rows_examined; + m_created_tmp_disk_tables= stat->m_created_tmp_disk_tables; + m_created_tmp_tables= stat->m_created_tmp_tables; + m_select_full_join= stat->m_select_full_join; + m_select_full_range_join= stat->m_select_full_range_join; + m_select_range= stat->m_select_range; + m_select_range_check= stat->m_select_range_check; + m_select_scan= stat->m_select_scan; + m_sort_merge_passes= stat->m_sort_merge_passes; + m_sort_range= stat->m_sort_range; + m_sort_rows= stat->m_sort_rows; + m_sort_scan= stat->m_sort_scan; + m_no_index_used= stat->m_no_index_used; + m_no_good_index_used= stat->m_no_good_index_used; + } + else + { + m_timer1_row.reset(); + + m_error_count= 0; + m_warning_count= 0; + m_lock_time= 0; + m_rows_affected= 0; + m_rows_sent= 0; + m_rows_examined= 0; + m_created_tmp_disk_tables= 0; + m_created_tmp_tables= 0; + m_select_full_join= 0; + m_select_full_range_join= 0; + m_select_range= 0; + m_select_range_check= 0; + m_select_scan= 0; + m_sort_merge_passes= 0; + m_sort_range= 0; + m_sort_rows= 0; + m_sort_scan= 0; + m_no_index_used= 0; + m_no_good_index_used= 0; + } + } + + /** Set a table field from the row. */ + void set_field(uint index, Field *f); +}; + +/** Row fragment for stored program statistics. */ +struct PFS_sp_stat_row +{ + PFS_stat_row m_timer1_row; + + /** Build a row from a memory buffer. */ + inline void set(time_normalizer *normalizer, const PFS_sp_stat *stat) + { + m_timer1_row.set(normalizer, & stat->m_timer1_stat); + } + + /** Set a table field from the row. */ + inline void set_field(uint index, Field *f) + { + m_timer1_row.set_field(index, f); + } +}; + +/** Row fragment for transaction statistics columns. */ +struct PFS_transaction_stat_row +{ + PFS_stat_row m_timer1_row; + PFS_stat_row m_read_write_row; + PFS_stat_row m_read_only_row; + ulonglong m_savepoint_count; + ulonglong m_rollback_to_savepoint_count; + ulonglong m_release_savepoint_count; + + /** Build a row from a memory buffer. */ + inline void set(time_normalizer *normalizer, const PFS_transaction_stat *stat) + { + /* Combine read write/read only stats */ + PFS_single_stat all; + all.aggregate(&stat->m_read_only_stat); + all.aggregate(&stat->m_read_write_stat); + + m_timer1_row.set(normalizer, &all); + m_read_write_row.set(normalizer, &stat->m_read_write_stat); + m_read_only_row.set(normalizer, &stat->m_read_only_stat); + } + + /** Set a table field from the row. */ + void set_field(uint index, Field *f); +}; + +/** Row fragment for connection statistics. */ +struct PFS_connection_stat_row +{ + ulonglong m_current_connections; + ulonglong m_total_connections; + + inline void set(const PFS_connection_stat *stat) + { + m_current_connections= stat->m_current_connections; + m_total_connections= stat->m_total_connections; + } + + /** Set a table field from the row. */ + void set_field(uint index, Field *f); +}; + +void set_field_object_type(Field *f, enum_object_type object_type); +void set_field_lock_type(Field *f, PFS_TL_LOCK_TYPE lock_type); +void set_field_mdl_type(Field *f, opaque_mdl_type mdl_type, bool backup); +void set_field_mdl_duration(Field *f, opaque_mdl_duration mdl_duration); +void set_field_mdl_status(Field *f, opaque_mdl_status mdl_status); +void set_field_isolation_level(Field *f, enum_isolation_level iso_level); +void set_field_xa_state(Field *f, enum_xa_transaction_state xa_state); + +/** Row fragment for socket io statistics columns. */ +struct PFS_socket_io_stat_row +{ + PFS_byte_stat_row m_read; + PFS_byte_stat_row m_write; + PFS_byte_stat_row m_misc; + PFS_byte_stat_row m_all; + + inline void set(time_normalizer *normalizer, const PFS_socket_io_stat *stat) + { + PFS_byte_stat all; + + m_read.set(normalizer, &stat->m_read); + m_write.set(normalizer, &stat->m_write); + m_misc.set(normalizer, &stat->m_misc); + + /* Combine stats for all operations */ + all.aggregate(&stat->m_read); + all.aggregate(&stat->m_write); + all.aggregate(&stat->m_misc); + + m_all.set(normalizer, &all); + } +}; + +/** Row fragment for file io statistics columns. */ +struct PFS_file_io_stat_row +{ + PFS_byte_stat_row m_read; + PFS_byte_stat_row m_write; + PFS_byte_stat_row m_misc; + PFS_byte_stat_row m_all; + + inline void set(time_normalizer *normalizer, const PFS_file_io_stat *stat) + { + PFS_byte_stat all; + + m_read.set(normalizer, &stat->m_read); + m_write.set(normalizer, &stat->m_write); + m_misc.set(normalizer, &stat->m_misc); + + /* Combine stats for all operations */ + all.aggregate(&stat->m_read); + all.aggregate(&stat->m_write); + all.aggregate(&stat->m_misc); + + m_all.set(normalizer, &all); + } +}; + +/** Row fragment for memory statistics columns. */ +struct PFS_memory_stat_row +{ + PFS_memory_stat m_stat; + + /** Build a row from a memory buffer. */ + inline void set(const PFS_memory_stat *stat) + { + m_stat= *stat; + } + + /** Set a table field from the row. */ + void set_field(uint index, Field *f); +}; + +struct PFS_variable_name_row +{ +public: + PFS_variable_name_row() + { + m_str[0]= '\0'; + m_length= 0; + } + + void make_row(const char* str, size_t length); + + char m_str[NAME_CHAR_LEN+1]; + uint m_length; +}; + +struct PFS_variable_value_row +{ +public: + /** Set the row from a status variable. */ + void make_row(const Status_variable *var); + + /** Set the row from a system variable. */ + void make_row(const System_variable *var); + + /** Set a table field from the row. */ + void set_field(Field *f); + +private: + void make_row(const CHARSET_INFO *cs, const char* str, size_t length); + + char m_str[1024]; + uint m_length; + const CHARSET_INFO *m_charset; +}; + +struct PFS_user_variable_value_row +{ +public: + PFS_user_variable_value_row() + : m_value(NULL), m_value_length(0) + {} + + PFS_user_variable_value_row(const PFS_user_variable_value_row& rhs) + { + make_row(rhs.m_value, rhs.m_value_length); + } + + ~PFS_user_variable_value_row() + { + clear(); + } + + void make_row(const char* val, size_t length); + + const char *get_value() const + { return m_value; } + + size_t get_value_length() const + { return m_value_length; } + + void clear(); + +private: + char *m_value; + size_t m_value_length; +}; + +/** @} */ + +#endif + diff --git a/storage/perfschema/table_host_cache.cc b/storage/perfschema/table_host_cache.cc new file mode 100644 index 00000000..03bfc151 --- /dev/null +++ b/storage/perfschema/table_host_cache.cc @@ -0,0 +1,379 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_host_cache.cc + Table HOST_CACHE (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "table_host_cache.h" +#include "hostname.h" +#include "field.h" +#include "sql_class.h" + +THR_LOCK table_host_cache::m_table_lock; + +PFS_engine_table_share_state +table_host_cache::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_host_cache::m_share= +{ + { C_STRING_WITH_LEN("host_cache") }, + &pfs_truncatable_acl, + table_host_cache::create, + NULL, /* write_row */ + table_host_cache::delete_all_rows, + table_host_cache::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE host_cache(" + "IP VARCHAR(64) not null comment 'Client IP address.'," + "HOST VARCHAR(255) collate utf8_bin comment 'IP''s resolved DNS host name, or NULL if unknown.'," + "HOST_VALIDATED ENUM ('YES', 'NO') not null comment 'YES if the IP-to-host DNS lookup was successful, and the HOST column can be used to avoid DNS calls, or NO if unsuccessful, in which case DNS lookup is performed for each connect until either successful or a permanent error.'," + "SUM_CONNECT_ERRORS BIGINT not null comment 'Number of connection errors. Counts only protocol handshake errors for hosts that passed validation. These errors count towards max_connect_errors.'," + "COUNT_HOST_BLOCKED_ERRORS BIGINT not null comment 'Number of blocked connections because SUM_CONNECT_ERRORS exceeded the max_connect_errors system variable.'," + "COUNT_NAMEINFO_TRANSIENT_ERRORS BIGINT not null comment 'Number of transient errors during IP-to-host DNS lookups.'," + "COUNT_NAMEINFO_PERMANENT_ERRORS BIGINT not null comment 'Number of permanent errors during IP-to-host DNS lookups.'," + "COUNT_FORMAT_ERRORS BIGINT not null comment 'Number of host name format errors, for example a numeric host column.'," + "COUNT_ADDRINFO_TRANSIENT_ERRORS BIGINT not null comment 'Number of transient errors during host-to-IP reverse DNS lookups.'," + "COUNT_ADDRINFO_PERMANENT_ERRORS BIGINT not null comment 'Number of permanent errors during host-to-IP reverse DNS lookups.'," + "COUNT_FCRDNS_ERRORS BIGINT not null comment 'Number of forward-confirmed reverse DNS errors, which occur when IP-to-host DNS lookup does not match the originating IP address.'," + "COUNT_HOST_ACL_ERRORS BIGINT not null comment 'Number of errors occurring because no user from the host is permitted to log in. These attempts return error code 1130 ER_HOST_NOT_PRIVILEGED and do not proceed to username and password authentication.'," + "COUNT_NO_AUTH_PLUGIN_ERRORS BIGINT not null comment 'Number of errors due to requesting an authentication plugin that was not available. This can be due to the plugin never having been loaded, or the load attempt failing.'," + "COUNT_AUTH_PLUGIN_ERRORS BIGINT not null comment 'Number of errors reported by an authentication plugin. Plugins can increment COUNT_AUTHENTICATION_ERRORS or COUNT_HANDSHAKE_ERRORS instead, but, if specified or the error is unknown, this column is incremented.'," + "COUNT_HANDSHAKE_ERRORS BIGINT not null comment 'Number of errors detected at the wire protocol level.'," + "COUNT_PROXY_USER_ERRORS BIGINT not null comment 'Number of errors detected when a proxy user is proxied to a user that does not exist.'," + "COUNT_PROXY_USER_ACL_ERRORS BIGINT not null comment 'Number of errors detected when a proxy user is proxied to a user that exists, but the proxy user doesn''t have the PROXY privilege.'," + "COUNT_AUTHENTICATION_ERRORS BIGINT not null comment 'Number of errors where authentication failed.'," + "COUNT_SSL_ERRORS BIGINT not null comment 'Number of errors due to TLS problems.'," + "COUNT_MAX_USER_CONNECTIONS_ERRORS BIGINT not null comment 'Number of errors due to the per-user quota being exceeded.'," + "COUNT_MAX_USER_CONNECTIONS_PER_HOUR_ERRORS BIGINT not null comment 'Number of errors due to the per-hour quota being exceeded.'," + "COUNT_DEFAULT_DATABASE_ERRORS BIGINT not null comment 'Number of errors due to the user not having permission to access the specified default database, or it not existing.'," + "COUNT_INIT_CONNECT_ERRORS BIGINT not null comment 'Number of errors due to statements in the init_connect system variable.'," + "COUNT_LOCAL_ERRORS BIGINT not null comment 'Number of local server errors, such as out-of-memory errors, unrelated to network, authentication, or authorization.'," + "COUNT_UNKNOWN_ERRORS BIGINT not null comment 'Number of unknown errors that cannot be allocated to another column.'," + "FIRST_SEEN TIMESTAMP(0) NOT NULL default 0 comment 'Timestamp of the first connection attempt by the IP.'," + "LAST_SEEN TIMESTAMP(0) NOT NULL default 0 comment 'Timestamp of the most recent connection attempt by the IP.'," + "FIRST_ERROR_SEEN TIMESTAMP(0) null default 0 comment 'Timestamp of the first error seen from the IP.'," + "LAST_ERROR_SEEN TIMESTAMP(0) null default 0 comment 'Timestamp of the most recent error seen from the IP.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_host_cache::create(void) +{ + table_host_cache *t= new table_host_cache(); + if (t != NULL) + { + THD *thd= current_thd; + assert(thd != NULL); + t->materialize(thd); + } + return t; +} + +int +table_host_cache::delete_all_rows(void) +{ + /* + TRUNCATE TABLE performance_schema.host_cache + is an alternate syntax for + FLUSH HOSTS + */ + hostname_cache_refresh(); + return 0; +} + +ha_rows +table_host_cache::get_row_count(void) +{ + ha_rows count; + hostname_cache_lock(); + count= hostname_cache_size(); + hostname_cache_unlock(); + return count; +} + +table_host_cache::table_host_cache() + : PFS_engine_table(&m_share, &m_pos), + m_all_rows(NULL), m_row_count(0), + m_row(NULL), m_pos(0), m_next_pos(0) +{} + +void table_host_cache::materialize(THD *thd) +{ + Host_entry *current; + Host_entry *first; + uint size; + uint index; + row_host_cache *rows; + row_host_cache *row; + + assert(m_all_rows == NULL); + assert(m_row_count == 0); + + hostname_cache_lock(); + + size= hostname_cache_size(); + if (size == 0) + { + /* Normal case, the cache is empty. */ + goto end; + } + + rows= (row_host_cache*) thd->alloc(size * sizeof(row_host_cache)); + if (rows == NULL) + { + /* Out of memory, this thread will error out. */ + goto end; + } + + index= 0; + row= rows; + + first= hostname_cache_first(); + current= first; + + while ((current != NULL) && (index < size)) + { + make_row(current, row); + index++; + row++; + current= current->next(); + } + + m_all_rows= rows; + m_row_count= index; + +end: + hostname_cache_unlock(); +} + +void table_host_cache::make_row(Host_entry *entry, row_host_cache *row) +{ + row->m_ip_length= (int)strlen(entry->ip_key); + strcpy(row->m_ip, entry->ip_key); + row->m_hostname_length= entry->m_hostname_length; + if (row->m_hostname_length > 0) + strncpy(row->m_hostname, entry->m_hostname, row->m_hostname_length); + row->m_host_validated= entry->m_host_validated; + row->m_sum_connect_errors= entry->m_errors.m_connect; + row->m_count_host_blocked_errors= entry->m_errors.m_host_blocked; + row->m_count_nameinfo_transient_errors= entry->m_errors.m_nameinfo_transient; + row->m_count_nameinfo_permanent_errors= entry->m_errors.m_nameinfo_permanent; + row->m_count_format_errors= entry->m_errors.m_format; + row->m_count_addrinfo_transient_errors= entry->m_errors.m_addrinfo_transient; + row->m_count_addrinfo_permanent_errors= entry->m_errors.m_addrinfo_permanent; + row->m_count_fcrdns_errors= entry->m_errors.m_FCrDNS; + row->m_count_host_acl_errors= entry->m_errors.m_host_acl; + row->m_count_no_auth_plugin_errors= entry->m_errors.m_no_auth_plugin; + row->m_count_auth_plugin_errors= entry->m_errors.m_auth_plugin; + row->m_count_handshake_errors= entry->m_errors.m_handshake; + row->m_count_proxy_user_errors= entry->m_errors.m_proxy_user; + row->m_count_proxy_user_acl_errors= entry->m_errors.m_proxy_user_acl; + row->m_count_authentication_errors= entry->m_errors.m_authentication; + row->m_count_ssl_errors= entry->m_errors.m_ssl; + row->m_count_max_user_connection_errors= entry->m_errors.m_max_user_connection; + row->m_count_max_user_connection_per_hour_errors= entry->m_errors.m_max_user_connection_per_hour; + row->m_count_default_database_errors= entry->m_errors.m_default_database; + row->m_count_init_connect_errors= entry->m_errors.m_init_connect; + row->m_count_local_errors= entry->m_errors.m_local; + + /* + Reserved for future use, to help with backward compatibility. + When new errors are added in entry->m_errors.m_xxx, + report them in this column (GA releases), + until the table HOST_CACHE structure can be extended (next development version). + */ + row->m_count_unknown_errors= 0; + + row->m_first_seen= entry->m_first_seen; + row->m_last_seen= entry->m_last_seen; + row->m_first_error_seen= entry->m_first_error_seen; + row->m_last_error_seen= entry->m_last_error_seen; +} + +void table_host_cache::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_host_cache::rnd_next(void) +{ + int result; + + m_pos.set_at(&m_next_pos); + + if (m_pos.m_index < m_row_count) + { + m_row= &m_all_rows[m_pos.m_index]; + m_next_pos.set_after(&m_pos); + result= 0; + } + else + { + m_row= NULL; + result= HA_ERR_END_OF_FILE; + } + + return result; +} + +int table_host_cache::rnd_pos(const void *pos) +{ + set_position(pos); + assert(m_pos.m_index < m_row_count); + m_row= &m_all_rows[m_pos.m_index]; + return 0; +} + +int table_host_cache::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + assert(m_row); + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* IP */ + set_field_varchar_utf8(f, m_row->m_ip, m_row->m_ip_length); + break; + case 1: /* HOST */ + if (m_row->m_hostname_length > 0) + set_field_varchar_utf8(f, m_row->m_hostname, m_row->m_hostname_length); + else + f->set_null(); + break; + case 2: /* HOST_VALIDATED */ + set_field_enum(f, m_row->m_host_validated ? ENUM_YES : ENUM_NO); + break; + case 3: /* SUM_CONNECT_ERRORS */ + set_field_ulonglong(f, m_row->m_sum_connect_errors); + break; + case 4: /* COUNT_HOST_BLOCKED_ERRORS. */ + set_field_ulonglong(f, m_row->m_count_host_blocked_errors); + break; + case 5: /* COUNT_NAMEINFO_TRANSIENT_ERRORS */ + set_field_ulonglong(f, m_row->m_count_nameinfo_transient_errors); + break; + case 6: /* COUNT_NAMEINFO_PERSISTENT_ERRORS */ + set_field_ulonglong(f, m_row->m_count_nameinfo_permanent_errors); + break; + case 7: /* COUNT_FORMAT_ERRORS */ + set_field_ulonglong(f, m_row->m_count_format_errors); + break; + case 8: /* COUNT_ADDRINFO_TRANSIENT_ERRORS */ + set_field_ulonglong(f, m_row->m_count_addrinfo_transient_errors); + break; + case 9: /* COUNT_ADDRINFO_PERSISTENT_ERRORS */ + set_field_ulonglong(f, m_row->m_count_addrinfo_permanent_errors); + break; + case 10: /* COUNT_FCRDNS_ERRORS */ + set_field_ulonglong(f, m_row->m_count_fcrdns_errors); + break; + case 11: /* COUNT_HOST_ACL_ERRORS */ + set_field_ulonglong(f, m_row->m_count_host_acl_errors); + break; + case 12: /* COUNT_NO_AUTH_PLUGIN_ERRORS */ + set_field_ulonglong(f, m_row->m_count_no_auth_plugin_errors); + break; + case 13: /* COUNT_AUTH_PLUGIN_ERRORS */ + set_field_ulonglong(f, m_row->m_count_auth_plugin_errors); + break; + case 14: /* COUNT_HANDSHAKE_ERRORS */ + set_field_ulonglong(f, m_row->m_count_handshake_errors); + break; + case 15: /* COUNT_PROXY_USER_ERRORS */ + set_field_ulonglong(f, m_row->m_count_proxy_user_errors); + break; + case 16: /* COUNT_PROXY_USER_ACL_ERRORS */ + set_field_ulonglong(f, m_row->m_count_proxy_user_acl_errors); + break; + case 17: /* COUNT_AUTHENTICATION_ERRORS */ + set_field_ulonglong(f, m_row->m_count_authentication_errors); + break; + case 18: /* COUNT_SSL_ERRORS */ + set_field_ulonglong(f, m_row->m_count_ssl_errors); + break; + case 19: /* COUNT_MAX_USER_CONNECTION_ERRORS */ + set_field_ulonglong(f, m_row->m_count_max_user_connection_errors); + break; + case 20: /* COUNT_MAX_USER_CONNECTION_PER_HOUR_ERRORS */ + set_field_ulonglong(f, m_row->m_count_max_user_connection_per_hour_errors); + break; + case 21: /* COUNT_DEFAULT_DATABASE_ERRORS */ + set_field_ulonglong(f, m_row->m_count_default_database_errors); + break; + case 22: /* COUNT_INIT_CONNECT_ERRORS */ + set_field_ulonglong(f, m_row->m_count_init_connect_errors); + break; + case 23: /* COUNT_LOCAL_ERRORS */ + set_field_ulonglong(f, m_row->m_count_local_errors); + break; + case 24: /* COUNT_UNKNOWN_ERRORS */ + set_field_ulonglong(f, m_row->m_count_unknown_errors); + break; + case 25: /* FIRST_SEEN */ + set_field_timestamp(f, m_row->m_first_seen); + break; + case 26: /* LAST_SEEN */ + set_field_timestamp(f, m_row->m_last_seen); + break; + case 27: /* FIRST_ERROR_SEEN */ + if (m_row->m_first_error_seen != 0) + set_field_timestamp(f, m_row->m_first_error_seen); + else + f->set_null(); + break; + case 28: /* LAST_ERROR_SEEN */ + if (m_row->m_last_error_seen != 0) + set_field_timestamp(f, m_row->m_last_error_seen); + else + f->set_null(); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_host_cache.h b/storage/perfschema/table_host_cache.h new file mode 100644 index 00000000..59e3a973 --- /dev/null +++ b/storage/perfschema/table_host_cache.h @@ -0,0 +1,150 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_HOST_CACHE_H +#define TABLE_HOST_CACHE_H + +/** + @file storage/perfschema/table_host_cache.h + Table HOST_CACHE (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" + +class Host_entry; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.HOST_CACHE. */ +struct row_host_cache +{ + /** Column IP. */ + char m_ip[64]; + uint m_ip_length; + /** Column HOST. */ + char m_hostname[255]; + uint m_hostname_length; + /** Column HOST_VALIDATED. */ + bool m_host_validated; + /** Column SUM_CONNECT_ERRORS. */ + ulonglong m_sum_connect_errors; + /** Column COUNT_HOST_BLOCKED_ERRORS. */ + ulonglong m_count_host_blocked_errors; + /** Column COUNT_NAMEINFO_TRANSIENT_ERRORS. */ + ulonglong m_count_nameinfo_transient_errors; + /** Column COUNT_NAMEINFO_PERMANENT_ERRORS. */ + ulonglong m_count_nameinfo_permanent_errors; + /** Column COUNT_FORMAT_ERRORS. */ + ulonglong m_count_format_errors; + /** Column COUNT_ADDRINFO_TRANSIENT_ERRORS. */ + ulonglong m_count_addrinfo_transient_errors; + /** Column COUNT_ADDRINFO_PERMANENT_ERRORS. */ + ulonglong m_count_addrinfo_permanent_errors; + /** Column COUNT_FCRDNS_ERRORS. */ + ulonglong m_count_fcrdns_errors; + /** Column COUNT_HOST_ACL_ERRORS. */ + ulonglong m_count_host_acl_errors; + /** Column COUNT_NO_AUTH_PLUGIN_ERRORS. */ + ulonglong m_count_no_auth_plugin_errors; + /** Column COUNT_AUTH_PLUGIN_ERRORS. */ + ulonglong m_count_auth_plugin_errors; + /** Column COUNT_HANDSHAKE_ERRORS. */ + ulonglong m_count_handshake_errors; + /** Column COUNT_PROXY_USER_ERRORS. */ + ulonglong m_count_proxy_user_errors; + /** Column COUNT_PROXY_USER_ACL_ERRORS. */ + ulonglong m_count_proxy_user_acl_errors; + /** Column COUNT_AUTHENTICATION_ERRORS. */ + ulonglong m_count_authentication_errors; + /** Column COUNT_SSL_ERRORS. */ + ulonglong m_count_ssl_errors; + /** Column COUNT_MAX_USER_CONNECTION_ERRORS. */ + ulonglong m_count_max_user_connection_errors; + /** Column COUNT_MAX_USER_CONNECTION_PER_HOUR_ERRORS. */ + ulonglong m_count_max_user_connection_per_hour_errors; + /** Column COUNT_DEFAULT_DATABASE_ERRORS. */ + ulonglong m_count_default_database_errors; + /** Column COUNT_INIT_CONNECT_ERRORS. */ + ulonglong m_count_init_connect_errors; + /** Column COUNT_LOCAL_ERRORS. */ + ulonglong m_count_local_errors; + /** Column COUNT_UNKNOWN_ERRORS. */ + ulonglong m_count_unknown_errors; + /** Column FIRST_SEEN. */ + ulonglong m_first_seen; + /** Column LAST_SEEN. */ + ulonglong m_last_seen; + /** Column FIRST_ERROR_SEEN. */ + ulonglong m_first_error_seen; + /** Column LAST_ERROR_SEEN. */ + ulonglong m_last_error_seen; +}; + +/** Table PERFORMANCE_SCHEMA.HOST_CACHE. */ +class table_host_cache : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_host_cache(); + +public: + ~table_host_cache() = default; + +private: + void materialize(THD *thd); + static void make_row(Host_entry *entry, row_host_cache *row); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + row_host_cache *m_all_rows; + uint m_row_count; + /** Current row. */ + row_host_cache *m_row; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_hosts.cc b/storage/perfschema/table_hosts.cc new file mode 100644 index 00000000..bfb3ecad --- /dev/null +++ b/storage/perfschema/table_hosts.cc @@ -0,0 +1,156 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "my_global.h" +#include "my_thread.h" +#include "table_hosts.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_account.h" +#include "pfs_host.h" +#include "pfs_visitor.h" +#include "pfs_memory.h" +#include "pfs_status.h" +#include "field.h" + +THR_LOCK table_hosts::m_table_lock; + +PFS_engine_table_share_state +table_hosts::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_hosts::m_share= +{ + { C_STRING_WITH_LEN("hosts") }, + &pfs_truncatable_acl, + table_hosts::create, + NULL, /* write_row */ + table_hosts::delete_all_rows, + cursor_by_host::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE hosts(" + "HOST CHAR(" STRINGIFY_ARG(HOSTNAME_LENGTH) ") collate utf8_bin default null comment 'Host name used by the client to connect, NULL for internal threads or user sessions that failed to authenticate.'," + "CURRENT_CONNECTIONS bigint not null comment 'Current number of the host''s connections.'," + "TOTAL_CONNECTIONS bigint not null comment 'Total number of the host''s connections')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_hosts::create() +{ + return new table_hosts(); +} + +int +table_hosts::delete_all_rows(void) +{ + reset_events_waits_by_thread(); + reset_events_waits_by_account(); + reset_events_waits_by_host(); + reset_events_stages_by_thread(); + reset_events_stages_by_account(); + reset_events_stages_by_host(); + reset_events_statements_by_thread(); + reset_events_statements_by_account(); + reset_events_statements_by_host(); + reset_events_transactions_by_thread(); + reset_events_transactions_by_account(); + reset_events_transactions_by_host(); + reset_memory_by_thread(); + reset_memory_by_account(); + reset_memory_by_host(); + reset_status_by_thread(); + reset_status_by_account(); + reset_status_by_host(); + purge_all_account(); + purge_all_host(); + return 0; +} + +table_hosts::table_hosts() + : cursor_by_host(& m_share), + m_row_exists(false) +{} + +void table_hosts::make_row(PFS_host *pfs) +{ + pfs_optimistic_state lock; + + m_row_exists= false; + pfs->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_host.make_row(pfs)) + return; + + PFS_connection_stat_visitor visitor; + PFS_connection_iterator::visit_host(pfs, + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! pfs->m_lock.end_optimistic_lock(& lock)) + return; + + m_row.m_connection_stat.set(& visitor.m_stat); + m_row_exists= true; +} + +int table_hosts::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* HOST */ + m_row.m_host.set_field(f); + break; + case 1: /* CURRENT_CONNECTIONS */ + case 2: /* TOTAL_CONNECTIONS */ + m_row.m_connection_stat.set_field(f->field_index - 1, f); + break; + default: + assert(false); + } + } + } + return 0; +} + diff --git a/storage/perfschema/table_hosts.h b/storage/perfschema/table_hosts.h new file mode 100644 index 00000000..1f82cc5c --- /dev/null +++ b/storage/perfschema/table_hosts.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_HOSTS_H +#define TABLE_HOSTS_H + +#include "pfs_column_types.h" +#include "cursor_by_host.h" +#include "table_helper.h" + +struct PFS_host; + +/** + \addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of PERFORMANCE_SCHEMA.HOSTS. +*/ +struct row_hosts +{ + /** Column HOST. */ + PFS_host_row m_host; + /** Columns CURRENT_CONNECTIONS, TOTAL_CONNECTIONS. */ + PFS_connection_stat_row m_connection_stat; +}; + +/** Table PERFORMANCE_SCHEMA.THREADS. */ +class table_hosts : public cursor_by_host +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + /** Table builder */ + static PFS_engine_table* create(); + static int delete_all_rows(); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + +protected: + table_hosts(); + +public: + ~table_hosts() = default; + +private: + virtual void make_row(PFS_host *pfs); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_hosts m_row; + /** True if the current row exists. */ + bool m_row_exists; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_md_locks.cc b/storage/perfschema/table_md_locks.cc new file mode 100644 index 00000000..e7b76c12 --- /dev/null +++ b/storage/perfschema/table_md_locks.cc @@ -0,0 +1,214 @@ +/* Copyright (c) 2012, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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_md_locks.cc + Table METADATA_LOCKS (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_md_locks.h" +#include "pfs_global.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_metadata_locks::m_table_lock; + +PFS_engine_table_share_state +table_metadata_locks::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_metadata_locks::m_share= +{ + { C_STRING_WITH_LEN("metadata_locks") }, + &pfs_readonly_acl, + table_metadata_locks::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_metadata_locks::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE metadata_locks(" + "OBJECT_TYPE VARCHAR(64) not null comment 'Object type. One of BACKUP, COMMIT, EVENT, FUNCTION, GLOBAL, LOCKING SERVICE, PROCEDURE, SCHEMA, TABLE, TABLESPACE, TRIGGER (unused) or USER LEVEL LOCK.'," + "OBJECT_SCHEMA VARCHAR(64) comment 'Object schema.'," + "OBJECT_NAME VARCHAR(64) comment 'Object name.'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null comment 'Address in memory of the instrumented object.'," + "LOCK_TYPE VARCHAR(32) not null comment 'Lock type. One of BACKUP_FTWRL1, BACKUP_START, BACKUP_TRANS_DML, EXCLUSIVE, INTENTION_EXCLUSIVE, SHARED, SHARED_HIGH_PRIO, SHARED_NO_READ_WRITE, SHARED_NO_WRITE, SHARED_READ, SHARED_UPGRADABLE or SHARED_WRITE.'," + "LOCK_DURATION VARCHAR(32) not null comment 'Lock duration. One of EXPLICIT (locks released by explicit action, for example a global lock acquired with FLUSH TABLES WITH READ LOCK) , STATEMENT (locks implicitly released at statement end) or TRANSACTION (locks implicitly released at transaction end).'," + "LOCK_STATUS VARCHAR(32) not null comment 'Lock status. One of GRANTED, KILLED, PENDING, POST_RELEASE_NOTIFY, PRE_ACQUIRE_NOTIFY, TIMEOUT or VICTIM.'," + "SOURCE VARCHAR(64) comment 'Source file containing the instrumented code that produced the event, as well as the line number where the instrumentation occurred. This allows one to examine the source code involved.'," + "OWNER_THREAD_ID BIGINT unsigned comment 'Thread that requested the lock.'," + "OWNER_EVENT_ID BIGINT unsigned comment 'Event that requested the lock.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_metadata_locks::create(void) +{ + return new table_metadata_locks(); +} + +ha_rows +table_metadata_locks::get_row_count(void) +{ + return global_mdl_container.get_row_count(); +} + +table_metadata_locks::table_metadata_locks() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_metadata_locks::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_metadata_locks::rnd_next(void) +{ + PFS_metadata_lock *pfs; + + m_pos.set_at(&m_next_pos); + PFS_mdl_iterator it= global_mdl_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_metadata_locks::rnd_pos(const void *pos) +{ + PFS_metadata_lock *pfs; + + set_position(pos); + + pfs= global_mdl_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_metadata_locks::make_row(PFS_metadata_lock *pfs) +{ + pfs_optimistic_state lock; + + m_row_exists= false; + + /* Protect this reader against a metadata lock destroy */ + pfs->m_lock.begin_optimistic_lock(&lock); + + m_row.m_identity= pfs->m_identity; + m_row.m_mdl_type= pfs->m_mdl_type; + m_row.m_mdl_duration= pfs->m_mdl_duration; + m_row.m_mdl_status= pfs->m_mdl_status; + + /* Disable source file and line to avoid stale __FILE__ pointers. */ + m_row.m_source_length= 0; + + m_row.m_owner_thread_id= static_cast<ulong>(pfs->m_owner_thread_id); + m_row.m_owner_event_id= static_cast<ulong>(pfs->m_owner_event_id); + + if (m_row.m_object.make_row(& pfs->m_mdl_key)) + return; + + if (pfs->m_lock.end_optimistic_lock(&lock)) + m_row_exists= true; +} + +int table_metadata_locks::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* OBJECT_TYPE */ + case 1: /* OBJECT_SCHEMA */ + case 2: /* OBJECT_NAME */ + m_row.m_object.set_nullable_field(f->field_index, f); + break; + case 3: /* OBJECT_INSTANCE */ + set_field_ulonglong(f, (intptr) m_row.m_identity); + break; + case 4: /* LOCK_TYPE */ + set_field_mdl_type(f, m_row.m_mdl_type, m_row.m_object.m_object_type == OBJECT_TYPE_BACKUP); + break; + case 5: /* LOCK_DURATION */ + set_field_mdl_duration(f, m_row.m_mdl_duration); + break; + case 6: /* LOCK_STATUS */ + set_field_mdl_status(f, m_row.m_mdl_status); + break; + case 7: /* SOURCE */ + set_field_varchar_utf8(f, m_row.m_source, m_row.m_source_length); + break; + case 8: /* OWNER_THREAD_ID */ + if (m_row.m_owner_thread_id != 0) + set_field_ulonglong(f, m_row.m_owner_thread_id); + else + f->set_null(); + break; + case 9: /* OWNER_EVENT_ID */ + if (m_row.m_owner_event_id != 0) + set_field_ulonglong(f, m_row.m_owner_event_id); + else + f->set_null(); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_md_locks.h b/storage/perfschema/table_md_locks.h new file mode 100644 index 00000000..5c9ad9b5 --- /dev/null +++ b/storage/perfschema/table_md_locks.h @@ -0,0 +1,107 @@ +/* Copyright (c) 2012, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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 TABLE_METADATA_LOCK_H +#define TABLE_METADATA_LOCK_H + +/** + @file storage/perfschema/table_md_locks.h + Table METADATA_LOCKS (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "table_helper.h" + +struct PFS_metadata_lock; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of table PERFORMANCE_SCHEMA.MUTEX_INSTANCES. */ +struct row_metadata_lock +{ + /** Column OBJECT_INSTANCE_BEGIN. */ + const void *m_identity; + opaque_mdl_type m_mdl_type; + opaque_mdl_duration m_mdl_duration; + opaque_mdl_status m_mdl_status; + /** Column SOURCE. */ + char m_source[COL_SOURCE_SIZE]; + /** Length in bytes of @c m_source. */ + uint m_source_length; + /** Column OWNER_THREAD_ID. */ + ulong m_owner_thread_id; + /** Column OWNER_EVENT_ID. */ + ulong m_owner_event_id; + /** Columns OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME. */ + PFS_object_row m_object; +}; + +/** Table PERFORMANCE_SCHEMA.METADATA_LOCKS. */ +class table_metadata_locks : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_metadata_locks(); + +public: + ~table_metadata_locks() + {} + +private: + void make_row(PFS_metadata_lock *pfs); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_metadata_lock m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_mems_by_account_by_event_name.cc b/storage/perfschema/table_mems_by_account_by_event_name.cc new file mode 100644 index 00000000..4168d83e --- /dev/null +++ b/storage/perfschema/table_mems_by_account_by_event_name.cc @@ -0,0 +1,228 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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_mems_by_account_by_event_name.cc + Table MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_mems_by_account_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_memory.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_mems_by_account_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_mems_by_account_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_mems_by_account_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("memory_summary_by_account_by_event_name") }, + &pfs_readonly_acl, + table_mems_by_account_by_event_name::create, + NULL, /* write_row */ + table_mems_by_account_by_event_name::delete_all_rows, + table_mems_by_account_by_event_name::get_row_count, + sizeof(pos_mems_by_account_by_event_name), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE memory_summary_by_account_by_event_name(" + "USER CHAR(32) collate utf8_bin default null comment 'User portion of the account.'," + "HOST CHAR(60) collate utf8_bin default null comment 'Host portion of the account.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name.'," + "COUNT_ALLOC BIGINT unsigned not null comment 'Total number of allocations to memory.'," + "COUNT_FREE BIGINT unsigned not null comment 'Total number of attempts to free the allocated memory.'," + "SUM_NUMBER_OF_BYTES_ALLOC BIGINT unsigned not null comment 'Total number of bytes allocated.'," + "SUM_NUMBER_OF_BYTES_FREE BIGINT unsigned not null comment 'Total number of bytes freed'," + "LOW_COUNT_USED BIGINT not null comment 'Lowest number of allocated blocks (lowest value of CURRENT_COUNT_USED).'," + "CURRENT_COUNT_USED BIGINT not null comment 'Currently allocated blocks that have not been freed (COUNT_ALLOC minus COUNT_FREE).'," + "HIGH_COUNT_USED BIGINT not null comment 'Highest number of allocated blocks (highest value of CURRENT_COUNT_USED).'," + "LOW_NUMBER_OF_BYTES_USED BIGINT not null comment 'Lowest number of bytes used.'," + "CURRENT_NUMBER_OF_BYTES_USED BIGINT not null comment 'Current number of bytes used (total allocated minus total freed).'," + "HIGH_NUMBER_OF_BYTES_USED BIGINT not null comment 'Highest number of bytes used.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_mems_by_account_by_event_name::create(void) +{ + return new table_mems_by_account_by_event_name(); +} + +int +table_mems_by_account_by_event_name::delete_all_rows(void) +{ + reset_memory_by_thread(); + reset_memory_by_account(); + return 0; +} + +ha_rows +table_mems_by_account_by_event_name::get_row_count(void) +{ + return global_account_container.get_row_count() * memory_class_max; +} + +table_mems_by_account_by_event_name::table_mems_by_account_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_mems_by_account_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_mems_by_account_by_event_name::rnd_next(void) +{ + PFS_account *account; + PFS_memory_class *memory_class; + bool has_more_account= true; + + for (m_pos.set_at(&m_next_pos); + has_more_account; + m_pos.next_account()) + { + account= global_account_container.get(m_pos.m_index_1, & has_more_account); + if (account != NULL) + { + do + { + memory_class= find_memory_class(m_pos.m_index_2); + if (memory_class != NULL) + { + if (! memory_class->is_global()) + { + make_row(account, memory_class); + m_next_pos.set_after(&m_pos); + return 0; + } + + m_pos.next_class(); + } + } + while (memory_class != NULL); + } + } + + return HA_ERR_END_OF_FILE; +} + +int table_mems_by_account_by_event_name::rnd_pos(const void *pos) +{ + PFS_account *account; + PFS_memory_class *memory_class; + + set_position(pos); + + account= global_account_container.get(m_pos.m_index_1); + if (account != NULL) + { + memory_class= find_memory_class(m_pos.m_index_2); + if (memory_class != NULL) + { + if (! memory_class->is_global()) + { + make_row(account, memory_class); + return 0; + } + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_mems_by_account_by_event_name +::make_row(PFS_account *account, PFS_memory_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + account->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_account.make_row(account)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_memory_visitor visitor(klass); + PFS_connection_iterator::visit_account(account, + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! account->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(& visitor.m_stat); +} + +int table_mems_by_account_by_event_name::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* USER */ + case 1: /* HOST */ + m_row.m_account.set_field(f->field_index, f); + break; + case 2: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 3, ... HIGH_NUMBER_OF_BYTES_USED */ + m_row.m_stat.set_field(f->field_index - 3, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_mems_by_account_by_event_name.h b/storage/perfschema/table_mems_by_account_by_event_name.h new file mode 100644 index 00000000..e242bc40 --- /dev/null +++ b/storage/perfschema/table_mems_by_account_by_event_name.h @@ -0,0 +1,130 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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 TABLE_MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME_H +#define TABLE_MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_mems_by_account_by_event_name.h + Table MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_account.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. */ +struct row_mems_by_account_by_event_name +{ + /** Column USER, HOST. */ + PFS_account_row m_account; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_ALLOC, ... */ + PFS_memory_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. + Index 1 on account (0 based) + Index 2 on memory class (1 based) +*/ +struct pos_mems_by_account_by_event_name +: public PFS_double_index +{ + pos_mems_by_account_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_account(void) + { + m_index_1++; + m_index_2= 1; + } + + inline void next_class(void) + { + m_index_2++; + } +}; + +/** Table PERFORMANCE_SCHEMA.MEMORY_SUMMARY_BY_ACCOUNT_BY_EVENT_NAME. */ +class table_mems_by_account_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_mems_by_account_by_event_name(); + +public: + ~table_mems_by_account_by_event_name() + {} + +private: + void make_row(PFS_account *account, PFS_memory_class *klass); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_mems_by_account_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_mems_by_account_by_event_name m_pos; + /** Next position. */ + pos_mems_by_account_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_mems_by_host_by_event_name.cc b/storage/perfschema/table_mems_by_host_by_event_name.cc new file mode 100644 index 00000000..cbe71d07 --- /dev/null +++ b/storage/perfschema/table_mems_by_host_by_event_name.cc @@ -0,0 +1,228 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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_mems_by_host_by_event_name.cc + Table MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_mems_by_host_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_memory.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_mems_by_host_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_mems_by_host_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_mems_by_host_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("memory_summary_by_host_by_event_name") }, + &pfs_readonly_acl, + table_mems_by_host_by_event_name::create, + NULL, /* write_row */ + table_mems_by_host_by_event_name::delete_all_rows, + table_mems_by_host_by_event_name::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE memory_summary_by_host_by_event_name(" + "HOST CHAR(60) collate utf8_bin default null comment 'Host portion of the account.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name.'," + "COUNT_ALLOC BIGINT unsigned not null comment 'Total number of allocations to memory.'," + "COUNT_FREE BIGINT unsigned not null comment 'Total number of attempts to free the allocated memory.'," + "SUM_NUMBER_OF_BYTES_ALLOC BIGINT unsigned not null comment 'Total number of bytes allocated.'," + "SUM_NUMBER_OF_BYTES_FREE BIGINT unsigned not null comment 'Total number of bytes freed'," + "LOW_COUNT_USED BIGINT not null comment 'Lowest number of allocated blocks (lowest value of CURRENT_COUNT_USED).'," + "CURRENT_COUNT_USED BIGINT not null comment 'Currently allocated blocks that have not been freed (COUNT_ALLOC minus COUNT_FREE).'," + "HIGH_COUNT_USED BIGINT not null comment 'Highest number of allocated blocks (highest value of CURRENT_COUNT_USED).'," + "LOW_NUMBER_OF_BYTES_USED BIGINT not null comment 'Lowest number of bytes used.'," + "CURRENT_NUMBER_OF_BYTES_USED BIGINT not null comment 'Current number of bytes used (total allocated minus total freed).'," + "HIGH_NUMBER_OF_BYTES_USED BIGINT not null comment 'Highest number of bytes used.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_mems_by_host_by_event_name::create(void) +{ + return new table_mems_by_host_by_event_name(); +} + +int +table_mems_by_host_by_event_name::delete_all_rows(void) +{ + reset_memory_by_thread(); + reset_memory_by_account(); + reset_memory_by_host(); + return 0; +} + +ha_rows +table_mems_by_host_by_event_name::get_row_count(void) +{ + return global_host_container.get_row_count() * memory_class_max; +} + +table_mems_by_host_by_event_name::table_mems_by_host_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_mems_by_host_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_mems_by_host_by_event_name::rnd_next(void) +{ + PFS_host *host; + PFS_memory_class *memory_class; + bool has_more_host= true; + + for (m_pos.set_at(&m_next_pos); + has_more_host; + m_pos.next_host()) + { + host= global_host_container.get(m_pos.m_index_1, & has_more_host); + if (host != NULL) + { + do + { + memory_class= find_memory_class(m_pos.m_index_2); + if (memory_class != NULL) + { + if (! memory_class->is_global()) + { + make_row(host, memory_class); + m_next_pos.set_after(&m_pos); + return 0; + } + + m_pos.next_class(); + } + } + while (memory_class != NULL); + } + } + + return HA_ERR_END_OF_FILE; +} + +int table_mems_by_host_by_event_name::rnd_pos(const void *pos) +{ + PFS_host *host; + PFS_memory_class *memory_class; + + set_position(pos); + + host= global_host_container.get(m_pos.m_index_1); + if (host != NULL) + { + memory_class= find_memory_class(m_pos.m_index_2); + if (memory_class != NULL) + { + if (! memory_class->is_global()) + { + make_row(host, memory_class); + return 0; + } + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_mems_by_host_by_event_name +::make_row(PFS_host *host, PFS_memory_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + host->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_host.make_row(host)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_memory_visitor visitor(klass); + PFS_connection_iterator::visit_host(host, + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! host->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(& visitor.m_stat); +} + +int table_mems_by_host_by_event_name::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* HOST */ + m_row.m_host.set_field(f); + break; + case 1: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 2, ... HIGH_NUMBER_OF_BYTES_USED */ + m_row.m_stat.set_field(f->field_index - 2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_mems_by_host_by_event_name.h b/storage/perfschema/table_mems_by_host_by_event_name.h new file mode 100644 index 00000000..7920b362 --- /dev/null +++ b/storage/perfschema/table_mems_by_host_by_event_name.h @@ -0,0 +1,130 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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 TABLE_MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME_H +#define TABLE_MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_mems_by_host_by_event_name.h + Table MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_host.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME. */ +struct row_mems_by_host_by_event_name +{ + /** Column HOST */ + PFS_host_row m_host; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_ALLOC, ... */ + PFS_memory_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME. + Index 1 on host (0 based) + Index 2 on memory class (1 based) +*/ +struct pos_mems_by_host_by_event_name +: public PFS_double_index +{ + pos_mems_by_host_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_host(void) + { + m_index_1++; + m_index_2= 1; + } + + inline void next_class(void) + { + m_index_2++; + } +}; + +/** Table PERFORMANCE_SCHEMA.MEMORY_SUMMARY_BY_HOST_BY_EVENT_NAME. */ +class table_mems_by_host_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_mems_by_host_by_event_name(); + +public: + ~table_mems_by_host_by_event_name() + {} + +private: + void make_row(PFS_host *host, PFS_memory_class *klass); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_mems_by_host_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_mems_by_host_by_event_name m_pos; + /** Next position. */ + pos_mems_by_host_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_mems_by_thread_by_event_name.cc b/storage/perfschema/table_mems_by_thread_by_event_name.cc new file mode 100644 index 00000000..2335688a --- /dev/null +++ b/storage/perfschema/table_mems_by_thread_by_event_name.cc @@ -0,0 +1,221 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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_mems_by_thread_by_event_name.cc + Table MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_mems_by_thread_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_memory.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_mems_by_thread_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_mems_by_thread_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_mems_by_thread_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("memory_summary_by_thread_by_event_name") }, + &pfs_readonly_acl, + table_mems_by_thread_by_event_name::create, + NULL, /* write_row */ + table_mems_by_thread_by_event_name::delete_all_rows, + table_mems_by_thread_by_event_name::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE memory_summary_by_thread_by_event_name(" + "THREAD_ID BIGINT unsigned not null comment 'Thread id.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name.'," + "COUNT_ALLOC BIGINT unsigned not null comment 'Total number of allocations to memory.'," + "COUNT_FREE BIGINT unsigned not null comment 'Total number of attempts to free the allocated memory.'," + "SUM_NUMBER_OF_BYTES_ALLOC BIGINT unsigned not null comment 'Total number of bytes allocated.'," + "SUM_NUMBER_OF_BYTES_FREE BIGINT unsigned not null comment 'Total number of bytes freed'," + "LOW_COUNT_USED BIGINT not null comment 'Lowest number of allocated blocks (lowest value of CURRENT_COUNT_USED).'," + "CURRENT_COUNT_USED BIGINT not null comment 'Currently allocated blocks that have not been freed (COUNT_ALLOC minus COUNT_FREE).'," + "HIGH_COUNT_USED BIGINT not null comment 'Highest number of allocated blocks (highest value of CURRENT_COUNT_USED).'," + "LOW_NUMBER_OF_BYTES_USED BIGINT not null comment 'Lowest number of bytes used.'," + "CURRENT_NUMBER_OF_BYTES_USED BIGINT not null comment 'Current number of bytes used (total allocated minus total freed).'," + "HIGH_NUMBER_OF_BYTES_USED BIGINT not null comment 'Highest number of bytes used.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_mems_by_thread_by_event_name::create(void) +{ + return new table_mems_by_thread_by_event_name(); +} + +int +table_mems_by_thread_by_event_name::delete_all_rows(void) +{ + reset_memory_by_thread(); + return 0; +} + +ha_rows +table_mems_by_thread_by_event_name::get_row_count(void) +{ + return global_thread_container.get_row_count() * memory_class_max; +} + +table_mems_by_thread_by_event_name::table_mems_by_thread_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_mems_by_thread_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_mems_by_thread_by_event_name::rnd_next(void) +{ + PFS_thread *thread; + PFS_memory_class *memory_class; + bool has_more_thread= true; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread); + if (thread != NULL) + { + do + { + memory_class= find_memory_class(m_pos.m_index_2); + if (memory_class != NULL) + { + if (! memory_class->is_global()) + { + make_row(thread, memory_class); + m_next_pos.set_after(&m_pos); + return 0; + } + + m_pos.next_class(); + } + } + while (memory_class != NULL); + } + } + + return HA_ERR_END_OF_FILE; +} + +int table_mems_by_thread_by_event_name::rnd_pos(const void *pos) +{ + PFS_thread *thread; + PFS_memory_class *memory_class; + + set_position(pos); + + thread= global_thread_container.get(m_pos.m_index_1); + if (thread != NULL) + { + memory_class= find_memory_class(m_pos.m_index_2); + if (memory_class != NULL) + { + if (! memory_class->is_global()) + { + make_row(thread, memory_class); + return 0; + } + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_mems_by_thread_by_event_name +::make_row(PFS_thread *thread, PFS_memory_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + /* Protect this reader against a thread termination */ + thread->m_lock.begin_optimistic_lock(&lock); + + m_row.m_thread_internal_id= thread->m_thread_internal_id; + + m_row.m_event_name.make_row(klass); + + PFS_connection_memory_visitor visitor(klass); + PFS_connection_iterator::visit_thread(thread, & visitor); + + if (! thread->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(& visitor.m_stat); +} + +int table_mems_by_thread_by_event_name::read_row_values(TABLE *table, + unsigned char *, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + 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_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 2, ... HIGH_NUMBER_OF_BYTES_USED */ + m_row.m_stat.set_field(f->field_index - 2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_mems_by_thread_by_event_name.h b/storage/perfschema/table_mems_by_thread_by_event_name.h new file mode 100644 index 00000000..0f698990 --- /dev/null +++ b/storage/perfschema/table_mems_by_thread_by_event_name.h @@ -0,0 +1,130 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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 TABLE_MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME_H +#define TABLE_MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_mems_by_thread_by_event_name.h + Table MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME. */ +struct row_mems_by_thread_by_event_name +{ + /** Column THREAD_ID. */ + ulonglong m_thread_internal_id; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_ALLOC, ... */ + PFS_memory_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME. + Index 1 on thread (0 based). + Index 2 on memory class (1 based). +*/ +struct pos_mems_by_thread_by_event_name +: public PFS_double_index +{ + pos_mems_by_thread_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= 1; + } + + inline void next_class(void) + { + m_index_2++; + } +}; + +/** Table PERFORMANCE_SCHEMA.MEMORY_SUMMARY_BY_THREAD_BY_EVENT_NAME. */ +class table_mems_by_thread_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_mems_by_thread_by_event_name(); + +public: + ~table_mems_by_thread_by_event_name() + {} + +private: + void make_row(PFS_thread *thread, PFS_memory_class *klass); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_mems_by_thread_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_mems_by_thread_by_event_name m_pos; + /** Next position. */ + pos_mems_by_thread_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_mems_by_user_by_event_name.cc b/storage/perfschema/table_mems_by_user_by_event_name.cc new file mode 100644 index 00000000..4a142cc9 --- /dev/null +++ b/storage/perfschema/table_mems_by_user_by_event_name.cc @@ -0,0 +1,228 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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_mems_by_user_by_event_name.cc + Table MEMORY_SUMMARY_BY_USER_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_mems_by_user_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_memory.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_mems_by_user_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_mems_by_user_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_mems_by_user_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("memory_summary_by_user_by_event_name") }, + &pfs_readonly_acl, + table_mems_by_user_by_event_name::create, + NULL, /* write_row */ + table_mems_by_user_by_event_name::delete_all_rows, + table_mems_by_user_by_event_name::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE memory_summary_by_user_by_event_name(" + "USER CHAR(32) collate utf8_bin default null comment 'User portion of the account.'," + "EVENT_NAME VARCHAR(128) not null comment 'Event name.'," + "COUNT_ALLOC BIGINT unsigned not null comment 'Total number of allocations to memory.'," + "COUNT_FREE BIGINT unsigned not null comment 'Total number of attempts to free the allocated memory.'," + "SUM_NUMBER_OF_BYTES_ALLOC BIGINT unsigned not null comment 'Total number of bytes allocated.'," + "SUM_NUMBER_OF_BYTES_FREE BIGINT unsigned not null comment 'Total number of bytes freed'," + "LOW_COUNT_USED BIGINT not null comment 'Lowest number of allocated blocks (lowest value of CURRENT_COUNT_USED).'," + "CURRENT_COUNT_USED BIGINT not null comment 'Currently allocated blocks that have not been freed (COUNT_ALLOC minus COUNT_FREE).'," + "HIGH_COUNT_USED BIGINT not null comment 'Highest number of allocated blocks (highest value of CURRENT_COUNT_USED).'," + "LOW_NUMBER_OF_BYTES_USED BIGINT not null comment 'Lowest number of bytes used.'," + "CURRENT_NUMBER_OF_BYTES_USED BIGINT not null comment 'Current number of bytes used (total allocated minus total freed).'," + "HIGH_NUMBER_OF_BYTES_USED BIGINT not null comment 'Highest number of bytes used.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_mems_by_user_by_event_name::create(void) +{ + return new table_mems_by_user_by_event_name(); +} + +int +table_mems_by_user_by_event_name::delete_all_rows(void) +{ + reset_memory_by_thread(); + reset_memory_by_account(); + reset_memory_by_user(); + return 0; +} + +ha_rows +table_mems_by_user_by_event_name::get_row_count(void) +{ + return global_user_container.get_row_count() * memory_class_max; +} + +table_mems_by_user_by_event_name::table_mems_by_user_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_mems_by_user_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_mems_by_user_by_event_name::rnd_next(void) +{ + PFS_user *user; + PFS_memory_class *memory_class; + bool has_more_user= true; + + for (m_pos.set_at(&m_next_pos); + has_more_user; + m_pos.next_user()) + { + user= global_user_container.get(m_pos.m_index_1, & has_more_user); + if (user != NULL) + { + do + { + memory_class= find_memory_class(m_pos.m_index_2); + if (memory_class != NULL) + { + if (! memory_class->is_global()) + { + make_row(user, memory_class); + m_next_pos.set_after(&m_pos); + return 0; + } + + m_pos.next_class(); + } + } + while (memory_class != NULL); + } + } + + return HA_ERR_END_OF_FILE; +} + +int table_mems_by_user_by_event_name::rnd_pos(const void *pos) +{ + PFS_user *user; + PFS_memory_class *memory_class; + + set_position(pos); + + user= global_user_container.get(m_pos.m_index_1); + if (user != NULL) + { + memory_class= find_memory_class(m_pos.m_index_2); + if (memory_class != NULL) + { + if (! memory_class->is_global()) + { + make_row(user, memory_class); + return 0; + } + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_mems_by_user_by_event_name +::make_row(PFS_user *user, PFS_memory_class *klass) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + user->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_user.make_row(user)) + return; + + m_row.m_event_name.make_row(klass); + + PFS_connection_memory_visitor visitor(klass); + PFS_connection_iterator::visit_user(user, + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! user->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(& visitor.m_stat); +} + +int table_mems_by_user_by_event_name::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* USER */ + m_row.m_user.set_field(f); + break; + case 1: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 2, ... HIGH_NUMBER_OF_BYTES_USED */ + m_row.m_stat.set_field(f->field_index - 2, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_mems_by_user_by_event_name.h b/storage/perfschema/table_mems_by_user_by_event_name.h new file mode 100644 index 00000000..c17f5d33 --- /dev/null +++ b/storage/perfschema/table_mems_by_user_by_event_name.h @@ -0,0 +1,130 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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 TABLE_MEMORY_SUMMARY_BY_USER_BY_EVENT_NAME_H +#define TABLE_MEMORY_SUMMARY_BY_USER_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_mems_by_user_by_event_name.h + Table MEMORY_SUMMARY_BY_USER_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_user.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.MEMORY_SUMMARY_BY_USER_BY_EVENT_NAME. */ +struct row_mems_by_user_by_event_name +{ + /** Column USER. */ + PFS_user_row m_user; + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_ALLOC, ... */ + PFS_memory_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_MEMORY_SUMMARY_BY_USER_BY_EVENT_NAME. + Index 1 on user (0 based) + Index 2 on memory class (1 based) +*/ +struct pos_mems_by_user_by_event_name +: public PFS_double_index +{ + pos_mems_by_user_by_event_name() + : PFS_double_index(0, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 1; + } + + inline void next_user(void) + { + m_index_1++; + m_index_2= 1; + } + + inline void next_class(void) + { + m_index_2++; + } +}; + +/** Table PERFORMANCE_SCHEMA.MEMORY_SUMMARY_BY_USER_BY_EVENT_NAME. */ +class table_mems_by_user_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_mems_by_user_by_event_name(); + +public: + ~table_mems_by_user_by_event_name() + {} + +private: + void make_row(PFS_user *user, PFS_memory_class *klass); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_mems_by_user_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_mems_by_user_by_event_name m_pos; + /** Next position. */ + pos_mems_by_user_by_event_name m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_mems_global_by_event_name.cc b/storage/perfschema/table_mems_global_by_event_name.cc new file mode 100644 index 00000000..69112a3b --- /dev/null +++ b/storage/perfschema/table_mems_global_by_event_name.cc @@ -0,0 +1,248 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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_mems_global_by_event_name.cc + Table MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_mems_global_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_builtin_memory.h" +#include "pfs_memory.h" +#include "field.h" + +THR_LOCK table_mems_global_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_mems_global_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_mems_global_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("memory_summary_global_by_event_name") }, + &pfs_readonly_acl, + table_mems_global_by_event_name::create, + NULL, /* write_row */ + table_mems_global_by_event_name::delete_all_rows, + table_mems_global_by_event_name::get_row_count, + sizeof(pos_t), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE memory_summary_global_by_event_name(" + "EVENT_NAME VARCHAR(128) not null comment 'Event name.'," + "COUNT_ALLOC BIGINT unsigned not null comment 'Total number of allocations to memory.'," + "COUNT_FREE BIGINT unsigned not null comment 'Total number of attempts to free the allocated memory.'," + "SUM_NUMBER_OF_BYTES_ALLOC BIGINT unsigned not null comment 'Total number of bytes allocated.'," + "SUM_NUMBER_OF_BYTES_FREE BIGINT unsigned not null comment 'Total number of bytes freed'," + "LOW_COUNT_USED BIGINT not null comment 'Lowest number of allocated blocks (lowest value of CURRENT_COUNT_USED).'," + "CURRENT_COUNT_USED BIGINT not null comment 'Currently allocated blocks that have not been freed (COUNT_ALLOC minus COUNT_FREE).'," + "HIGH_COUNT_USED BIGINT not null comment 'Highest number of allocated blocks (highest value of CURRENT_COUNT_USED).'," + "LOW_NUMBER_OF_BYTES_USED BIGINT not null comment 'Lowest number of bytes used.'," + "CURRENT_NUMBER_OF_BYTES_USED BIGINT not null comment 'Current number of bytes used (total allocated minus total freed).'," + "HIGH_NUMBER_OF_BYTES_USED BIGINT not null comment 'Highest number of bytes used.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_mems_global_by_event_name::create(void) +{ + return new table_mems_global_by_event_name(); +} + +int +table_mems_global_by_event_name::delete_all_rows(void) +{ + reset_memory_by_thread(); + reset_memory_by_account(); + reset_memory_by_user(); + reset_memory_by_host(); + reset_memory_global(); + return 0; +} + +ha_rows +table_mems_global_by_event_name::get_row_count(void) +{ + return memory_class_max; +} + +table_mems_global_by_event_name::table_mems_global_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_mems_global_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_mems_global_by_event_name::rnd_next(void) +{ + PFS_memory_class *pfs; + PFS_builtin_memory_class *pfs_builtin; + + /* Do not advertise hard coded instruments when disabled. */ + if (! pfs_initialized) + return HA_ERR_END_OF_FILE; + + for (m_pos.set_at(&m_next_pos); + m_pos.has_more_view(); + m_pos.next_view()) + { + switch (m_pos.m_index_1) + { + case pos_mems_global_by_event_name::VIEW_BUILTIN_MEMORY: + pfs_builtin= find_builtin_memory_class(m_pos.m_index_2); + if (pfs_builtin != NULL) + { + make_row(pfs_builtin); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_mems_global_by_event_name::VIEW_MEMORY: + pfs= find_memory_class(m_pos.m_index_2); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + } + } + + return HA_ERR_END_OF_FILE; +} + +int table_mems_global_by_event_name::rnd_pos(const void *pos) +{ + PFS_builtin_memory_class *pfs_builtin; + PFS_memory_class *pfs; + + /* Do not advertise hard coded instruments when disabled. */ + if (! pfs_initialized) + return HA_ERR_END_OF_FILE; + + set_position(pos); + + switch(m_pos.m_index_1) + { + case pos_mems_global_by_event_name::VIEW_BUILTIN_MEMORY: + pfs_builtin= find_builtin_memory_class(m_pos.m_index_2); + if (pfs_builtin != NULL) + { + make_row(pfs_builtin); + return 0; + } + break; + case pos_mems_global_by_event_name::VIEW_MEMORY: + pfs= find_memory_class(m_pos.m_index_2); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + break; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_mems_global_by_event_name::make_row(PFS_memory_class *klass) +{ + m_row.m_event_name.make_row(klass); + + PFS_connection_memory_visitor visitor(klass); + + if (klass->is_global()) + { + PFS_connection_iterator::visit_global(false, /* hosts */ + false, /* users */ + false, /* accounts */ + false, /* threads */ + false, /* THDs */ + &visitor); + } + else + { + PFS_connection_iterator::visit_global(true, /* hosts */ + false, /* users */ + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + &visitor); + } + + m_row.m_stat.set(& visitor.m_stat); + m_row_exists= true; +} + +void table_mems_global_by_event_name::make_row(PFS_builtin_memory_class *klass) +{ + m_row.m_event_name.make_row(& klass->m_class); + m_row.m_stat.set(& klass->m_stat); + m_row_exists= true; +} + +int table_mems_global_by_event_name::read_row_values(TABLE *table, + unsigned char *, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + default: /* 1, ... HIGH_NUMBER_OF_BYTES_USED */ + m_row.m_stat.set_field(f->field_index - 1, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_mems_global_by_event_name.h b/storage/perfschema/table_mems_global_by_event_name.h new file mode 100644 index 00000000..ae3cd043 --- /dev/null +++ b/storage/perfschema/table_mems_global_by_event_name.h @@ -0,0 +1,133 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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 TABLE_MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME_H +#define TABLE_MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_mems_global_by_event_name.h + Table MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_builtin_memory.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME. */ +struct row_mems_global_by_event_name +{ + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + /** Columns COUNT_ALLOC, ... */ + PFS_memory_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME. + Index 1 on view + Index 2 on instrument key (1 based) +*/ +struct pos_mems_global_by_event_name : public PFS_double_index +{ + static const uint FIRST_VIEW= 1; + static const uint VIEW_BUILTIN_MEMORY= 1; + static const uint VIEW_MEMORY= 2; + static const uint LAST_VIEW= 2; + + pos_mems_global_by_event_name() + : PFS_double_index(FIRST_VIEW, 1) + {} + + inline void reset(void) + { + m_index_1= FIRST_VIEW; + m_index_2= 1; + } + + inline bool has_more_view(void) + { return (m_index_1 <= LAST_VIEW); } + + inline void next_view(void) + { + m_index_1++; + m_index_2= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.MEMORY_SUMMARY_GLOBAL_BY_EVENT_NAME. */ +class table_mems_global_by_event_name : public PFS_engine_table +{ + typedef pos_mems_global_by_event_name pos_t; + +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_mems_global_by_event_name(); + +public: + ~table_mems_global_by_event_name() + {} + +private: + void make_row(PFS_builtin_memory_class *klass); + void make_row(PFS_memory_class *klass); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_mems_global_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_os_global_by_type.cc b/storage/perfschema/table_os_global_by_type.cc new file mode 100644 index 00000000..19629d44 --- /dev/null +++ b/storage/perfschema/table_os_global_by_type.cc @@ -0,0 +1,310 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_os_global_by_type.cc + Table OBJECTS_SUMMARY_GLOBAL_BY_TYPE (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_os_global_by_type.h" +#include "pfs_global.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_os_global_by_type::m_table_lock; + +PFS_engine_table_share_state +table_os_global_by_type::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_os_global_by_type::m_share= +{ + { C_STRING_WITH_LEN("objects_summary_global_by_type") }, + &pfs_truncatable_acl, + table_os_global_by_type::create, + NULL, /* write_row */ + table_os_global_by_type::delete_all_rows, + table_os_global_by_type::get_row_count, + sizeof(pos_os_global_by_type), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE objects_summary_global_by_type(" + "OBJECT_TYPE VARCHAR(64) comment 'Groups records together with OBJECT_SCHEMA and OBJECT_NAME.'," + "OBJECT_SCHEMA VARCHAR(64) comment 'Groups records together with OBJECT_TYPE and OBJECT_NAME.'," + "OBJECT_NAME VARCHAR(64) comment 'Groups records together with OBJECT_SCHEMA and OBJECT_TYPE.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_os_global_by_type::create(void) +{ + return new table_os_global_by_type(); +} + +int +table_os_global_by_type::delete_all_rows(void) +{ + reset_table_waits_by_table_handle(); + reset_table_waits_by_table(); + return 0; +} + +ha_rows +table_os_global_by_type::get_row_count(void) +{ + return global_table_share_container.get_row_count() + + global_program_container.get_row_count(); +} + +table_os_global_by_type::table_os_global_by_type() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_os_global_by_type::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_os_global_by_type::rnd_next(void) +{ + for (m_pos.set_at(&m_next_pos); + m_pos.has_more_view(); + m_pos.next_view()) + { + switch (m_pos.m_index_1) { + case pos_os_global_by_type::VIEW_TABLE: + { + PFS_table_share *table_share; + bool has_more_share= true; + + for (; + has_more_share; + m_pos.m_index_2++) + { + table_share= global_table_share_container.get(m_pos.m_index_2, & has_more_share); + if (table_share != NULL) + { + make_table_row(table_share); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + break; + case pos_os_global_by_type::VIEW_PROGRAM: + { + PFS_program *pfs_program; + bool has_more_program= true; + + for (; + has_more_program; + m_pos.m_index_2++) + { + pfs_program= global_program_container.get(m_pos.m_index_2, & has_more_program); + if (pfs_program != NULL) + { + make_program_row(pfs_program); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + break; + default: + break; + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_os_global_by_type::rnd_pos(const void *pos) +{ + set_position(pos); + + switch (m_pos.m_index_1) { + case pos_os_global_by_type::VIEW_TABLE: + { + PFS_table_share *table_share; + table_share= global_table_share_container.get(m_pos.m_index_2); + if (table_share != NULL) + { + make_table_row(table_share); + return 0; + } + } + break; + case pos_os_global_by_type::VIEW_PROGRAM: + { + PFS_program *pfs_program; + pfs_program= global_program_container.get(m_pos.m_index_2); + if (pfs_program != NULL) + { + make_program_row(pfs_program); + return 0; + } + } + break; + default: + break; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_os_global_by_type::make_program_row(PFS_program *pfs_program) +{ + pfs_optimistic_state lock; + PFS_single_stat cumulated_stat; + + m_row_exists= false; + + pfs_program->m_lock.begin_optimistic_lock(&lock); + + m_row.m_object.make_row(pfs_program); + + time_normalizer *normalizer= time_normalizer::get(wait_timer); + m_row.m_stat.set(normalizer, &pfs_program->m_sp_stat.m_timer1_stat); + + if (! pfs_program->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; +} + +void table_os_global_by_type::make_table_row(PFS_table_share *share) +{ + pfs_optimistic_state lock; + PFS_single_stat cumulated_stat; + uint safe_key_count; + + m_row_exists= false; + + share->m_lock.begin_optimistic_lock(&lock); + + m_row.m_object.make_row(share); + + /* This is a dirty read, some thread can write data while we are reading it */ + safe_key_count= sanitize_index_count(share->m_key_count); + + share->sum(& cumulated_stat, safe_key_count); + + if (! share->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + + if (share->get_refcount() > 0) + { + /* For all the table handles still opened ... */ + PFS_table_iterator it= global_table_container.iterate(); + PFS_table *table= it.scan_next(); + + while (table != NULL) + { + if (table->m_share == share) + { + /* + If the opened table handle is for this table share, + aggregate the table handle statistics. + */ + table->m_table_stat.sum(& cumulated_stat, safe_key_count); + } + table= it.scan_next(); + } + } + + time_normalizer *normalizer= time_normalizer::get(wait_timer); + m_row.m_stat.set(normalizer, &cumulated_stat); +} + +int table_os_global_by_type::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* OBJECT_TYPE */ + set_field_object_type(f, m_row.m_object.m_object_type); + break; + case 1: /* SCHEMA_NAME */ + set_field_varchar_utf8(f, m_row.m_object.m_schema_name, + m_row.m_object.m_schema_name_length); + break; + case 2: /* OBJECT_NAME */ + set_field_varchar_utf8(f, m_row.m_object.m_object_name, + m_row.m_object.m_object_name_length); + break; + case 3: /* COUNT */ + set_field_ulonglong(f, m_row.m_stat.m_count); + break; + case 4: /* SUM */ + set_field_ulonglong(f, m_row.m_stat.m_sum); + break; + case 5: /* MIN */ + set_field_ulonglong(f, m_row.m_stat.m_min); + break; + case 6: /* AVG */ + set_field_ulonglong(f, m_row.m_stat.m_avg); + break; + case 7: /* MAX */ + set_field_ulonglong(f, m_row.m_stat.m_max); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_os_global_by_type.h b/storage/perfschema/table_os_global_by_type.h new file mode 100644 index 00000000..7d0ed58c --- /dev/null +++ b/storage/perfschema/table_os_global_by_type.h @@ -0,0 +1,131 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_OBJECTS_SUMMARY_GLOBAL_BY_TYPE_H +#define TABLE_OBJECTS_SUMMARY_GLOBAL_BY_TYPE_H + +/** + @file storage/perfschema/table_os_global_by_type.h + Table OBJECTS_SUMMARY_GLOBAL_BY_TYPE (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_program.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.OBJECTS_SUMMARY_GLOBAL_BY_TYPE. +*/ +struct row_os_global_by_type +{ + /** Column OBJECT_TYPE, SCHEMA_NAME, OBJECT_NAME. */ + PFS_object_row m_object; + + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER_WAIT. */ + PFS_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.OBJECTS_SUMMARY_GLOBAL_BY_TYPE. + Index 1 on object type + Index 2 on object instance (0 based) +*/ +struct pos_os_global_by_type : public PFS_double_index, + public PFS_object_view_constants +{ + pos_os_global_by_type() + : PFS_double_index(FIRST_VIEW, 0) + {} + + inline void reset(void) + { + m_index_1= FIRST_VIEW; + m_index_2= 0; + } + + inline bool has_more_view(void) + { return (m_index_1 <= LAST_VIEW); } + + inline void next_view(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** Table PERFORMANCE_SCHEMA.OBJECTS_SUMMARY_GLOBAL_BY_TYPE. */ +class table_os_global_by_type : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_os_global_by_type(); + +public: + ~table_os_global_by_type() = default; + +protected: + void make_table_row(PFS_table_share *table_share); + void make_program_row(PFS_program *pfs_program); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_os_global_by_type m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_os_global_by_type m_pos; + /** Next position. */ + pos_os_global_by_type m_next_pos; +}; + + +/** @} */ +#endif diff --git a/storage/perfschema/table_performance_timers.cc b/storage/perfschema/table_performance_timers.cc new file mode 100644 index 00000000..da5f38c9 --- /dev/null +++ b/storage/perfschema/table_performance_timers.cc @@ -0,0 +1,184 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_performance_timers.cc + Table PERFORMANCE_TIMERS (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "table_performance_timers.h" +#include "pfs_timer.h" +#include "pfs_global.h" +#include "field.h" + +THR_LOCK table_performance_timers::m_table_lock; + +PFS_engine_table_share_state +table_performance_timers::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_performance_timers::m_share= +{ + { C_STRING_WITH_LEN("performance_timers") }, + &pfs_readonly_acl, + table_performance_timers::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_performance_timers::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE performance_timers(" + "TIMER_NAME ENUM ('CYCLE', 'NANOSECOND', 'MICROSECOND', 'MILLISECOND', 'TICK') not null comment 'Time name, used in the setup_timers table.'," + "TIMER_FREQUENCY BIGINT comment 'Number of timer units per second. Dependent on the processor speed.'," + "TIMER_RESOLUTION BIGINT comment 'Number of timer units by which timed values increase each time.'," + "TIMER_OVERHEAD BIGINT comment 'Minimum timer overhead, determined during initialization by calling the timer 20 times and selecting the smallest value. Total overhead will be at least double this, as the timer is called at the beginning and end of each timed event.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_performance_timers::create(void) +{ + return new table_performance_timers(); +} + +ha_rows +table_performance_timers::get_row_count(void) +{ + return COUNT_TIMER_NAME; +} + +table_performance_timers::table_performance_timers() + : PFS_engine_table(&m_share, &m_pos), + m_row(NULL), m_pos(0), m_next_pos(0) +{ + int index; + + index= (int)TIMER_NAME_CYCLE - FIRST_TIMER_NAME; + m_data[index].m_timer_name= TIMER_NAME_CYCLE; + m_data[index].m_info= sys_timer_info.cycles; + + index= (int)TIMER_NAME_NANOSEC - FIRST_TIMER_NAME; + m_data[index].m_timer_name= TIMER_NAME_NANOSEC; + m_data[index].m_info= sys_timer_info.nanoseconds; + + index= (int)TIMER_NAME_MICROSEC - FIRST_TIMER_NAME; + m_data[index].m_timer_name= TIMER_NAME_MICROSEC; + m_data[index].m_info= sys_timer_info.microseconds; + + index= (int)TIMER_NAME_MILLISEC - FIRST_TIMER_NAME; + m_data[index].m_timer_name= TIMER_NAME_MILLISEC; + m_data[index].m_info= sys_timer_info.milliseconds; + + index= (int)TIMER_NAME_TICK - FIRST_TIMER_NAME; + m_data[index].m_timer_name= TIMER_NAME_TICK; + m_data[index].m_info= sys_timer_info.ticks; +} + +void table_performance_timers::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_performance_timers::rnd_next(void) +{ + int result; + + m_pos.set_at(&m_next_pos); + + if (m_pos.m_index < COUNT_TIMER_NAME) + { + m_row= &m_data[m_pos.m_index]; + m_next_pos.set_after(&m_pos); + result= 0; + } + else + { + m_row= NULL; + result= HA_ERR_END_OF_FILE; + } + + return result; +} + +int table_performance_timers::rnd_pos(const void *pos) +{ + set_position(pos); + assert(m_pos.m_index < COUNT_TIMER_NAME); + m_row= &m_data[m_pos.m_index]; + return 0; +} + +int table_performance_timers::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + assert(m_row); + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* TIMER_NAME */ + set_field_enum(f, m_row->m_timer_name); + break; + case 1: /* TIMER_FREQUENCY */ + if (m_row->m_info.routine != 0) + set_field_ulonglong(f, m_row->m_info.frequency); + else + f->set_null(); + break; + case 2: /* TIMER_RESOLUTION */ + if (m_row->m_info.routine != 0) + set_field_ulonglong(f, m_row->m_info.resolution); + else + f->set_null(); + break; + case 3: /* TIMER_OVERHEAD */ + if (m_row->m_info.routine != 0) + set_field_ulonglong(f, m_row->m_info.overhead); + else + f->set_null(); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_performance_timers.h b/storage/perfschema/table_performance_timers.h new file mode 100644 index 00000000..a28b4d3f --- /dev/null +++ b/storage/perfschema/table_performance_timers.h @@ -0,0 +1,92 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_PERFORMANCE_TIMERS_H +#define TABLE_PERFORMANCE_TIMERS_H + +/** + @file storage/perfschema/table_performance_timers.h + Table PERFORMANCE_TIMERS (declarations). +*/ + +#include <my_rdtsc.h> +#include "pfs_column_types.h" +#include "pfs_engine_table.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.PERFORMANCE_TIMERS. */ +struct row_performance_timers +{ + /** Column TIMER_NAME. */ + enum_timer_name m_timer_name; + /** + Columns ROUTINE (not displayed), TIMER_OVERHEAD, + TIMER_FREQUENCY, TIMER_RESOLUTION. + */ + struct my_timer_unit_info m_info; +}; + +/** Table PERFORMANCE_SCHEMA.PERFORMANCE_TIMERS. */ +class table_performance_timers : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + +protected: + table_performance_timers(); + +public: + ~table_performance_timers() = default; + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_performance_timers *m_row; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; + row_performance_timers m_data[COUNT_TIMER_NAME]; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_prepared_stmt_instances.cc b/storage/perfschema/table_prepared_stmt_instances.cc new file mode 100644 index 00000000..f064d8f0 --- /dev/null +++ b/storage/perfschema/table_prepared_stmt_instances.cc @@ -0,0 +1,301 @@ +/* Copyright (c) 2014, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_prepared_stmt_instances.cc + Table PREPARED_STATEMENTS_INSTANCES (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "pfs_global.h" +#include "pfs_instr.h" +#include "pfs_timer.h" +#include "pfs_visitor.h" +#include "pfs_prepared_stmt.h" +#include "table_prepared_stmt_instances.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_prepared_stmt_instances::m_table_lock; + +PFS_engine_table_share_state +table_prepared_stmt_instances::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_prepared_stmt_instances::m_share= +{ + { C_STRING_WITH_LEN("prepared_statements_instances") }, + &pfs_truncatable_acl, + table_prepared_stmt_instances::create, + NULL, /* write_row */ + table_prepared_stmt_instances::delete_all_rows, + table_prepared_stmt_instances::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE prepared_statements_instances(" + "OBJECT_INSTANCE_BEGIN bigint(20) unsigned NOT NULL comment 'The address in memory of the instrumented prepared statement.'," + "STATEMENT_ID bigint(20) unsigned NOT NULL comment 'The internal statement ID assigned by the server.'," + "STATEMENT_NAME varchar(64) default NULL comment 'For the binary protocol, this column is NULL. For the text protocol, this column is the external statement name assigned by the user.'," + "SQL_TEXT longtext NOT NULL comment 'The prepared statement text, with ? placeholder markers.'," + "OWNER_THREAD_ID bigint(20) unsigned NOT NULL comment 'Event thread id that created the prepared statement.'," + "OWNER_EVENT_ID bigint(20) unsigned NOT NULL comment 'Event id that created the prepared statement.'," + "OWNER_OBJECT_TYPE enum('EVENT','FUNCTION','PROCEDURE','TABLE','TRIGGER') DEFAULT NULL comment 'NULL for a prepared statement created by a client session. Type of the stored program that created the prepared statement.'," + "OWNER_OBJECT_SCHEMA varchar(64) DEFAULT NULL comment 'NULL for a prepared statement created by a client session. Schema of the stored program that created the prepared statement.'," + "OWNER_OBJECT_NAME varchar(64) DEFAULT NULL comment 'NULL for a prepared statement created by a client session. Name of the stored program that created the prepared statement.'," + "TIMER_PREPARE bigint(20) unsigned NOT NULL comment 'The time spent executing the statement preparation itself.'," + "COUNT_REPREPARE bigint(20) unsigned NOT NULL comment 'The number of times the statement was reprepared internally.'," + "COUNT_EXECUTE bigint(20) unsigned NOT NULL comment 'Total times the prepared statement was executed.'," + "SUM_TIMER_EXECUTE bigint(20) unsigned NOT NULL comment 'Total time spent executing all prepared statements.'," + "MIN_TIMER_EXECUTE bigint(20) unsigned NOT NULL comment 'Minimum time spent executing any of the prepared statements.'," + "AVG_TIMER_EXECUTE bigint(20) unsigned NOT NULL comment 'Average time spent executing any of the prepared statements.'," + "MAX_TIMER_EXECUTE bigint(20) unsigned NOT NULL comment 'Maximum time spent executing any of the prepared statements.'," + "SUM_LOCK_TIME bigint(20) unsigned NOT NULL comment 'The total time spent (in picoseconds) waiting for table locks for the prepared statements.'," + "SUM_ERRORS bigint(20) unsigned NOT NULL comment 'The total number of errors that occurend for the prepared statements.'," + "SUM_WARNINGS bigint(20) unsigned NOT NULL comment 'The total number of warnings that occurend for the prepared statements.'," + "SUM_ROWS_AFFECTED bigint(20) unsigned NOT NULL comment 'The total number of affected rows by the prepared statements.'," + "SUM_ROWS_SENT bigint(20) unsigned NOT NULL comment 'The total number of rows returned by the prepared statements.'," + "SUM_ROWS_EXAMINED bigint(20) unsigned NOT NULL comment 'The total number of rows examined by the prepared statements.'," + "SUM_CREATED_TMP_DISK_TABLES bigint(20) unsigned NOT NULL comment 'The total number of on-disk temporary tables created by the prepared statements.'," + "SUM_CREATED_TMP_TABLES bigint(20) unsigned NOT NULL comment 'The total number of in-memory temporary tables created by the prepared statements.'," + "SUM_SELECT_FULL_JOIN bigint(20) unsigned NOT NULL comment 'The total number of full joins executed by the prepared statements.'," + "SUM_SELECT_FULL_RANGE_JOIN bigint(20) unsigned NOT NULL comment 'The total number of range search joins executed by the prepared statements.'," + "SUM_SELECT_RANGE bigint(20) unsigned NOT NULL comment 'The total number of joins that used ranges on the first table executed by the prepared statements.'," + "SUM_SELECT_RANGE_CHECK bigint(20) unsigned NOT NULL comment 'The total number of joins that check for key usage after each row executed by the prepared statements.'," + "SUM_SELECT_SCAN bigint(20) unsigned NOT NULL comment 'The total number of joins that did a full scan of the first table executed by the prepared statements.'," + "SUM_SORT_MERGE_PASSES bigint(20) unsigned NOT NULL comment 'The total number of merge passes that the sort algorithm has had to do for the prepared statements.'," + "SUM_SORT_RANGE bigint(20) unsigned NOT NULL comment 'The total number of sorts that were done using ranges for the prepared statements.'," + "SUM_SORT_ROWS bigint(20) unsigned NOT NULL comment 'The total number of sorted rows that were sorted by the prepared statements.'," + "SUM_SORT_SCAN bigint(20) unsigned NOT NULL comment 'The total number of sorts that were done by scanning the table by the prepared statements.'," + "SUM_NO_INDEX_USED bigint(20) unsigned NOT NULL comment 'The total number of statements that performed a table scan without using an index.'," + "SUM_NO_GOOD_INDEX_USED bigint(20) unsigned NOT NULL comment 'The total number of statements where no good index was found.')")}, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_prepared_stmt_instances::create(void) +{ + return new table_prepared_stmt_instances(); +} + +int +table_prepared_stmt_instances::delete_all_rows(void) +{ + reset_prepared_stmt_instances(); + return 0; +} + +ha_rows +table_prepared_stmt_instances::get_row_count(void) +{ + return global_prepared_stmt_container.get_row_count(); +} + +table_prepared_stmt_instances::table_prepared_stmt_instances() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_prepared_stmt_instances::reset_position(void) +{ + m_pos= 0; + m_next_pos= 0; +} + +int table_prepared_stmt_instances::rnd_next(void) +{ + PFS_prepared_stmt* pfs; + + m_pos.set_at(&m_next_pos); + PFS_prepared_stmt_iterator it= global_prepared_stmt_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int +table_prepared_stmt_instances::rnd_pos(const void *pos) +{ + PFS_prepared_stmt* pfs; + + set_position(pos); + + pfs= global_prepared_stmt_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + + +void table_prepared_stmt_instances::make_row(PFS_prepared_stmt* prepared_stmt) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + prepared_stmt->m_lock.begin_optimistic_lock(&lock); + + m_row.m_identity= prepared_stmt->m_identity; + + m_row.m_stmt_id= prepared_stmt->m_stmt_id; + + m_row.m_owner_thread_id= prepared_stmt->m_owner_thread_id; + m_row.m_owner_event_id= prepared_stmt->m_owner_event_id; + + m_row.m_stmt_name_length= prepared_stmt->m_stmt_name_length; + if(m_row.m_stmt_name_length > 0) + memcpy(m_row.m_stmt_name, prepared_stmt->m_stmt_name, + m_row.m_stmt_name_length); + + m_row.m_sql_text_length= prepared_stmt->m_sqltext_length; + if(m_row.m_sql_text_length > 0) + memcpy(m_row.m_sql_text, prepared_stmt->m_sqltext, + m_row.m_sql_text_length); + + m_row.m_owner_object_type= prepared_stmt->m_owner_object_type; + + m_row.m_owner_object_name_length= prepared_stmt->m_owner_object_name_length; + if(m_row.m_owner_object_name_length > 0) + memcpy(m_row.m_owner_object_name, prepared_stmt->m_owner_object_name, + m_row.m_owner_object_name_length); + + m_row.m_owner_object_schema_length= prepared_stmt->m_owner_object_schema_length; + if(m_row.m_owner_object_schema_length > 0) + memcpy(m_row.m_owner_object_schema, prepared_stmt->m_owner_object_schema, + m_row.m_owner_object_schema_length); + + time_normalizer *normalizer= time_normalizer::get(statement_timer); + /* Get prepared statement prepare stats. */ + m_row.m_prepare_stat.set(normalizer, & prepared_stmt->m_prepare_stat); + /* Get prepared statement reprepare stats. */ + m_row.m_reprepare_stat.set(normalizer, & prepared_stmt->m_reprepare_stat); + /* Get prepared statement execute stats. */ + m_row.m_execute_stat.set(normalizer, & prepared_stmt->m_execute_stat); + + if (! prepared_stmt->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; +} + +int table_prepared_stmt_instances +::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* + Set the null bits. + */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* OBJECT_INSTANCE_BEGIN */ + set_field_ulonglong(f, (intptr)m_row.m_identity); + break; + case 1: /* STATEMENT_ID */ + set_field_ulonglong(f, m_row.m_stmt_id); + break; + case 2: /* STATEMENT_NAME */ + if(m_row.m_stmt_name_length > 0) + set_field_varchar_utf8(f, m_row.m_stmt_name, + m_row.m_stmt_name_length); + else + f->set_null(); + break; + case 3: /* SQL_TEXT */ + if(m_row.m_sql_text_length > 0) + set_field_longtext_utf8(f, m_row.m_sql_text, + m_row.m_sql_text_length); + else + f->set_null(); + break; + case 4: /* OWNER_THREAD_ID */ + set_field_ulonglong(f, m_row.m_owner_thread_id); + break; + case 5: /* OWNER_EVENT_ID */ + if(m_row.m_owner_event_id > 0) + set_field_ulonglong(f, m_row.m_owner_event_id); + else + f->set_null(); + break; + case 6: /* OWNER_OBJECT_TYPE */ + if(m_row.m_owner_object_type != 0) + set_field_enum(f, m_row.m_owner_object_type); + else + f->set_null(); + break; + case 7: /* OWNER_OBJECT_SCHEMA */ + if(m_row.m_owner_object_schema_length > 0) + set_field_varchar_utf8(f, m_row.m_owner_object_schema, + m_row.m_owner_object_schema_length); + else + f->set_null(); + break; + case 8: /* OWNER_OBJECT_NAME */ + if(m_row.m_owner_object_name_length > 0) + set_field_varchar_utf8(f, m_row.m_owner_object_name, + m_row.m_owner_object_name_length); + else + f->set_null(); + break; + case 9: /* TIMER_PREPARE */ + m_row.m_prepare_stat.set_field(1, f); + break; + case 10: /* COUNT_REPREPARE */ + m_row.m_reprepare_stat.set_field(0, f); + break; + default: /* 14, ... COUNT/SUM/MIN/AVG/MAX */ + m_row.m_execute_stat.set_field(f->field_index - 11, f); + break; + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_prepared_stmt_instances.h b/storage/perfschema/table_prepared_stmt_instances.h new file mode 100644 index 00000000..8bd9acaf --- /dev/null +++ b/storage/perfschema/table_prepared_stmt_instances.h @@ -0,0 +1,133 @@ +/* Copyright (c) 2014, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_PREPARED_STMT_INSTANCES +#define TABLE_PREPARED_STMT_INSTANCES + +/** + @file storage/perfschema/table_prepared_stmt_instances.h + Table PREPARED_STATEMENT_INSTANCE(declarations). +*/ + +#include "table_helper.h" +#include "pfs_prepared_stmt.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.PREPARED_STATEMENT_INSTANCES. +*/ +struct row_prepared_stmt_instances +{ + /** Column OBJECT_INSTANCE_BEGIN. */ + const void *m_identity; + + /** Column STMT_ID. */ + ulonglong m_stmt_id; + + /** Column STMT_NAME. */ + char m_stmt_name[COL_INFO_SIZE]; + int m_stmt_name_length; + + /** Column SQL_TEXT. */ + char m_sql_text[COL_INFO_SIZE]; + int m_sql_text_length; + + /** Column OWNER_THREAD_ID. */ + ulonglong m_owner_thread_id; + + /** Column OWNER_EVENT_ID. */ + ulonglong m_owner_event_id; + + /** Column OWNER_OBJECT_TYPE. */ + enum_object_type m_owner_object_type; + + /** Column OWNER_OBJECT_SCHEMA */ + char m_owner_object_schema[COL_OBJECT_SCHEMA_SIZE]; + int m_owner_object_schema_length; + + /** Column OWNER_OBJECT_NAME */ + char m_owner_object_name[COL_OBJECT_NAME_SIZE]; + int m_owner_object_name_length; + + /** Columns TIMER_PREPARE. */ + PFS_stat_row m_prepare_stat; + + /** Columns COUNT_REPREPARE. */ + PFS_stat_row m_reprepare_stat; + + /** Columns COUNT_STAR...SUM_NO_GOOD_INDEX_USED. */ + PFS_statement_stat_row m_execute_stat; +}; + +/** Table PERFORMANCE_SCHEMA.PREPARED_STATEMENT_INSTANCES. */ +class table_prepared_stmt_instances : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_prepared_stmt_instances(); + +public: + ~table_prepared_stmt_instances() + {} + +protected: + void make_row(PFS_prepared_stmt*); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_prepared_stmt_instances m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_processlist.cc b/storage/perfschema/table_processlist.cc new file mode 100644 index 00000000..8c91a523 --- /dev/null +++ b/storage/perfschema/table_processlist.cc @@ -0,0 +1,360 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_processlist.cc + TABLE PROCESSLIST. +*/ + +#include "my_global.h" + +#include "table_processlist.h" + +#include <assert.h> +// #include "lex_string.h" +#include "my_compiler.h" + +#include "my_thread.h" +//#include "auth_acls.h" +#include "sql_acl.h" +#include "field.h" +#include "sql_class.h" +#include "sql_parse.h" +#include "table.h" +#include "pfs_buffer_container.h" +#include "pfs_global.h" +#include "pfs_instr.h" +#include "pfs_instr_class.h" + +THR_LOCK table_processlist::m_table_lock; + +PFS_engine_table_share_state +table_processlist::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share table_processlist::m_share = { + {C_STRING_WITH_LEN("processlist")}, + &pfs_readonly_processlist_acl, + table_processlist::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + cursor_by_thread::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + &m_field_def, + false, /* m_perpetual */ + true, /* m_optional */ + &m_share_state +}; + +PFS_engine_table *table_processlist::create() { + return new table_processlist(); +} + +table_processlist::table_processlist() + : cursor_by_thread(&m_share), m_row_exists(false) { + m_row_priv.m_auth = PROCESSLIST_DENIED; +} + +int table_processlist::set_access(void) { + THD *thd = current_thd; + if (thd == NULL) { + /* Robustness, no user session. */ + m_row_priv.m_auth = PROCESSLIST_DENIED; + return 0; + } + + if (thd->security_context()->check_access(PROCESS_ACL)) { + /* PROCESS_ACL granted. */ + m_row_priv.m_auth = PROCESSLIST_ALL; + return 0; + } + + LEX_CSTRING client_priv_user = thd->security_context()->priv_user(); + if (client_priv_user.length == 0) { + /* Anonymous user. */ + m_row_priv.m_auth = PROCESSLIST_DENIED; + return 0; + } + + /* Authenticated user, PROCESS_ACL not granted. */ + m_row_priv.m_auth = PROCESSLIST_USER_ONLY; + m_row_priv.m_priv_user_length = + std::min(client_priv_user.length, sizeof(m_row_priv.m_priv_user)); + memcpy(m_row_priv.m_priv_user, client_priv_user.str, + m_row_priv.m_priv_user_length); + return 0; +} + +int table_processlist::rnd_init(bool scan) { + set_access(); + return 0; +} + +void table_processlist::make_row(PFS_thread *pfs) { + pfs_optimistic_state lock; + pfs_optimistic_state session_lock; + pfs_optimistic_state stmt_lock; + PFS_stage_class *stage_class; + PFS_thread_class *safe_class; + + m_row_exists = false; + + if (m_row_priv.m_auth == PROCESSLIST_DENIED) { + return; + } + + /* Protect this reader against thread termination */ + pfs->m_lock.begin_optimistic_lock(&lock); + + safe_class = sanitize_thread_class(pfs->m_class); + if (unlikely(safe_class == NULL)) { + return; + } + + /* Ignore background threads. */ + if (pfs->m_username_length == 0 || pfs->m_processlist_id == 0) return; + + m_row.m_processlist_id = pfs->m_processlist_id; + + /* Protect this reader against session attribute changes */ + pfs->m_session_lock.begin_optimistic_lock(&session_lock); + + /* Maintain user/host compatibility with the legacy SHOW PROCESSLIST. */ + const char *username = pfs->m_username; + uint username_len = pfs->m_username_length; + uint hostname_len = pfs->m_hostname_length; + + if (pfs->m_class->is_system_thread()) { + if (username_len == 0 || + (!strncmp(username, "root", 4) && username_len == 4)) { + username = "system user"; + username_len = strlen(username); + hostname_len = 0; + } + } else { + if (username_len == 0) { + username = "unauthenticated user"; + username_len = strlen(username); + hostname_len = 0; + } + } + + m_row.m_username_length = username_len; + if (unlikely(m_row.m_username_length > sizeof(m_row.m_username))) { + return; + } + + if (m_row.m_username_length != 0) { + memcpy(m_row.m_username, username, username_len); + } + + m_row.m_hostname_length = hostname_len; + if (unlikely(m_row.m_hostname_length > sizeof(m_row.m_hostname))) { + return; + } + + if (m_row.m_hostname_length != 0) { + memcpy(m_row.m_hostname, pfs->m_hostname, m_row.m_hostname_length); + } + + if (!pfs->m_session_lock.end_optimistic_lock(&session_lock)) { + /* + One of the columns: + - USER + - HOST + is being updated. + Do not discard the entire row. + Do not loop waiting for a stable value. + Just return NULL values. + */ + m_row.m_username_length = 0; + m_row.m_hostname_length = 0; + } + + /* Enforce row filtering. */ + if (m_row_priv.m_auth == PROCESSLIST_USER_ONLY) { + if (m_row.m_username_length != m_row_priv.m_priv_user_length) { + return; + } + if (strncmp(m_row.m_username, m_row_priv.m_priv_user, + m_row_priv.m_priv_user_length) != 0) { + return; + } + } + + /* Protect this reader against statement attributes changes */ + pfs->m_stmt_lock.begin_optimistic_lock(&stmt_lock); + + m_row.m_dbname_length = pfs->m_dbname_length; + if (unlikely(m_row.m_dbname_length > sizeof(m_row.m_dbname))) { + return; + } + + if (m_row.m_dbname_length != 0) { + memcpy(m_row.m_dbname, pfs->m_dbname, m_row.m_dbname_length); + } + + m_row.m_processlist_info_ptr = &pfs->m_processlist_info[0]; + m_row.m_processlist_info_length = pfs->m_processlist_info_length; + + if (!pfs->m_stmt_lock.end_optimistic_lock(&stmt_lock)) { + /* + One of the columns: + - DB + - INFO + is being updated. + Do not discard the entire row. + Do not loop waiting for a stable value. + Just return NULL values. + */ + m_row.m_dbname_length = 0; + m_row.m_processlist_info_length = 0; + } + + /* Dirty read, sanitize the command. */ + m_row.m_command = pfs->m_command; + if ((m_row.m_command < 0) || (m_row.m_command > COM_END)) { + m_row.m_command = COM_END; + } + + m_row.m_start_time = pfs->m_start_time; + + stage_class = find_stage_class(pfs->m_stage); + if (stage_class != NULL) { + m_row.m_processlist_state_ptr = + stage_class->m_name + stage_class->m_prefix_length; + m_row.m_processlist_state_length = + stage_class->m_name_length - stage_class->m_prefix_length; + if (m_row.m_processlist_state_length > 64) { + /* + Column STATE is VARCHAR(64) + for compatibility reasons with the historical + INFORMATION_SCHEMA.PROCESSLIST table. + Stages however can have longer names. + We silently truncate data here, + so it fits into STATE. + */ + m_row.m_processlist_state_length = 64; + } + } else { + m_row.m_processlist_state_ptr = ""; + m_row.m_processlist_state_length = 0; + } + + m_row.m_port = pfs->m_peer_port; + + if (m_row.m_hostname_length > 0 && m_row.m_port != 0) { + /* Create HOST:PORT. */ + char str_port[10]; + sprintf(str_port, ":%d", m_row.m_port); + std::string host(m_row.m_hostname, m_row.m_hostname_length); + std::string host_ip = host + str_port; + m_row.m_hostname_length = + std::min((int)HOST_AND_PORT_LENGTH, (int)host_ip.length()); + memcpy(m_row.m_hostname, host_ip.c_str(), m_row.m_hostname_length); + } + + if (!pfs->m_lock.end_optimistic_lock(&lock)) { + return; + } + + m_row_exists = true; + return; +} + +int table_processlist::read_row_values(TABLE *table, unsigned char *buf, + Field **fields, bool read_all) { + Field *f; + + if (unlikely(!m_row_exists)) { + return HA_ERR_RECORD_DELETED; + } + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0] = 0; + + for (; (f = *fields); fields++) { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) { + switch (f->field_index) { + case 0: /* ID */ + set_field_ulonglong(f, m_row.m_processlist_id); + break; + case 1: /* USER */ + if (m_row.m_username_length > 0) { + set_field_varchar_utf8(f, m_row.m_username, + m_row.m_username_length); + } else { + f->set_null(); + } + break; + case 2: /* HOST */ + if (m_row.m_hostname_length > 0) { + set_field_varchar_utf8(f, m_row.m_hostname, + m_row.m_hostname_length); + } else { + f->set_null(); + } + break; + case 3: /* DB */ + if (m_row.m_dbname_length > 0) { + set_field_varchar_utf8(f, m_row.m_dbname, m_row.m_dbname_length); + } else { + f->set_null(); + } + break; + case 4: /* COMMAND */ + set_field_varchar_utf8(f, command_name[m_row.m_command].str, + command_name[m_row.m_command].length); + break; + case 5: /* TIME */ + if (m_row.m_start_time) { + time_t now = my_time(0); + ulonglong elapsed = + (now > m_row.m_start_time ? now - m_row.m_start_time : 0); + set_field_ulonglong(f, elapsed); + } else { + f->set_null(); + } + break; + case 6: /* STATE */ + /* For compatibility, leave blank if state is NULL. */ + set_field_varchar_utf8(f, m_row.m_processlist_state_ptr, + m_row.m_processlist_state_length); + break; + case 7: /* INFO */ + if (m_row.m_processlist_info_length > 0) + set_field_blob(f, m_row.m_processlist_info_ptr, + m_row.m_processlist_info_length); + else { + f->set_null(); + } + break; + default: + assert(false); + } + } + } + return 0; +} diff --git a/storage/perfschema/table_processlist.h b/storage/perfschema/table_processlist.h new file mode 100644 index 00000000..2cf55e1e --- /dev/null +++ b/storage/perfschema/table_processlist.h @@ -0,0 +1,133 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef TABLE_PROCESSLIST_H +#define TABLE_PROCESSLIST_H + +/** + @file storage/perfschema/table_processlist.h + TABLE THREADS. +*/ + +#include <sys/types.h> +#include <time.h> +#include <algorithm> + +// #include "my_hostname.h" +// #include "my_inttypes.h" +#include "cursor_by_thread.h" +#include "pfs_column_types.h" + +struct PFS_thread; + +/** + @addtogroup performance_schema_tables + @{ +*/ + +/** + A row of PERFORMANCE_SCHEMA.PROCESSLIST. +*/ +struct row_processlist { + /** Column ID. */ + ulonglong m_processlist_id; + /** Column USER. */ + char m_username[USERNAME_LENGTH]; + /** Length in bytes of @c m_username. */ + uint m_username_length; + /** Column HOST. */ + char m_hostname[HOST_AND_PORT_LENGTH]; + /** Length in bytes of @c m_hostname. */ + uint m_hostname_length; + /** Port. */ + uint m_port; + /** Column DB. */ + char m_dbname[NAME_LEN]; + /** Length in bytes of @c m_dbname. */ + uint m_dbname_length; + /** Column COMMAND. */ + int m_command; + /** Column TIME. */ + time_t m_start_time; + /** Column STATE. */ + const char *m_processlist_state_ptr; + /** Length in bytes of @c m_processlist_state_ptr. */ + uint m_processlist_state_length; + /** Column INFO. */ + const char *m_processlist_info_ptr; + /** Length in bytes of @c m_processlist_info_ptr. */ + uint m_processlist_info_length; +}; + +enum enum_priv_processlist { + /** User is not allowed to see any data. */ + PROCESSLIST_DENIED, + /** User does not have the PROCESS_ACL priviledge. */ + PROCESSLIST_USER_ONLY, + /** User has the PROCESS_ACL priviledge. */ + PROCESSLIST_ALL +}; + +struct row_priv_processlist { + enum enum_priv_processlist m_auth; + char m_priv_user[USERNAME_LENGTH]; + size_t m_priv_user_length; +}; + +/** Table PERFORMANCE_SCHEMA.PROCESSLIST. */ +class table_processlist : public cursor_by_thread { + public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + /** Table builder */ + static PFS_engine_table *create(); + + protected: + table_processlist(); + + virtual int rnd_init(bool scan); + + virtual int read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all); + int set_access(void); + + public: + virtual ~table_processlist() {} + + private: + virtual void make_row(PFS_thread *pfs); + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_processlist m_row; + /** Row privileges. */ + row_priv_processlist m_row_priv; + /** True if the current row exists. */ + bool m_row_exists; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_replication_applier_configuration.cc b/storage/perfschema/table_replication_applier_configuration.cc new file mode 100644 index 00000000..601e2b49 --- /dev/null +++ b/storage/perfschema/table_replication_applier_configuration.cc @@ -0,0 +1,188 @@ +/* + Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_replication_applier_configuration.cc + Table replication_applier_configuration (implementation). +*/ + +//#define HAVE_REPLICATION + +#include "my_global.h" +#ifdef HAVE_REPLICATION +#include "table_replication_applier_configuration.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "slave.h" +#include "rpl_rli.h" +#include "rpl_mi.h" +#include "sql_parse.h" + +THR_LOCK table_replication_applier_configuration::m_table_lock; + +PFS_engine_table_share_state +table_replication_applier_configuration::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_replication_applier_configuration::m_share= +{ + { C_STRING_WITH_LEN("replication_applier_configuration") }, + &pfs_readonly_acl, + table_replication_applier_configuration::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_replication_applier_configuration::get_row_count, + sizeof(pos_t), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE replication_applier_configuration(" + "CHANNEL_NAME VARCHAR(256) collate utf8_general_ci not null comment 'Replication channel name.'," + "DESIRED_DELAY INTEGER not null comment 'Target number of seconds the replica should be delayed to the master.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_replication_applier_configuration::create(void) +{ + return new table_replication_applier_configuration(); +} + +table_replication_applier_configuration + ::table_replication_applier_configuration() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +table_replication_applier_configuration + ::~table_replication_applier_configuration() +{} + +void table_replication_applier_configuration::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + + +ha_rows table_replication_applier_configuration::get_row_count() +{ + return master_info_index->master_info_hash.records; +} + + +int table_replication_applier_configuration::rnd_next(void) +{ + Master_info *mi; + mysql_mutex_lock(&LOCK_active_mi); + + for (m_pos.set_at(&m_next_pos); + m_pos.m_index < master_info_index->master_info_hash.records; + m_pos.next()) + { + mi= (Master_info *)my_hash_element(&master_info_index->master_info_hash, m_pos.m_index); + + if (mi && mi->host[0]) + { + make_row(mi); + m_next_pos.set_after(&m_pos); + mysql_mutex_unlock(&LOCK_active_mi); + return 0; + } + } + + mysql_mutex_unlock(&LOCK_active_mi); + return HA_ERR_END_OF_FILE; +} + +int table_replication_applier_configuration::rnd_pos(const void *pos) +{ + Master_info *mi; + int res= HA_ERR_RECORD_DELETED; + + set_position(pos); + + mysql_mutex_lock(&LOCK_active_mi); + + if ((mi= (Master_info *)my_hash_element(&master_info_index->master_info_hash, m_pos.m_index))) + { + make_row(mi); + res= 0; + } + + mysql_mutex_unlock(&LOCK_active_mi); + return res; +} + +void table_replication_applier_configuration::make_row(Master_info *mi) +{ + m_row_exists= false; + + DBUG_ASSERT(mi != NULL); + + mysql_mutex_lock(&mi->data_lock); + mysql_mutex_lock(&mi->rli.data_lock); + + m_row.channel_name_length= static_cast<uint>(mi->connection_name.length); + memcpy(m_row.channel_name, mi->connection_name.str, m_row.channel_name_length); + m_row.desired_delay= mi->rli.get_sql_delay(); + + mysql_mutex_unlock(&mi->rli.data_lock); + mysql_mutex_unlock(&mi->data_lock); + + m_row_exists= true; +} + +int table_replication_applier_configuration::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + DBUG_ASSERT(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* channel_name */ + set_field_varchar_utf8(f, m_row.channel_name, m_row.channel_name_length); + break; + case 1: /* desired_delay */ + set_field_ulong(f, static_cast<ulong>(m_row.desired_delay)); + break; + default: + assert(false); + } + } + } + return 0; +} +#endif diff --git a/storage/perfschema/table_replication_applier_configuration.h b/storage/perfschema/table_replication_applier_configuration.h new file mode 100644 index 00000000..580b78cf --- /dev/null +++ b/storage/perfschema/table_replication_applier_configuration.h @@ -0,0 +1,106 @@ +/* + Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#ifndef TABLE_REPLICATION_APPLIER_CONFIGURATION_H +#define TABLE_REPLICATION_APPLIER_CONFIGURATION_H + +/** + @file storage/perfschema/table_replication_applier_configuration.h + Table replication_applier_configuration (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "rpl_mi.h" +#include "mysql_com.h" +#include "my_thread.h" + +class Master_info; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row in the table*/ +struct st_row_applier_config { + char channel_name[CHANNEL_NAME_LENGTH]; + uint channel_name_length; + time_t desired_delay; + bool desired_delay_is_set; +}; + +/** Table PERFORMANCE_SCHEMA.replication_applier_configuration */ +class table_replication_applier_configuration: public PFS_engine_table +{ + typedef PFS_simple_index pos_t; + +private: + void make_row(Master_info *mi); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + /** Current row */ + st_row_applier_config m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + +protected: + /** + Read the current row values. + @param table Table handle + @param buf row buffer + @param fields Table fields + @param read_all true if all columns are read. + */ + + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_replication_applier_configuration(); + +public: + ~table_replication_applier_configuration(); + + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_replication_applier_status.cc b/storage/perfschema/table_replication_applier_status.cc new file mode 100644 index 00000000..fda3c927 --- /dev/null +++ b/storage/perfschema/table_replication_applier_status.cc @@ -0,0 +1,228 @@ +/* + Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_replication_applier_status.cc + Table replication_applier_status (implementation). +*/ + +//#define HAVE_REPLICATION + +#include "my_global.h" + +#ifdef HAVE_REPLICATION +#include "table_replication_applier_status.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "slave.h" +//#include "rpl_info.h" +#include "rpl_rli.h" +#include "rpl_mi.h" +#include "sql_parse.h" +//#include "rpl_msr.h" /*Multi source replication */ + +THR_LOCK table_replication_applier_status::m_table_lock; + +PFS_engine_table_share_state +table_replication_applier_status::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_replication_applier_status::m_share= +{ + { C_STRING_WITH_LEN("replication_applier_status") }, + &pfs_readonly_acl, + table_replication_applier_status::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_replication_applier_status::get_row_count, /* records */ + sizeof(pos_t), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE replication_applier_status(" + "CHANNEL_NAME VARCHAR(256) collate utf8_general_ci not null comment 'The replication channel name.'," + "SERVICE_STATE ENUM('ON','OFF') not null comment 'Shows ON when the replication channel''s applier threads are active or idle, OFF means that the applier threads are not active.'," + "REMAINING_DELAY INTEGER unsigned comment 'Seconds the replica needs to wait to reach the desired delay from master.'," + "COUNT_TRANSACTIONS_RETRIES BIGINT unsigned not null comment 'The number of retries that were made because the replication SQL thread failed to apply a transaction.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + + +PFS_engine_table* table_replication_applier_status::create(void) +{ + return new table_replication_applier_status(); +} + +table_replication_applier_status::table_replication_applier_status() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +table_replication_applier_status::~table_replication_applier_status() +{} + +void table_replication_applier_status::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +ha_rows table_replication_applier_status::get_row_count() +{ + return master_info_index->master_info_hash.records; +} + + +int table_replication_applier_status::rnd_next(void) +{ + Master_info *mi; + mysql_mutex_lock(&LOCK_active_mi); + + for (m_pos.set_at(&m_next_pos); + m_pos.m_index < master_info_index->master_info_hash.records; + m_pos.next()) + { + mi= (Master_info *)my_hash_element(&master_info_index->master_info_hash, m_pos.m_index); + + if (mi && mi->host[0]) + { + make_row(mi); + m_next_pos.set_after(&m_pos); + mysql_mutex_unlock(&LOCK_active_mi); + return 0; + } + } + + mysql_mutex_unlock(&LOCK_active_mi); + return HA_ERR_END_OF_FILE; +} + + +int table_replication_applier_status::rnd_pos(const void *pos) +{ + Master_info *mi=NULL; + int res= HA_ERR_RECORD_DELETED; + + set_position(pos); + + mysql_mutex_lock(&LOCK_active_mi); + + if ((mi= (Master_info *)my_hash_element(&master_info_index->master_info_hash, m_pos.m_index))) + { + make_row(mi); + res= 0; + } + + mysql_mutex_unlock(&LOCK_active_mi); + return res; +} + +void table_replication_applier_status::make_row(Master_info *mi) +{ + char *slave_sql_running_state= NULL; + + m_row_exists= false; + + DBUG_ASSERT(mi != NULL); + + m_row.channel_name_length= static_cast<uint>(mi->connection_name.length); + memcpy(m_row.channel_name, mi->connection_name.str, m_row.channel_name_length); + + mysql_mutex_lock(&mi->rli.run_lock); + + slave_sql_running_state= const_cast<char *> + (mi->rli.sql_driver_thd ? + mi->rli.sql_driver_thd->get_proc_info() : ""); + mysql_mutex_unlock(&mi->rli.run_lock); + + mysql_mutex_lock(&mi->data_lock); + mysql_mutex_lock(&mi->rli.data_lock); + + if (mi->rli.slave_running) + m_row.service_state= PS_RPL_YES; + else + m_row.service_state= PS_RPL_NO; + + m_row.remaining_delay= 0; + if (slave_sql_running_state == Relay_log_info::state_delaying_string) + { + time_t t= my_time(0), sql_delay_end= mi->rli.get_sql_delay_end(); + m_row.remaining_delay= (uint)(t < sql_delay_end ? + sql_delay_end - t : 0); + m_row.remaining_delay_is_set= true; + } + else + m_row.remaining_delay_is_set= false; + + m_row.count_transactions_retries= mi->rli.retried_trans; + + mysql_mutex_unlock(&mi->rli.data_lock); + mysql_mutex_unlock(&mi->data_lock); + + m_row_exists= true; +} + +int table_replication_applier_status::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /**channel_name*/ + set_field_varchar_utf8(f, m_row.channel_name, m_row.channel_name_length); + break; + case 1: /* service_state */ + set_field_enum(f, m_row.service_state); + break; + case 2: /* remaining_delay */ + if (m_row.remaining_delay_is_set) + set_field_ulong(f, m_row.remaining_delay); + else + f->set_null(); + break; + case 3: /* total number of times transactions were retried */ + set_field_ulonglong(f, m_row.count_transactions_retries); + break; + default: + assert(false); + } + } + } + return 0; +} +#endif diff --git a/storage/perfschema/table_replication_applier_status.h b/storage/perfschema/table_replication_applier_status.h new file mode 100644 index 00000000..4da2087a --- /dev/null +++ b/storage/perfschema/table_replication_applier_status.h @@ -0,0 +1,117 @@ +/* + Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#ifndef TABLE_REPLICATION_APPLIER_STATUS_H +#define TABLE_REPLICATION_APPLIER_STATUS_H + +/** + @file storage/perfschema/table_replication_applier_status.h + Table replication_applier_status (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "rpl_mi.h" +#include "mysql_com.h" +#include "my_thread.h" + +class Master_info; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +#ifndef ENUM_RPL_YES_NO +#define ENUM_RPL_YES_NO +/** enum values for Service_State field*/ +enum enum_rpl_yes_no { + PS_RPL_YES= 1, + PS_RPL_NO +}; +#endif + +/** A row in the table. */ +struct st_row_applier_status { + char channel_name[CHANNEL_NAME_LENGTH]; + uint channel_name_length; + enum_rpl_yes_no service_state; + uint remaining_delay; + bool remaining_delay_is_set; + ulong count_transactions_retries; +}; + +/** Table PERFORMANCE_SCHEMA.replication_applier_status */ +class table_replication_applier_status: public PFS_engine_table +{ + typedef PFS_simple_index pos_t; + +private: + void make_row(Master_info *mi); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + /** Current row */ + st_row_applier_status m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + +protected: + /** + Read the current row values. + @param table Table handle + @param buf row buffer + @param fields Table fields + @param read_all true if all columns are read. + */ + + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_replication_applier_status(); + +public: + ~table_replication_applier_status(); + + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_replication_applier_status_by_coordinator.cc b/storage/perfschema/table_replication_applier_status_by_coordinator.cc new file mode 100644 index 00000000..02419fd3 --- /dev/null +++ b/storage/perfschema/table_replication_applier_status_by_coordinator.cc @@ -0,0 +1,276 @@ +/* + Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_replication_applier_status_by_cordinator.cc + Table replication_applier_status_by_coordinator (implementation). +*/ + +//#define HAVE_REPLICATION + +#include "my_global.h" + +#ifdef HAVE_REPLICATION +#include "table_replication_applier_status_by_coordinator.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "slave.h" +//#include "rpl_info.h" +#include "rpl_rli.h" +#include "rpl_mi.h" +#include "sql_parse.h" +//#include "rpl_msr.h" /* Multisource replication */ + +THR_LOCK table_replication_applier_status_by_coordinator::m_table_lock; + +PFS_engine_table_share_state +table_replication_applier_status_by_coordinator::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_replication_applier_status_by_coordinator::m_share= +{ + { C_STRING_WITH_LEN("replication_applier_status_by_coordinator") }, + &pfs_readonly_acl, + table_replication_applier_status_by_coordinator::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_replication_applier_status_by_coordinator::get_row_count, + sizeof(pos_t), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE replication_applier_status_by_coordinator(" + "CHANNEL_NAME VARCHAR(256) collate utf8_general_ci not null comment 'Replication channel name.'," + "THREAD_ID BIGINT UNSIGNED comment 'The SQL/coordinator thread ID.'," + "SERVICE_STATE ENUM('ON','OFF') not null comment 'ON (thread exists and is active or idle) or OFF (thread no longer exists).'," + "LAST_ERROR_NUMBER INTEGER not null comment 'Last error number that caused the SQL/coordinator thread to stop.'," + "LAST_ERROR_MESSAGE VARCHAR(1024) not null comment 'Last error message that caused the SQL/coordinator thread to stop.'," + "LAST_ERROR_TIMESTAMP TIMESTAMP(0) not null comment 'Timestamp that shows when the most recent SQL/coordinator error occurred.'," + "LAST_SEEN_TRANSACTION CHAR(57) not null comment 'The transaction the worker has last seen.'," + "LAST_TRANS_RETRY_COUNT INTEGER not null comment 'Total number of retries attempted by last transaction.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_replication_applier_status_by_coordinator::create(void) +{ + return new table_replication_applier_status_by_coordinator(); +} + +table_replication_applier_status_by_coordinator + ::table_replication_applier_status_by_coordinator() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +table_replication_applier_status_by_coordinator + ::~table_replication_applier_status_by_coordinator() +{} + +void table_replication_applier_status_by_coordinator::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +ha_rows table_replication_applier_status_by_coordinator::get_row_count() +{ + return master_info_index->master_info_hash.records; +} + + +int table_replication_applier_status_by_coordinator::rnd_next(void) +{ + Master_info *mi; + + mysql_mutex_lock(&LOCK_active_mi); + + + for (m_pos.set_at(&m_next_pos); + m_pos.m_index < master_info_index->master_info_hash.records; + m_pos.next()) + { + mi= (Master_info *)my_hash_element(&master_info_index->master_info_hash, m_pos.m_index); + + if (mi && mi->host[0]) + { + make_row(mi); + m_next_pos.set_after(&m_pos); + mysql_mutex_unlock(&LOCK_active_mi); + return 0; + } + } + + mysql_mutex_unlock(&LOCK_active_mi); + return HA_ERR_END_OF_FILE; +} + +int table_replication_applier_status_by_coordinator::rnd_pos(const void *pos) +{ + Master_info *mi=NULL; + int res= HA_ERR_RECORD_DELETED; + + set_position(pos); + + mysql_mutex_lock(&LOCK_active_mi); + + if ((mi= (Master_info *)my_hash_element(&master_info_index->master_info_hash, m_pos.m_index))) + { + make_row(mi); + res= 0; + } + + mysql_mutex_unlock(&LOCK_active_mi); + return res; +} + +void table_replication_applier_status_by_coordinator::make_row(Master_info *mi) +{ + m_row_exists= false; + rpl_gtid gtid; + StringBuffer<10+1+10+1+20+1> str; + bool first= true; + + DBUG_ASSERT(mi != NULL); + + mysql_mutex_lock(&mi->rli.data_lock); + + gtid= mi->rli.last_seen_gtid; + m_row.channel_name_length= static_cast<uint>(mi->connection_name.length); + memcpy(m_row.channel_name, mi->connection_name.str, m_row.channel_name_length); + + if (mi->rli.slave_running) + { + PSI_thread *psi= thd_get_psi(mi->rli.sql_driver_thd); + PFS_thread *pfs= reinterpret_cast<PFS_thread *> (psi); + if(pfs) + { + m_row.thread_id= pfs->m_thread_internal_id; + m_row.thread_id_is_null= false; + } + else + m_row.thread_id_is_null= true; + } + else + m_row.thread_id_is_null= true; + + if (mi->rli.slave_running) + m_row.service_state= PS_RPL_YES; + else + m_row.service_state= PS_RPL_NO; + + if ((gtid.seq_no > 0 && + !rpl_slave_state_tostring_helper(&str, >id, &first))) + { + strmake(m_row.last_seen_transaction,str.ptr(), str.length()); + m_row.last_seen_transaction_length= str.length(); + } + else + { + m_row.last_seen_transaction_length= 0; + memcpy(m_row.last_seen_transaction, "", 1); + } + + mysql_mutex_lock(&mi->rli.err_lock); + + m_row.last_error_number= (long int) mi->rli.last_error().number; + m_row.last_error_message_length= 0; + m_row.last_error_timestamp= 0; + + /** if error, set error message and timestamp */ + if (m_row.last_error_number) + { + char *temp_store= (char*) mi->rli.last_error().message; + m_row.last_error_message_length= static_cast<uint>(strlen(temp_store)); + memcpy(m_row.last_error_message, temp_store, + m_row.last_error_message_length); + + /** time in millisecond since epoch */ + m_row.last_error_timestamp= (ulonglong)mi->rli.last_error().skr*1000000; + } + + mysql_mutex_unlock(&mi->rli.err_lock); + m_row.last_trans_retry_count= (ulong)mi->rli.last_trans_retry_count; + mysql_mutex_unlock(&mi->rli.data_lock); + + m_row_exists= true; +} + +int table_replication_applier_status_by_coordinator + ::read_row_values(TABLE *table, unsigned char *buf, + Field **fields, bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* channel_name */ + set_field_varchar_utf8(f, m_row.channel_name, + m_row.channel_name_length); + break; + case 1: /*thread_id*/ + if (!m_row.thread_id_is_null) + set_field_ulonglong(f, m_row.thread_id); + else + f->set_null(); + break; + case 2: /*service_state*/ + set_field_enum(f, m_row.service_state); + break; + case 3: /*last_error_number*/ + set_field_ulong(f, m_row.last_error_number); + break; + case 4: /*last_error_message*/ + set_field_varchar_utf8(f, m_row.last_error_message, + m_row.last_error_message_length); + break; + case 5: /*last_error_timestamp*/ + set_field_timestamp(f, m_row.last_error_timestamp); + break; + case 6: /*last_seen_transaction*/ + set_field_char_utf8(f, m_row.last_seen_transaction, + m_row.last_seen_transaction_length); + break; + case 7: /*last_trans_retry_count*/ + set_field_ulong(f, m_row.last_trans_retry_count); + break; + + default: + assert(false); + } + } + } + return 0; +} +#endif diff --git a/storage/perfschema/table_replication_applier_status_by_coordinator.h b/storage/perfschema/table_replication_applier_status_by_coordinator.h new file mode 100644 index 00000000..991e46af --- /dev/null +++ b/storage/perfschema/table_replication_applier_status_by_coordinator.h @@ -0,0 +1,126 @@ +/* + Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#ifndef TABLE_REPLICATION_APPLIER_STATUS_BY_COORDINATOR_H +#define TABLE_REPLICATION_APPLIER_STATUS_BY_COORDINATOR_H + +/** + @file storage/perfschema/table_replication_applier_applier_by_coordinator.h + Table replication_applier_status_by_coordinator(declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "rpl_mi.h" +#include "mysql_com.h" +#include "my_thread.h" + +class Master_info; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +#ifndef ENUM_RPL_YES_NO +#define ENUM_RPL_YES_NO +/** enum values for Service_State of coordinator thread */ +enum enum_rpl_yes_no { + PS_RPL_YES= 1, /* Service_State= on */ + PS_RPL_NO /* Service_State= off */ +}; +#endif + +/* + A row in coordinator's table. The fields with string values have an + additional length field denoted by <field_name>_length. +*/ +struct st_row_coordinator { + char channel_name[CHANNEL_NAME_LENGTH]; + uint channel_name_length; + ulonglong thread_id; + bool thread_id_is_null; + enum_rpl_yes_no service_state; + uint last_error_number; + char last_error_message[MAX_SLAVE_ERRMSG]; + uint last_error_message_length; + ulonglong last_error_timestamp; + char last_seen_transaction[GTID_MAX_STR_LENGTH + 1]; + uint last_seen_transaction_length; + ulong last_trans_retry_count; +}; + +/** Table PERFORMANCE_SCHEMA.replication_applier_status_by_coordinator */ +class table_replication_applier_status_by_coordinator: public PFS_engine_table +{ + typedef PFS_simple_index pos_t; + +private: + void make_row(Master_info *mi); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + /** Current row */ + st_row_coordinator m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + +protected: + /** + Read the current row values. + @param table Table handle + @param buf row buffer + @param fields Table fields + @param read_all true if all columns are read. + */ + + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_replication_applier_status_by_coordinator(); + +public: + ~table_replication_applier_status_by_coordinator(); + + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_replication_applier_status_by_worker.cc b/storage/perfschema/table_replication_applier_status_by_worker.cc new file mode 100644 index 00000000..94aa305a --- /dev/null +++ b/storage/perfschema/table_replication_applier_status_by_worker.cc @@ -0,0 +1,289 @@ +/* + Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_replication_applier_status_by_worker.cc + Table replication_applier_status_by_worker (implementation). +*/ + +//#define HAVE_REPLICATION + +#include "my_global.h" +#include "table_replication_applier_status_by_worker.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "slave.h" +#include "rpl_rli.h" +#include "rpl_mi.h" +#include "sql_parse.h" +#include "rpl_parallel.h" + +THR_LOCK table_replication_applier_status_by_worker::m_table_lock; + +PFS_engine_table_share_state +table_replication_applier_status_by_worker::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_replication_applier_status_by_worker::m_share= +{ + { C_STRING_WITH_LEN("replication_applier_status_by_worker") }, + &pfs_readonly_acl, + table_replication_applier_status_by_worker::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_replication_applier_status_by_worker::get_row_count, /*records*/ + sizeof(pos_t), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE replication_applier_status_by_worker(" + "CHANNEL_NAME VARCHAR(256) collate utf8_general_ci not null comment 'Name of replication channel through which the transaction is received.'," + "THREAD_ID BIGINT UNSIGNED comment 'Thread_Id as displayed in the performance_schema.threads table for thread with name ''thread/sql/rpl_parallel_thread''. THREAD_ID will be NULL when worker threads are stopped due to error/force stop.'," + "SERVICE_STATE ENUM('ON','OFF') not null comment 'Whether or not the thread is running.'," + "LAST_SEEN_TRANSACTION CHAR(57) not null comment 'Last GTID executed by worker'," + "LAST_ERROR_NUMBER INTEGER not null comment 'Last Error that occurred on a particular worker.'," + "LAST_ERROR_MESSAGE VARCHAR(1024) not null comment 'Last error specific message.'," + "LAST_ERROR_TIMESTAMP TIMESTAMP(0) not null comment 'Time stamp of last error.'," + "WORKER_IDLE_TIME BIGINT UNSIGNED not null comment 'Total idle time in seconds that the worker thread has spent waiting for work from SQL thread.'," + "LAST_TRANS_RETRY_COUNT INTEGER not null comment 'Total number of retries attempted by last transaction.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_replication_applier_status_by_worker::create(void) +{ + return new table_replication_applier_status_by_worker(); +} + +table_replication_applier_status_by_worker + ::table_replication_applier_status_by_worker() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +table_replication_applier_status_by_worker + ::~table_replication_applier_status_by_worker() +{} + +void table_replication_applier_status_by_worker::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +ha_rows table_replication_applier_status_by_worker::get_row_count() +{ + return opt_slave_parallel_threads; +} + +int table_replication_applier_status_by_worker::rnd_next(void) +{ + rpl_parallel_thread_pool *pool= &global_rpl_thread_pool; + struct pool_bkp_for_pfs *bkp_pool= &pool->pfs_bkp; + mysql_mutex_lock(&pool->LOCK_rpl_thread_pool); + if (bkp_pool->inited && bkp_pool->count && bkp_pool->is_valid) + { + for (m_pos.set_at(&m_next_pos); + m_pos.has_more_workers(bkp_pool->count); + m_pos.next_worker()) + { + rpl_parallel_thread *rpt= bkp_pool->rpl_thread_arr[m_pos.m_index]; + make_row(rpt); + m_next_pos.set_after(&m_pos); + mysql_mutex_unlock(&pool->LOCK_rpl_thread_pool); + return 0; + } + } + else + { + if (pool->inited && pool->count) + { + uint worker_count= pool->count; + for (m_pos.set_at(&m_next_pos); + m_pos.has_more_workers(worker_count); + m_pos.next_worker()) + { + rpl_parallel_thread *rpt= pool->threads[m_pos.m_index]; + make_row(rpt); + m_next_pos.set_after(&m_pos); + mysql_mutex_unlock(&pool->LOCK_rpl_thread_pool); + return 0; + } + } + } + mysql_mutex_unlock(&pool->LOCK_rpl_thread_pool); + return HA_ERR_END_OF_FILE; +} + +int table_replication_applier_status_by_worker::rnd_pos(const void *pos) +{ + int res= HA_ERR_RECORD_DELETED; + rpl_parallel_thread_pool *pool= &global_rpl_thread_pool; + struct pool_bkp_for_pfs *bkp_pool= &pool->pfs_bkp; + + set_position(pos); + mysql_mutex_lock(&pool->LOCK_rpl_thread_pool); + if (bkp_pool->inited && bkp_pool->count && bkp_pool->is_valid + && m_pos.m_index < bkp_pool->count) + { + rpl_parallel_thread *rpt= bkp_pool->rpl_thread_arr[m_pos.m_index]; + make_row(rpt); + res= 0; + } + else + { + if (pool->inited && pool->count && m_pos.m_index < pool->count) + { + rpl_parallel_thread *rpt= pool->threads[m_pos.m_index]; + make_row(rpt); + res= 0; + } + } + mysql_mutex_unlock(&pool->LOCK_rpl_thread_pool); + return res; +} + +/** + Function to display slave worker thread specific information + + @param[in] rpl_parallel_thread + + @retval void +*/ +void table_replication_applier_status_by_worker::make_row(rpl_parallel_thread *rpt) +{ + char buf[10+1+10+1+20+1]; + String str(buf, sizeof(buf), system_charset_info); + bool first= true; + + str.length(0); + rpl_gtid gtid= rpt->last_seen_gtid; + + m_row_exists= false; + + m_row.channel_name_length= rpt->channel_name_length; + if (m_row.channel_name_length) + memcpy(m_row.channel_name, rpt->channel_name, m_row.channel_name_length); + + m_row.thread_id_is_null= true; + if (rpt->running) + { + PSI_thread *psi= thd_get_psi(rpt->thd); + PFS_thread *pfs= reinterpret_cast<PFS_thread *> (psi); + if(pfs) + { + m_row.thread_id= pfs->m_thread_internal_id; + m_row.thread_id_is_null= false; + } + } + + if ((gtid.seq_no > 0 && + !rpl_slave_state_tostring_helper(&str, >id, &first))) + { + strmake(m_row.last_seen_transaction,str.ptr(), str.length()); + m_row.last_seen_transaction_length= str.length(); + } + else + { + m_row.last_seen_transaction_length= 0; + memcpy(m_row.last_seen_transaction, "", 1); + } + + if (rpt->running) + m_row.service_state= PS_RPL_YES; + else + m_row.service_state= PS_RPL_NO; + m_row.last_error_number= rpt->last_error_number; + m_row.last_error_message_length= 0; + m_row.last_error_timestamp= 0; + if (m_row.last_error_number) + { + char* temp_store= (char*)rpt->last_error_message; + m_row.last_error_message_length= (uint)strlen(temp_store); + strmake(m_row.last_error_message, rpt->last_error_message, + m_row.last_error_message_length); + /** time in millisecond since epoch */ + m_row.last_error_timestamp= rpt->last_error_timestamp; + } + + m_row.last_trans_retry_count= rpt->last_trans_retry_count; + m_row.worker_idle_time= rpt->get_worker_idle_time(); + m_row_exists= true; +} + +int table_replication_applier_status_by_worker + ::read_row_values(TABLE *table, unsigned char *buf, Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /*channel_name*/ + set_field_varchar_utf8(f, m_row.channel_name, m_row.channel_name_length); + break; + case 1: /*thread_id*/ + if(m_row.thread_id_is_null) + f->set_null(); + else + set_field_ulonglong(f, m_row.thread_id); + break; + case 2: /*service_state*/ + set_field_enum(f, m_row.service_state); + break; + case 3: /*last_seen_transaction*/ + set_field_char_utf8(f, m_row.last_seen_transaction, m_row.last_seen_transaction_length); + break; + case 4: /*last_error_number*/ + set_field_ulong(f, m_row.last_error_number); + break; + case 5: /*last_error_message*/ + set_field_varchar_utf8(f, m_row.last_error_message, m_row.last_error_message_length); + break; + case 6: /*last_error_timestamp*/ + set_field_timestamp(f, m_row.last_error_timestamp); + break; + case 7: /*worker_idle_time*/ + set_field_ulonglong(f, m_row.worker_idle_time); + break; + case 8: /*last_trans_retry_count*/ + set_field_ulong(f, m_row.last_trans_retry_count); + break; + default: + assert(false); + } + } + } + return 0; +} diff --git a/storage/perfschema/table_replication_applier_status_by_worker.h b/storage/perfschema/table_replication_applier_status_by_worker.h new file mode 100644 index 00000000..2e400783 --- /dev/null +++ b/storage/perfschema/table_replication_applier_status_by_worker.h @@ -0,0 +1,155 @@ +/* + Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#ifndef TABLE_REPLICATION_APPLIER_STATUS_BY_WORKER_H +#define TABLE_REPLICATION_APPLIER_STATUS_BY_WORKER_H + +/** + @file storage/perfschema/table_replication_applier_status_by_worker.h + Table replication_applier_status_by_worker (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "rpl_mi.h" +#include "mysql_com.h" +#include "my_thread.h" +#include "rpl_parallel.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +#ifndef ENUM_RPL_YES_NO +#define ENUM_RPL_YES_NO +/** enumerated values for service_state of worker thread*/ +enum enum_rpl_yes_no { + PS_RPL_YES= 1, /* service_state= on */ + PS_RPL_NO /* service_state= off */ +}; +#endif + +/* + A row in worker's table. The fields with string values have an additional + length field denoted by <field_name>_length. +*/ +struct st_row_worker { + + char channel_name[CHANNEL_NAME_LENGTH]; + uint channel_name_length; + ulonglong thread_id; + uint thread_id_is_null; + enum_rpl_yes_no service_state; + char last_seen_transaction[GTID_MAX_STR_LENGTH + 1]; + uint last_seen_transaction_length; + uint last_error_number; + char last_error_message[MAX_SLAVE_ERRMSG]; + uint last_error_message_length; + ulonglong last_error_timestamp; + ulonglong worker_idle_time; + ulong last_trans_retry_count; +}; + +/** + Position in table replication_applier_status_by_worker. + We have global replication thread pool. +*/ +struct pos_replication_applier_status_by_worker : public PFS_simple_index +{ + + pos_replication_applier_status_by_worker() : PFS_simple_index(0) + {} + + inline void reset(void) + { + m_index= 0; + } + + inline bool has_more_workers(uint num) + { return (m_index < num); } + + inline void next_worker(void) + { + m_index++; + } +}; + + +/** Table PERFORMANCE_SCHEMA.replication_applier_status_by_worker */ +class table_replication_applier_status_by_worker: public PFS_engine_table +{ + typedef pos_replication_applier_status_by_worker pos_t; + +private: + /* + Master_info to construct a row to display SQL Thread's status + information in STS mode + */ + void make_row(rpl_parallel_thread *); + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + /** current row*/ + st_row_worker m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + +protected: + /** + Read the current row values. + @param table Table handle + @param buf row buffer + @param fields Table fields + @param read_all true if all columns are read. + */ + + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_replication_applier_status_by_worker(); + +public: + ~table_replication_applier_status_by_worker(); + + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_replication_connection_configuration.cc b/storage/perfschema/table_replication_connection_configuration.cc new file mode 100644 index 00000000..81135074 --- /dev/null +++ b/storage/perfschema/table_replication_connection_configuration.cc @@ -0,0 +1,392 @@ +/* + Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_replication_connection_configuration.cc + Table replication_connection_configuration (implementation). +*/ + +//#define HAVE_REPLICATION + +#include "my_global.h" +#include "table_replication_connection_configuration.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "slave.h" +//#include "rpl_info.h" +#include "rpl_rli.h" +#include "rpl_mi.h" +#include "sql_parse.h" +//#include "rpl_msr.h" /* Multisource replciation */ + +#ifdef HAVE_REPLICATION +THR_LOCK table_replication_connection_configuration::m_table_lock; + +PFS_engine_table_share_state +table_replication_connection_configuration::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_replication_connection_configuration::m_share= +{ + { C_STRING_WITH_LEN("replication_connection_configuration") }, + &pfs_readonly_acl, + table_replication_connection_configuration::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_replication_connection_configuration::get_row_count, /* records */ + sizeof(pos_t), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE replication_connection_configuration(" + "CHANNEL_NAME VARCHAR(256) collate utf8_general_ci not null comment 'The replication channel used.'," + "HOST CHAR(60) collate utf8_bin not null comment 'The host name of the source that the replica is connected to.'," + "PORT INTEGER not null comment 'The port used to connect to the source.'," + "USER CHAR(32) collate utf8_bin not null comment 'The user name of the replication user account used to connect to the source.'," + "USING_GTID ENUM('NO','CURRENT_POS','SLAVE_POS') not null comment 'Whether replication is using GTIDs or not'," + "SSL_ALLOWED ENUM('YES','NO','IGNORED') not null comment 'Whether SSL is allowed for the replica connection.'," + "SSL_CA_FILE VARCHAR(512) not null comment 'Path to the file that contains one or more certificates for trusted Certificate Authorities (CA) to use for TLS.'," + "SSL_CA_PATH VARCHAR(512) not null comment 'Path to a directory that contains one or more PEM files that contain X509 certificates for a trusted Certificate Authority (CA) to use for TLS.'," + "SSL_CERTIFICATE VARCHAR(512) not null comment 'Path to the certificate used to authenticate the master.'," + "SSL_CIPHER VARCHAR(512) not null comment 'Which cipher is used for encription.'," + "SSL_KEY VARCHAR(512) not null comment 'Path to the private key used for TLS.'," + "SSL_VERIFY_SERVER_CERTIFICATE ENUM('YES','NO') not null comment 'Whether the server certificate is verified as part of the SSL connection'," + "SSL_CRL_FILE VARCHAR(255) not null comment 'Path to the PEM file containing one or more revoked X.509 certificates.'," + "SSL_CRL_PATH VARCHAR(255) not null comment 'PATH to a folder containing PEM files containing one or more revoked X.509 certificates.'," + "CONNECTION_RETRY_INTERVAL INTEGER not null comment 'The number of seconds between connect retries.'," + "CONNECTION_RETRY_COUNT BIGINT unsigned not null comment 'The number of times the replica can attempt to reconnect to the source in the event of a lost connection.'," + "HEARTBEAT_INTERVAL DOUBLE(10,3) unsigned not null COMMENT 'Number of seconds after which a heartbeat will be sent.'," + "IGNORE_SERVER_IDS LONGTEXT not null comment 'Binary log events from servers (ids) to ignore.'," + "REPL_DO_DOMAIN_IDS LONGTEXT not null comment 'Only apply binary logs from these domain ids.'," + "REPL_IGNORE_DOMAIN_IDS LONGTEXT not null comment 'Binary log events from domains to ignore.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +static char *convert_array_to_str(DYNAMIC_ARRAY *ids) +{ + char *buf; + size_t sz, cur_len= 0; + + sz= (sizeof(ulong) * 3 + 1) * (1 + ids->elements); + + if (!(buf= (char *) my_malloc(PSI_INSTRUMENT_ME, sz, MYF(MY_WME)))) + return NULL; + buf[0]= 0; + + for (uint i= 0; i < ids->elements; i++) + { + ulong domain_id; + get_dynamic(ids, (void *) &domain_id, i); + cur_len+= my_snprintf(buf + cur_len, sz, (i == 0 ? "%lu" : ", %lu"), domain_id); + sz-= cur_len; + } + return buf; +} + +PFS_engine_table* table_replication_connection_configuration::create(void) +{ + return new table_replication_connection_configuration(); +} + +table_replication_connection_configuration + ::table_replication_connection_configuration() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +table_replication_connection_configuration + ::~table_replication_connection_configuration() +{} + +void table_replication_connection_configuration::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +ha_rows table_replication_connection_configuration::get_row_count() +{ + /* + We actually give the MAX_CHANNELS rather than the current + number of channels + */ + + return master_info_index->master_info_hash.records; +} + +int table_replication_connection_configuration::rnd_next(void) +{ + Master_info *mi; + + mysql_mutex_lock(&LOCK_active_mi); + + for (m_pos.set_at(&m_next_pos); + m_pos.m_index < master_info_index->master_info_hash.records; + m_pos.next()) + { + mi= (Master_info *)my_hash_element(&master_info_index->master_info_hash, m_pos.m_index); + + if (mi && mi->host[0]) + { + make_row(mi); + m_next_pos.set_after(&m_pos); + mysql_mutex_unlock(&LOCK_active_mi); + return 0; + } + } + + mysql_mutex_unlock(&LOCK_active_mi); + return HA_ERR_END_OF_FILE; +} + +int table_replication_connection_configuration::rnd_pos(const void *pos) +{ + Master_info *mi; + int res= HA_ERR_RECORD_DELETED; + + mysql_mutex_lock(&LOCK_active_mi); + + set_position(pos); + + if ((mi= (Master_info *)my_hash_element(&master_info_index->master_info_hash, m_pos.m_index))) + { + make_row(mi); + res= 0; + } + + mysql_mutex_unlock(&LOCK_active_mi); + return res; +} + +void table_replication_connection_configuration::make_row(Master_info *mi) +{ + DBUG_ENTER("table_replication_connection_configuration::make_row"); + char * temp_store; + bool error= false; + + m_row_exists= false; + + + assert(mi != NULL); + + mysql_mutex_lock(&mi->data_lock); + mysql_mutex_lock(&mi->rli.data_lock); + + m_row.channel_name_length= static_cast<uint>(mi->connection_name.length); + memcpy(m_row.channel_name, mi->connection_name.str, m_row.channel_name_length); + + m_row.host_length= static_cast<uint>(strlen(mi->host)); + memcpy(m_row.host, mi->host, m_row.host_length); + + m_row.port= (unsigned int) mi->port; + + /* can't the user be NULL? */ + temp_store= (char*)mi->user; + m_row.user_length= static_cast<uint>(strlen(temp_store)); + memcpy(m_row.user, temp_store, m_row.user_length); + + if (mi->using_gtid == Master_info::USE_GTID_NO) + m_row.using_gtid= PS_USE_GTID_NO; + else if (mi->using_gtid == Master_info::USE_GTID_CURRENT_POS) + m_row.using_gtid= PS_USE_GTID_CURRENT_POS; + else + m_row.using_gtid= PS_USE_GTID_SLAVE_POS; + +#ifdef HAVE_OPENSSL + m_row.ssl_allowed= mi->ssl? PS_SSL_ALLOWED_YES:PS_SSL_ALLOWED_NO; +#else + m_row.ssl_allowed= mi->ssl? PS_SSL_ALLOWED_IGNORED:PS_SSL_ALLOWED_NO; +#endif + + temp_store= (char*)mi->ssl_ca; + m_row.ssl_ca_file_length= static_cast<uint>(strlen(temp_store)); + memcpy(m_row.ssl_ca_file, temp_store, m_row.ssl_ca_file_length); + + temp_store= (char*)mi->ssl_capath; + m_row.ssl_ca_path_length= static_cast<uint>(strlen(temp_store)); + memcpy(m_row.ssl_ca_path, temp_store, m_row.ssl_ca_path_length); + + temp_store= (char*)mi->ssl_cert; + m_row.ssl_certificate_length= static_cast<uint>(strlen(temp_store)); + memcpy(m_row.ssl_certificate, temp_store, m_row.ssl_certificate_length); + + temp_store= (char*)mi->ssl_cipher; + m_row.ssl_cipher_length= static_cast<uint>(strlen(temp_store)); + memcpy(m_row.ssl_cipher, temp_store, m_row.ssl_cipher_length); + + temp_store= (char*)mi->ssl_key; + m_row.ssl_key_length= static_cast<uint>(strlen(temp_store)); + memcpy(m_row.ssl_key, temp_store, m_row.ssl_key_length); + + if (mi->ssl_verify_server_cert) + m_row.ssl_verify_server_certificate= PS_RPL_YES; + else + m_row.ssl_verify_server_certificate= PS_RPL_NO; + + temp_store= (char*)mi->ssl_crl; + m_row.ssl_crl_file_length= static_cast<uint>(strlen(temp_store)); + memcpy(m_row.ssl_crl_file, temp_store, m_row.ssl_crl_file_length); + + temp_store= (char*)mi->ssl_crlpath; + m_row.ssl_crl_path_length= static_cast<uint>(strlen(temp_store)); + memcpy(m_row.ssl_crl_path, temp_store, m_row.ssl_crl_path_length); + + m_row.connection_retry_interval= (unsigned int) mi->connect_retry; + + m_row.connection_retry_count= master_retry_count; //(ulong) mi->retry_count; + + m_row.heartbeat_interval= (double)mi->heartbeat_period; + + m_row.ignore_server_ids= convert_array_to_str(&mi->ignore_server_ids); + if (m_row.ignore_server_ids == NULL) + { + error= true; + goto end; + } + m_row.ignore_server_ids_length= static_cast<uint>(strlen(m_row.ignore_server_ids)); + + m_row.do_domain_ids_str= + convert_array_to_str(&mi->domain_id_filter.m_domain_ids[Domain_id_filter::DO_DOMAIN_IDS]); + if (m_row.do_domain_ids_str == NULL) + { + error= true; + goto end; + } + m_row.do_domain_ids_str_length= static_cast<uint>(strlen(m_row.do_domain_ids_str)); + + m_row.ignore_domain_ids_str= + convert_array_to_str(&mi->domain_id_filter.m_domain_ids[Domain_id_filter::IGNORE_DOMAIN_IDS]); + if (m_row.ignore_domain_ids_str == NULL) + { + error= true; + goto end; + } + m_row.ignore_domain_ids_str_length= + static_cast<uint>(strlen(m_row.ignore_domain_ids_str)); + +end: + mysql_mutex_unlock(&mi->rli.data_lock); + mysql_mutex_unlock(&mi->data_lock); + if (!error) + m_row_exists= true; + DBUG_VOID_RETURN; +} + +int table_replication_connection_configuration::read_row_values(TABLE *table, + unsigned char *, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /** connection_name */ + set_field_varchar_utf8(f, m_row.channel_name, m_row.channel_name_length); + break; + case 1: /** host */ + set_field_char_utf8(f, m_row.host, m_row.host_length); + break; + case 2: /** port */ + set_field_ulong(f, m_row.port); + break; + case 3: /** user */ + set_field_char_utf8(f, m_row.user, m_row.user_length); + break; + case 4: /** use_gtid */ + set_field_enum(f, m_row.using_gtid); + break; + case 5: /** ssl_allowed */ + set_field_enum(f, m_row. ssl_allowed); + break; + case 6: /**ssl_ca_file */ + set_field_varchar_utf8(f, m_row.ssl_ca_file, + m_row.ssl_ca_file_length); + break; + case 7: /** ssl_ca_path */ + set_field_varchar_utf8(f, m_row.ssl_ca_path, + m_row.ssl_ca_path_length); + break; + case 8: /** ssl_certificate */ + set_field_varchar_utf8(f, m_row.ssl_certificate, + m_row.ssl_certificate_length); + break; + case 9: /** ssl_cipher */ + set_field_varchar_utf8(f, m_row.ssl_cipher, m_row.ssl_cipher_length); + break; + case 10: /** ssl_key */ + set_field_varchar_utf8(f, m_row.ssl_key, m_row.ssl_key_length); + break; + case 11: /** ssl_verify_server_certificate */ + set_field_enum(f, m_row.ssl_verify_server_certificate); + break; + case 12: /** ssl_crl_file */ + set_field_varchar_utf8(f, m_row.ssl_crl_file, + m_row.ssl_crl_file_length); + break; + case 13: /** ssl_crl_path */ + set_field_varchar_utf8(f, m_row.ssl_crl_path, + m_row.ssl_crl_path_length); + break; + case 14: /** connection_retry_interval */ + set_field_ulong(f, m_row.connection_retry_interval); + break; + case 15: /** connect_retry_count */ + set_field_ulonglong(f, m_row.connection_retry_count); + break; + case 16:/** number of seconds after which heartbeat will be sent */ + set_field_double(f, m_row.heartbeat_interval); + break; + case 17: /** ignore_server_ids */ + set_field_longtext_utf8(f, m_row.ignore_server_ids, + m_row.ignore_server_ids_length); + break; + case 18: /** do_domain_ids */ + set_field_longtext_utf8(f, m_row.do_domain_ids_str, + m_row.do_domain_ids_str_length); + break; + case 19: /** ignore_domain_ids */ + set_field_longtext_utf8(f, m_row.ignore_domain_ids_str, + m_row.ignore_domain_ids_str_length); + break; + + default: + assert(false); + } + } + } + m_row.cleanup(); + return 0; +} +#endif diff --git a/storage/perfschema/table_replication_connection_configuration.h b/storage/perfschema/table_replication_connection_configuration.h new file mode 100644 index 00000000..5390c51e --- /dev/null +++ b/storage/perfschema/table_replication_connection_configuration.h @@ -0,0 +1,178 @@ +/* + Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#ifndef TABLE_REPLICATION_CONFIGURATION_H +#define TABLE_REPLICATION_CONFIGURATION_H + +/** + @file storage/perfschema/table_replication_connection_configuration.h + Table replication_connection_configuration (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "rpl_mi.h" +#include "mysql_com.h" +#include "my_thread.h" + +class Master_info; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +#ifndef ENUM_RPL_YES_NO +#define ENUM_RPL_YES_NO +enum enum_rpl_yes_no { + PS_RPL_YES= 1, + PS_RPL_NO +}; +#endif + +/** enum values for SSL_Allowed*/ +enum enum_ssl_allowed { + PS_SSL_ALLOWED_YES= 1, + PS_SSL_ALLOWED_NO, + PS_SSL_ALLOWED_IGNORED +}; +enum enum_using_gtid { + PS_USE_GTID_NO= 1, + PS_USE_GTID_CURRENT_POS, + PS_USE_GTID_SLAVE_POS +}; + + +/** + A row in the table. The fields with string values have an additional + length field denoted by <field_name>_length. +*/ +struct st_row_connect_config { + char channel_name[CHANNEL_NAME_LENGTH]; + uint channel_name_length; + char host[HOSTNAME_LENGTH]; + uint host_length; + uint port; + char user[USERNAME_LENGTH]; + uint user_length; + enum_using_gtid using_gtid; + enum_ssl_allowed ssl_allowed; + char ssl_ca_file[FN_REFLEN]; + uint ssl_ca_file_length; + char ssl_ca_path[FN_REFLEN]; + uint ssl_ca_path_length; + char ssl_certificate[FN_REFLEN]; + uint ssl_certificate_length; + char ssl_cipher[FN_REFLEN]; + uint ssl_cipher_length; + char ssl_key[FN_REFLEN]; + uint ssl_key_length; + enum_rpl_yes_no ssl_verify_server_certificate; + char ssl_crl_file[FN_REFLEN]; + uint ssl_crl_file_length; + char ssl_crl_path[FN_REFLEN]; + uint ssl_crl_path_length; + uint connection_retry_interval; + ulong connection_retry_count; + double heartbeat_interval; + char *ignore_server_ids; + uint ignore_server_ids_length; + char *do_domain_ids_str; + uint do_domain_ids_str_length; + char *ignore_domain_ids_str; + uint ignore_domain_ids_str_length; + void cleanup() + { + if (ignore_server_ids != NULL) + { + my_free(ignore_server_ids); + ignore_server_ids= NULL; + } + if (do_domain_ids_str != NULL) + { + my_free(do_domain_ids_str); + do_domain_ids_str= NULL; + } + if (ignore_domain_ids_str != NULL) + { + my_free(ignore_domain_ids_str); + ignore_domain_ids_str= NULL; + } + } + +}; + +/** Table PERFORMANCE_SCHEMA.TABLE_REPLICATION_CONNECTION_CONFIGURATION. */ +class table_replication_connection_configuration: public PFS_engine_table +{ + typedef PFS_simple_index pos_t; + +private: + void make_row(Master_info *); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + /** True if the current row exists. */ + bool m_row_exists; + /** Current row */ + st_row_connect_config m_row; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + +protected: + /** + Read the current row values. + @param table Table handle + @param buf row buffer + @param fields Table fields + @param read_all true if all columns are read. + */ + + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_replication_connection_configuration(); + +public: + ~table_replication_connection_configuration(); + + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_replication_connection_status.cc b/storage/perfschema/table_replication_connection_status.cc new file mode 100644 index 00000000..315392ef --- /dev/null +++ b/storage/perfschema/table_replication_connection_status.cc @@ -0,0 +1,446 @@ +/* + Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_replication_connection_status.cc + Table replication_connection_status (implementation). +*/ + +//#define HAVE_REPLICATION + +#include "my_global.h" +#include "table_replication_connection_status.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "slave.h" +//#include "rpl_info.h" +#include "rpl_rli.h" +#include "rpl_mi.h" +#include "sql_parse.h" +//#include "rpl_msr.h" /* Multi source replication */ +#include "log.h" +//#include "rpl_group_replication.h" + +/* + Callbacks implementation for GROUP_REPLICATION_CONNECTION_STATUS_CALLBACKS. +*/ +static void set_channel_name(void* const context, const char& value, + size_t length) +{ +} + +static void set_group_name(void* const context, const char& value, + size_t length) +{ + struct st_row_connect_status* row= + static_cast<struct st_row_connect_status*>(context); + const size_t max= UUID_LENGTH; + length= std::min(length, max); + + row->group_name_is_null= false; + memcpy(row->group_name, &value, length); +} + +static void set_source_uuid(void* const context, const char& value, + size_t length) +{ + struct st_row_connect_status* row= + static_cast<struct st_row_connect_status*>(context); + const size_t max= UUID_LENGTH; + length= std::min(length, max); + + row->source_uuid_is_null= false; + memcpy(row->source_uuid, &value, length); +} + +static void set_service_state(void* const context, bool value) +{ + struct st_row_connect_status* row= + static_cast<struct st_row_connect_status*>(context); + + row->service_state= value ? PS_RPL_CONNECT_SERVICE_STATE_YES + : PS_RPL_CONNECT_SERVICE_STATE_NO; +} + + +THR_LOCK table_replication_connection_status::m_table_lock; + + +/* Numbers in varchar count utf8 characters. */ +static const TABLE_FIELD_TYPE field_types[]= +{ + { + {C_STRING_WITH_LEN("CHANNEL_NAME")}, + {C_STRING_WITH_LEN("char(64)")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("GROUP_NAME")}, + {C_STRING_WITH_LEN("char(36)")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("SOURCE_UUID")}, + {C_STRING_WITH_LEN("char(36)")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("THREAD_ID")}, + {C_STRING_WITH_LEN("bigint(20)")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("SERVICE_STATE")}, + {C_STRING_WITH_LEN("enum('ON','OFF','CONNECTING')")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("COUNT_RECEIVED_HEARTBEATS")}, + {C_STRING_WITH_LEN("bigint(20)")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("LAST_HEARTBEAT_TIMESTAMP")}, + {C_STRING_WITH_LEN("timestamp")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("RECEIVED_TRANSACTION_SET")}, + {C_STRING_WITH_LEN("longtext")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("LAST_ERROR_NUMBER")}, + {C_STRING_WITH_LEN("int(11)")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("LAST_ERROR_MESSAGE")}, + {C_STRING_WITH_LEN("varchar(1024)")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("LAST_ERROR_TIMESTAMP")}, + {C_STRING_WITH_LEN("timestamp")}, + {NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_replication_connection_status::m_field_def= { 11, field_types }; + +PFS_engine_table_share_state +table_replication_connection_status::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_replication_connection_status::m_share= +{ + { C_STRING_WITH_LEN("replication_connection_status") }, + &pfs_readonly_acl, + table_replication_connection_status::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_replication_connection_status::get_row_count, /* records */ + sizeof(pos_t), /* ref length */ + &m_table_lock, + &m_field_def, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_replication_connection_status::create(void) +{ + return new table_replication_connection_status(); +} + +table_replication_connection_status::table_replication_connection_status() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{ +} + +table_replication_connection_status::~table_replication_connection_status() +{ +} + +void table_replication_connection_status::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +ha_rows table_replication_connection_status::get_row_count() +{ + /*A lock is not needed for an estimate */ + return channel_map.get_max_channels(); +} + + + +int table_replication_connection_status::rnd_next(void) +{ + Master_info *mi= NULL; + channel_map.rdlock(); + + for (m_pos.set_at(&m_next_pos); + m_pos.m_index < channel_map.get_max_channels(); + m_pos.next()) + { + mi= channel_map.get_mi_at_pos(m_pos.m_index); + + if (mi && mi->host[0]) + { + make_row(mi); + m_next_pos.set_after(&m_pos); + channel_map.unlock(); + return 0; + } + } + + channel_map.unlock(); + return HA_ERR_END_OF_FILE; +} + +int table_replication_connection_status::rnd_pos(const void *pos) +{ + Master_info *mi; + int res= HA_ERR_RECORD_DELETED; + + set_position(pos); + + channel_map.rdlock(); + + if ((mi= channel_map.get_mi_at_pos(m_pos.m_index))) + { + make_row(mi); + res= 0; + } + + channel_map.unlock(); + return res; +} + +void table_replication_connection_status::make_row(Master_info *mi) +{ + DBUG_ENTER("table_replication_connection_status::make_row"); + m_row_exists= false; + bool error= false; + + /* Default values */ + m_row.group_name_is_null= true; + m_row.source_uuid_is_null= true; + m_row.thread_id_is_null= true; + m_row.service_state= PS_RPL_CONNECT_SERVICE_STATE_NO; + + assert(mi != NULL); + assert(mi->rli != NULL); + + mysql_mutex_lock(&mi->data_lock); + mysql_mutex_lock(&mi->rli->data_lock); + + m_row.channel_name_length= mi->get_channel() ? strlen(mi->get_channel()):0; + memcpy(m_row.channel_name, mi->get_channel(), m_row.channel_name_length); + + if (is_group_replication_plugin_loaded() && + channel_map.is_group_replication_channel_name(mi->get_channel(), true)) + { + /* + Group Replication applier channel. + Set callbacks on GROUP_REPLICATION_GROUP_MEMBER_STATS_CALLBACKS. + */ + const GROUP_REPLICATION_CONNECTION_STATUS_CALLBACKS callbacks= + { + &m_row, + &set_channel_name, + &set_group_name, + &set_source_uuid, + &set_service_state, + }; + + // Query plugin and let callbacks do their job. + if (get_group_replication_connection_status_info(callbacks)) + { + DBUG_PRINT("info", ("Group Replication stats not available!")); + } + } + else + { + /* Slave channel. */ + if (mi->master_uuid[0] != 0) + { + memcpy(m_row.source_uuid, mi->master_uuid, UUID_LENGTH); + m_row.source_uuid_is_null= false; + } + + if (mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) + m_row.service_state= PS_RPL_CONNECT_SERVICE_STATE_YES; + else + { + if (mi->slave_running == MYSQL_SLAVE_RUN_NOT_CONNECT) + m_row.service_state= PS_RPL_CONNECT_SERVICE_STATE_CONNECTING; + else + m_row.service_state= PS_RPL_CONNECT_SERVICE_STATE_NO; + } + } + + if (mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) + { + PSI_thread *psi= thd_get_psi(mi->info_thd); + PFS_thread *pfs= reinterpret_cast<PFS_thread *> (psi); + if(pfs) + { + m_row.thread_id= pfs->m_thread_internal_id; + m_row.thread_id_is_null= false; + } + } + + m_row.count_received_heartbeats= mi->received_heartbeats; + /* + Time in Milliseconds since epoch. active_mi->last_heartbeat contains + number of seconds so we multiply by 1000000. + */ + m_row.last_heartbeat_timestamp= (ulonglong)mi->last_heartbeat*1000000; + + { + global_sid_lock->wrlock(); + const Gtid_set* io_gtid_set= mi->rli->get_gtid_set(); + + if ((m_row.received_transaction_set_length= + io_gtid_set->to_string(&m_row.received_transaction_set)) < 0) + { + my_free(m_row.received_transaction_set); + m_row.received_transaction_set_length= 0; + global_sid_lock->unlock(); + error= true; + goto end; + } + global_sid_lock->unlock(); + } + + /* Errors */ + mysql_mutex_lock(&mi->err_lock); + mysql_mutex_lock(&mi->rli->err_lock); + m_row.last_error_number= (unsigned int) mi->last_error().number; + m_row.last_error_message_length= 0; + m_row.last_error_timestamp= 0; + + /** If error, set error message and timestamp */ + if (m_row.last_error_number) + { + char* temp_store= (char*)mi->last_error().message; + m_row.last_error_message_length= strlen(temp_store); + memcpy(m_row.last_error_message, temp_store, + m_row.last_error_message_length); + + /* + Time in millisecond since epoch. active_mi->last_error().skr contains + number of seconds so we multiply by 1000000. */ + m_row.last_error_timestamp= (ulonglong)mi->last_error().skr*1000000; + } + mysql_mutex_unlock(&mi->rli->err_lock); + mysql_mutex_unlock(&mi->err_lock); + +end: + mysql_mutex_unlock(&mi->rli->data_lock); + mysql_mutex_unlock(&mi->data_lock); + + if (!error) + m_row_exists= true; + DBUG_VOID_RETURN; +} + +int table_replication_connection_status::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /** channel_name*/ + set_field_char_utf8(f, m_row.channel_name,m_row.channel_name_length); + break; + case 1: /** group_name */ + if (m_row.group_name_is_null) + f->set_null(); + else + set_field_char_utf8(f, m_row.group_name, UUID_LENGTH); + break; + case 2: /** source_uuid */ + if (m_row.source_uuid_is_null) + f->set_null(); + else + set_field_char_utf8(f, m_row.source_uuid, UUID_LENGTH); + break; + case 3: /** thread_id */ + if(m_row.thread_id_is_null) + f->set_null(); + else + set_field_ulonglong(f, m_row.thread_id); + break; + case 4: /** service_state */ + set_field_enum(f, m_row.service_state); + break; + case 5: /** number of heartbeat events received **/ + set_field_ulonglong(f, m_row.count_received_heartbeats); + break; + case 6: /** time of receipt of last heartbeat event **/ + set_field_timestamp(f, m_row.last_heartbeat_timestamp); + break; + case 7: /** received_transaction_set */ + set_field_longtext_utf8(f, m_row.received_transaction_set, + m_row.received_transaction_set_length); + break; + case 8: /*last_error_number*/ + set_field_ulong(f, m_row.last_error_number); + break; + case 9: /*last_error_message*/ + set_field_varchar_utf8(f, m_row.last_error_message, + m_row.last_error_message_length); + break; + case 10: /*last_error_timestamp*/ + set_field_timestamp(f, m_row.last_error_timestamp); + break; + default: + assert(false); + } + } + } + m_row.cleanup(); + + return 0; +} diff --git a/storage/perfschema/table_replication_connection_status.h b/storage/perfschema/table_replication_connection_status.h new file mode 100644 index 00000000..57cc9a94 --- /dev/null +++ b/storage/perfschema/table_replication_connection_status.h @@ -0,0 +1,151 @@ +/* + Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#ifndef TABLE_REPLICATION_CONNECTION_STATUS_H +#define TABLE_REPLICATION_CONNECTION_STATUS_H + +/** + @file storage/perfschema/table_replication_connection_status.h + Table replication_connection_status (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "rpl_mi.h" +#include "rpl_reporting.h" /* MAX_SLAVE_ERRMSG */ +#include "mysql_com.h" +//#include "rpl_msr.h" +//#include "rpl_info.h" /*CHANNEL_NAME_LENGTH */ +#include "my_thread.h" + +class Master_info; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +#ifndef ENUM_RPL_YES_NO +#define ENUM_RPL_YES_NO +enum enum_rpl_yes_no { + PS_RPL_YES= 1, + PS_RPL_NO +}; +#endif + +enum enum_rpl_connect_status_service_state { + PS_RPL_CONNECT_SERVICE_STATE_YES= 1, + PS_RPL_CONNECT_SERVICE_STATE_NO, + PS_RPL_CONNECT_SERVICE_STATE_CONNECTING +}; + +/* + A row in the table. The fields with string values have an additional + length field denoted by <field_name>_length. +*/ +struct st_row_connect_status { + char group_name[NAME_LEN]; + bool group_name_is_null; + char channel_name[CHANNEL_NAME_LENGTH]; + uint channel_name_length; + char source_uuid[11]; // typeof(server_id) == uint32 + bool source_uuid_is_null; + ulonglong thread_id; + bool thread_id_is_null; + enum_rpl_connect_status_service_state service_state; + ulonglong count_received_heartbeats; + ulonglong last_heartbeat_timestamp; + char* received_transaction_set; + int received_transaction_set_length; + uint last_error_number; + char last_error_message[MAX_SLAVE_ERRMSG]; + uint last_error_message_length; + ulonglong last_error_timestamp; + + st_row_connect_status() : received_transaction_set(NULL) {} + + void cleanup() + { + if (received_transaction_set != NULL) + { + my_free(received_transaction_set); + received_transaction_set= NULL; + } + } +}; + + +/** Table PERFORMANCE_SCHEMA.REPLICATION_CONNECTION_STATUS. */ +class table_replication_connection_status: public PFS_engine_table +{ + typedef PFS_simple_index pos_t; + +private: + void make_row(Master_info *mi); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + /** True if the current row exists. */ + bool m_row_exists; + /** Current row */ + st_row_connect_status m_row; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + +protected: + /** + Read the current row values. + @param table Table handle + @param buf row buffer + @param fields Table fields + @param read_all true if all columns are read. + */ + + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_replication_connection_status(); + +public: + ~table_replication_connection_status(); + + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_replication_group_member_stats.cc b/storage/perfschema/table_replication_group_member_stats.cc new file mode 100644 index 00000000..245b4ffb --- /dev/null +++ b/storage/perfschema/table_replication_group_member_stats.cc @@ -0,0 +1,378 @@ +/* + Copyright (c) 2014, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_replication_group_member_stats.cc + Table replication_group_member_stats (implementation). +*/ + +//#define HAVE_REPLICATION + +#include "my_global.h" +#include "table_replication_group_member_stats.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "log.h" +#include "rpl_group_replication.h" + +/* + Callbacks implementation for GROUP_REPLICATION_GROUP_MEMBER_STATS_CALLBACKS. +*/ +static void set_channel_name(void* const context, const char& value, + size_t length) +{ + struct st_row_group_member_stats* row= + static_cast<struct st_row_group_member_stats*>(context); + const size_t max= CHANNEL_NAME_LENGTH; + length= std::min(length, max); + + row->channel_name_length= length; + memcpy(row->channel_name, &value, length); +} + +static void set_view_id(void* const context, const char& value, size_t length) +{ + struct st_row_group_member_stats* row= + static_cast<struct st_row_group_member_stats*>(context); + const size_t max= HOSTNAME_LENGTH; + length= std::min(length, max); + + row->view_id_length= length; + memcpy(row->view_id, &value, length); +} + +static void set_member_id(void* const context, const char& value, size_t length) +{ + struct st_row_group_member_stats* row= + static_cast<struct st_row_group_member_stats*>(context); + const size_t max= UUID_LENGTH; + length= std::min(length, max); + + row->member_id_length= length; + memcpy(row->member_id, &value, length); +} + +static void set_transactions_committed(void* const context, const char& value, + size_t length) +{ + struct st_row_group_member_stats* row= + static_cast<struct st_row_group_member_stats*>(context); + + if (row->trx_committed != NULL) + my_free(row->trx_committed); + + row->trx_committed_length= length; + row->trx_committed= (char*) my_malloc(PSI_NOT_INSTRUMENTED, + length, + MYF(0)); + memcpy(row->trx_committed, &value, length); +} + +static void set_last_conflict_free_transaction(void* const context, + const char& value, size_t length) +{ + struct st_row_group_member_stats* row= + static_cast<struct st_row_group_member_stats*>(context); + const size_t max= Gtid::MAX_TEXT_LENGTH+1; + length= std::min(length, max); + + row->last_cert_trx_length= length; + memcpy(row->last_cert_trx, &value, length); +} + +static void set_transactions_in_queue(void* const context, + unsigned long long int value) +{ + struct st_row_group_member_stats* row= + static_cast<struct st_row_group_member_stats*>(context); + row->trx_in_queue= value; +} + +static void set_transactions_certified(void* const context, + unsigned long long int value) +{ + struct st_row_group_member_stats* row= + static_cast<struct st_row_group_member_stats*>(context); + row->trx_checked= value; +} + +static void set_transactions_conflicts_detected(void* const context, + unsigned long long int value) +{ + struct st_row_group_member_stats* row= + static_cast<struct st_row_group_member_stats*>(context); + row->trx_conflicts= value; +} + +static void set_transactions_rows_in_validation(void* const context, + unsigned long long int value) +{ + struct st_row_group_member_stats* row= + static_cast<struct st_row_group_member_stats*>(context); + row->trx_rows_validating= value; +} + + +THR_LOCK table_replication_group_member_stats::m_table_lock; + +static const TABLE_FIELD_TYPE field_types[]= +{ + { + {C_STRING_WITH_LEN("CHANNEL_NAME")}, + {C_STRING_WITH_LEN("char(64)")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("VIEW_ID")}, + {C_STRING_WITH_LEN("char(60)")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("MEMBER_ID")}, + {C_STRING_WITH_LEN("char(36)")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("COUNT_TRANSACTIONS_IN_QUEUE")}, + {C_STRING_WITH_LEN("bigint")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("COUNT_TRANSACTIONS_CHECKED")}, + {C_STRING_WITH_LEN("bigint")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("COUNT_CONFLICTS_DETECTED")}, + {C_STRING_WITH_LEN("bigint")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("COUNT_TRANSACTIONS_ROWS_VALIDATING")}, + {C_STRING_WITH_LEN("bigint")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("TRANSACTIONS_COMMITTED_ALL_MEMBERS")}, + {C_STRING_WITH_LEN("longtext")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("LAST_CONFLICT_FREE_TRANSACTION")}, + {C_STRING_WITH_LEN("text")}, + {NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_replication_group_member_stats::m_field_def= +{ 9, field_types }; + +PFS_engine_table_share_state +table_replication_group_member_stats::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_replication_group_member_stats::m_share= +{ + { C_STRING_WITH_LEN("replication_group_member_stats") }, + &pfs_readonly_acl, + &table_replication_group_member_stats::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_replication_group_member_stats::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + &m_field_def, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_replication_group_member_stats::create(void) +{ + return new table_replication_group_member_stats(); +} + +table_replication_group_member_stats::table_replication_group_member_stats() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{ + m_row.trx_committed= NULL; +} + +table_replication_group_member_stats::~table_replication_group_member_stats() +{ + if (m_row.trx_committed != NULL) + { + my_free(m_row.trx_committed); + m_row.trx_committed= NULL; + } +} + +void table_replication_group_member_stats::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +ha_rows table_replication_group_member_stats::get_row_count() +{ + uint row_count= 0; + + if (is_group_replication_plugin_loaded()) + row_count= 1; + + return row_count; +} + +int table_replication_group_member_stats::rnd_next(void) +{ + if (!is_group_replication_plugin_loaded()) + return HA_ERR_END_OF_FILE; + + m_pos.set_at(&m_next_pos); + if (m_pos.m_index == 0) + { + make_row(); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_replication_group_member_stats::rnd_pos(const void *pos) +{ + if (get_row_count() == 0) + return HA_ERR_END_OF_FILE; + + set_position(pos); + assert(m_pos.m_index < 1); + make_row(); + + return 0; +} + +void table_replication_group_member_stats::make_row() +{ + DBUG_ENTER("table_replication_group_member_stats::make_row"); + // Set default values. + m_row_exists= false; + m_row.channel_name_length= 0; + m_row.view_id_length= 0; + m_row.member_id_length= 0; + m_row.trx_committed_length= 0; + m_row.last_cert_trx_length= 0; + m_row.trx_in_queue= 0; + m_row.trx_checked= 0; + m_row.trx_conflicts= 0; + m_row.trx_rows_validating= 0; + + // Set callbacks on GROUP_REPLICATION_GROUP_MEMBER_STATS_CALLBACKS. + const GROUP_REPLICATION_GROUP_MEMBER_STATS_CALLBACKS callbacks= + { + &m_row, + &set_channel_name, + &set_view_id, + &set_member_id, + &set_transactions_committed, + &set_last_conflict_free_transaction, + &set_transactions_in_queue, + &set_transactions_certified, + &set_transactions_conflicts_detected, + &set_transactions_rows_in_validation, + }; + + // Query plugin and let callbacks do their job. + if (get_group_replication_group_member_stats_info(callbacks)) + { + DBUG_PRINT("info", ("Group Replication stats not available!")); + } + else + { + m_row_exists= true; + } + + DBUG_VOID_RETURN; +} + + +int table_replication_group_member_stats::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + assert(table->s->null_bytes == 0); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /** channel_name */ + set_field_char_utf8(f, m_row.channel_name, + m_row.channel_name_length); + break; + case 1: /** view id */ + set_field_char_utf8(f, m_row.view_id, m_row.view_id_length); + break; + case 2: /** member_id */ + set_field_char_utf8(f, m_row.member_id, m_row.member_id_length); + break; + case 3: /** transaction_in_queue */ + set_field_ulonglong(f, m_row.trx_in_queue); + break; + case 4: /** transactions_certified */ + set_field_ulonglong(f, m_row.trx_checked); + break; + case 5: /** negatively_certified_transaction */ + set_field_ulonglong(f, m_row.trx_conflicts); + break; + case 6: /** certification_db_size */ + set_field_ulonglong(f, m_row.trx_rows_validating); + break; + case 7: /** stable_set */ + set_field_longtext_utf8(f, m_row.trx_committed, + m_row.trx_committed_length); + break; + case 8: /** last_certified_transaction */ + set_field_longtext_utf8(f, m_row.last_cert_trx, + m_row.last_cert_trx_length); + + break; + default: + assert(false); + } + } + } + return 0; +} diff --git a/storage/perfschema/table_replication_group_member_stats.h b/storage/perfschema/table_replication_group_member_stats.h new file mode 100644 index 00000000..d3d00a7a --- /dev/null +++ b/storage/perfschema/table_replication_group_member_stats.h @@ -0,0 +1,117 @@ +/* + Copyright (c) 2014, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#ifndef TABLE_REPLICATION_GROUP_MEMBER_STATS_H +#define TABLE_REPLICATION_GROUP_MEMBER_STATS_H + +/** + @file storage/perfschema/table_replication_group_member_stats.h + Table replication_group_member_stats (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "mysql_com.h" +//#include "rpl_info.h" +//#include "rpl_gtid.h" +//#include <mysql/plugin_group_replication.h> + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row in node status table. The fields with string values have an additional + length field denoted by <field_name>_length. +*/ + +struct st_row_group_member_stats { + char channel_name[CHANNEL_NAME_LENGTH]; + uint channel_name_length; + char view_id[HOSTNAME_LENGTH]; + uint view_id_length; + char member_id[11]; // typeof(server_id) == uint32 + uint member_id_length; + ulonglong trx_in_queue; + ulonglong trx_checked; + ulonglong trx_conflicts; + ulonglong trx_rows_validating; + char *trx_committed; + size_t trx_committed_length; + char last_cert_trx[GTID_MAX_STR_LENGTH + 1]; + int last_cert_trx_length; +}; + +/** Table PERFORMANCE_SCHEMA.REPLICATION_GROUP_MEMBER_STATS. */ +class table_replication_group_member_stats: public PFS_engine_table +{ +private: + void make_row(); + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + /** True if the current row exists. */ + bool m_row_exists; + /** Current row */ + st_row_group_member_stats m_row; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; + +protected: + /** + Read the current row values. + @param table Table handle + @param buf row buffer + @param fields Table fields + @param read_all true if all columns are read. + */ + + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_replication_group_member_stats(); + +public: + ~table_replication_group_member_stats(); + + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +}; + +/** @} */ +#endif + diff --git a/storage/perfschema/table_replication_group_members.cc b/storage/perfschema/table_replication_group_members.cc new file mode 100644 index 00000000..80f67ccc --- /dev/null +++ b/storage/perfschema/table_replication_group_members.cc @@ -0,0 +1,287 @@ +/* + Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_replication_group_members.cc + Table replication_group_members (implementation). +*/ + +//#define HAVE_REPLICATION + +#include "my_global.h" +#include "table_replication_group_members.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "log.h" +//#include "rpl_group_replication.h" + +/* + Callbacks implementation for GROUP_REPLICATION_GROUP_MEMBERS_CALLBACKS. +*/ +static void set_channel_name(void* const context, const char& value, + size_t length) +{ + struct st_row_group_members* row= + static_cast<struct st_row_group_members*>(context); + const size_t max= CHANNEL_NAME_LENGTH; + length= std::min(length, max); + + row->channel_name_length= length; + memcpy(row->channel_name, &value, length); +} + +static void set_member_id(void* const context, const char& value, + size_t length) +{ + struct st_row_group_members* row= + static_cast<struct st_row_group_members*>(context); + const size_t max= UUID_LENGTH; + length= std::min(length, max); + + row->member_id_length= length; + memcpy(row->member_id, &value, length); +} + +static void set_member_host(void* const context, const char& value, + size_t length) +{ + struct st_row_group_members* row= + static_cast<struct st_row_group_members*>(context); + const size_t max= HOSTNAME_LENGTH; + length= std::min(length, max); + + row->member_host_length= length; + memcpy(row->member_host, &value, length); +} + +static void set_member_port(void* const context, unsigned int value) +{ + struct st_row_group_members* row= + static_cast<struct st_row_group_members*>(context); + row->member_port= value; +} + +static void set_member_state(void* const context, const char& value, + size_t length) +{ + struct st_row_group_members* row= + static_cast<struct st_row_group_members*>(context); + const size_t max= NAME_LEN; + length= std::min(length, max); + + row->member_state_length= length; + memcpy(row->member_state, &value, length); +} + + +THR_LOCK table_replication_group_members::m_table_lock; + +/* Numbers in varchar count utf8 characters. */ +static const TABLE_FIELD_TYPE field_types[]= +{ + { + {C_STRING_WITH_LEN("CHANNEL_NAME")}, + {C_STRING_WITH_LEN("char(64)")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("MEMBER_ID")}, + {C_STRING_WITH_LEN("char(36)")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("MEMBER_HOST")}, + {C_STRING_WITH_LEN("char(60)")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("MEMBER_PORT")}, + {C_STRING_WITH_LEN("int(11)")}, + {NULL, 0} + }, + { + {C_STRING_WITH_LEN("MEMBER_STATE")}, + {C_STRING_WITH_LEN("char(64)")}, + {NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_replication_group_members::m_field_def= +{ 5, field_types }; + +PFS_engine_table_share_state +table_replication_group_members::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_replication_group_members::m_share= +{ + { C_STRING_WITH_LEN("replication_group_members") }, + &pfs_readonly_acl, + &table_replication_group_members::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_replication_group_members::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + &m_field_def, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_replication_group_members::create(void) +{ + return new table_replication_group_members(); +} + +table_replication_group_members::table_replication_group_members() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +table_replication_group_members::~table_replication_group_members() +{} + +void table_replication_group_members::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +ha_rows table_replication_group_members::get_row_count() +{ + return get_group_replication_members_number_info(); +} + +int table_replication_group_members::rnd_next(void) +{ + if (!is_group_replication_plugin_loaded()) + return HA_ERR_END_OF_FILE; + + for (m_pos.set_at(&m_next_pos); + m_pos.m_index < get_row_count(); + m_pos.next()) + { + make_row(m_pos.m_index); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_replication_group_members::rnd_pos(const void *pos) +{ + if (!is_group_replication_plugin_loaded()) + return HA_ERR_END_OF_FILE; + + set_position(pos); + assert(m_pos.m_index < get_row_count()); + make_row(m_pos.m_index); + + return 0; +} + +void table_replication_group_members::make_row(uint index) +{ + DBUG_ENTER("table_replication_group_members::make_row"); + // Set default values. + m_row_exists= false; + m_row.channel_name_length= 0; + m_row.member_id_length= 0; + m_row.member_host_length= 0; + m_row.member_port= 0; + m_row.member_state_length= 0; + + // Set callbacks on GROUP_REPLICATION_GROUP_MEMBERS_CALLBACKS. + const GROUP_REPLICATION_GROUP_MEMBERS_CALLBACKS callbacks= + { + &m_row, + &set_channel_name, + &set_member_id, + &set_member_host, + &set_member_port, + &set_member_state, + }; + + // Query plugin and let callbacks do their job. + if (get_group_replication_group_members_info(index, callbacks)) + { + DBUG_PRINT("info", ("Group Replication stats not available!")); + } + else + { + m_row_exists= true; + } + + DBUG_VOID_RETURN; +} + + +int table_replication_group_members::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /** channel_name */ + set_field_char_utf8(f, m_row.channel_name, m_row.channel_name_length); + break; + case 1: /** member_id */ + set_field_char_utf8(f, m_row.member_id, m_row.member_id_length); + break; + case 2: /** member_host */ + set_field_char_utf8(f, m_row.member_host, m_row.member_host_length); + break; + case 3: /** member_port */ + if (m_row.member_port > 0) + set_field_ulong(f, m_row.member_port); + else + f->set_null(); + break; + case 4: /** member_state */ + set_field_char_utf8(f, m_row.member_state, m_row.member_state_length); + break; + default: + assert(false); + } + } + } + return 0; +} diff --git a/storage/perfschema/table_replication_group_members.h b/storage/perfschema/table_replication_group_members.h new file mode 100644 index 00000000..3ea57878 --- /dev/null +++ b/storage/perfschema/table_replication_group_members.h @@ -0,0 +1,109 @@ +/* + Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#ifndef TABLE_REPLICATION_GROUP_MEMBERS_H +#define TABLE_REPLICATION_GROUP_MEMBERS_H + +/** + @file storage/perfschema/table_replication_group_members.h + Table replication_group_members (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "mysql_com.h" +//#include "rpl_info.h" +//#include <mysql/plugin_group_replication.h> + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row in connection nodes table. The fields with string values have an additional + length field denoted by <field_name>_length. +*/ +struct st_row_group_members { + char channel_name[CHANNEL_NAME_LENGTH]; + uint channel_name_length; + char member_id[11]; // typeof(server_id) == uint32 + uint member_id_length; + char member_host[HOSTNAME_LENGTH]; + uint member_host_length; + uint member_port; + char member_state[NAME_LEN]; + uint member_state_length; +}; + +/** Table PERFORMANCE_SCHEMA.replication_group_members. */ +class table_replication_group_members: public PFS_engine_table +{ +private: + void make_row(uint index); + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + /** True if the current row exists. */ + bool m_row_exists; + /** Current row */ + st_row_group_members m_row; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; + +protected: + /** + Read the current row values. + @param table Table handle + @param buf row buffer + @param fields Table fields + @param read_all true if all columns are read. + */ + + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_replication_group_members(); + +public: + ~table_replication_group_members(); + + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_session_account_connect_attrs.cc b/storage/perfschema/table_session_account_connect_attrs.cc new file mode 100644 index 00000000..3caa43bf --- /dev/null +++ b/storage/perfschema/table_session_account_connect_attrs.cc @@ -0,0 +1,89 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include "table_session_account_connect_attrs.h" + +THR_LOCK table_session_account_connect_attrs::m_table_lock; + +PFS_engine_table_share_state +table_session_account_connect_attrs::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_session_account_connect_attrs::m_share= +{ + { C_STRING_WITH_LEN("session_account_connect_attrs") }, + &pfs_readonly_world_acl, + table_session_account_connect_attrs::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + cursor_by_thread_connect_attr::get_row_count, + sizeof(pos_connect_attr_by_thread_by_attr), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE session_account_connect_attrs(" + "PROCESSLIST_ID INT NOT NULL comment 'Session connection identifier.'," + "ATTR_NAME VARCHAR(32) NOT NULL comment 'Attribute name.'," + "ATTR_VALUE VARCHAR(1024) comment 'Attribute value.'," + "ORDINAL_POSITION INT comment 'Order in which attribute was added to the connection attributes.'" + ") CHARACTER SET utf8 COLLATE utf8_bin") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_session_account_connect_attrs::create() +{ + return new table_session_account_connect_attrs(); +} + +table_session_account_connect_attrs::table_session_account_connect_attrs() + : table_session_connect(&m_share) +{} + +bool +table_session_account_connect_attrs::thread_fits(PFS_thread *thread) +{ + PFS_thread *current_thread= PFS_thread::get_current_thread(); + /* The current thread may not have instrumentation attached. */ + if (current_thread == NULL) + return false; + + /* The thread we compare to, by definition, has some instrumentation. */ + assert(thread != NULL); + + uint username_length= current_thread->m_username_length; + uint hostname_length= current_thread->m_hostname_length; + + if ( (thread->m_username_length != username_length) + || (thread->m_hostname_length != hostname_length)) + return false; + + if (memcmp(thread->m_username, current_thread->m_username, username_length) != 0) + return false; + + if (memcmp(thread->m_hostname, current_thread->m_hostname, hostname_length) != 0) + return false; + + return true; +} diff --git a/storage/perfschema/table_session_account_connect_attrs.h b/storage/perfschema/table_session_account_connect_attrs.h new file mode 100644 index 00000000..6a405db6 --- /dev/null +++ b/storage/perfschema/table_session_account_connect_attrs.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2012, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_SESSION_ACCOUNT_CONNECT_ATTRS_H +#define TABLE_SESSION_ACCOUNT_CONNECT_ATTRS_H + +#include "table_session_connect.h" +/** + \addtogroup Performance_schema_tables + @{ +*/ + +/** Table PERFORMANCE_SCHEMA.SESSION_ACCOUNT_CONNECT_ATTRS. */ +class table_session_account_connect_attrs : public table_session_connect +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + /** Table builder */ + static PFS_engine_table* create(); + +protected: + table_session_account_connect_attrs(); + +public: + ~table_session_account_connect_attrs() = default; + +protected: + virtual bool thread_fits(PFS_thread *thread); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_session_connect.cc b/storage/perfschema/table_session_connect.cc new file mode 100644 index 00000000..54511e1b --- /dev/null +++ b/storage/perfschema/table_session_connect.cc @@ -0,0 +1,295 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include "table_session_connect.h" +#include "field.h" + +table_session_connect::table_session_connect(const PFS_engine_table_share *share) + : cursor_by_thread_connect_attr(share) +{ + if (session_connect_attrs_size_per_thread > 0) + { + m_copy_session_connect_attrs= (char *) my_malloc(PSI_INSTRUMENT_ME, + session_connect_attrs_size_per_thread, + MYF(0)); + } + else + { + m_copy_session_connect_attrs= NULL; + } + m_copy_session_connect_attrs_length= 0; +} + +table_session_connect::~table_session_connect() +{ + my_free(m_copy_session_connect_attrs); +} + +/** + Take a length encoded string + + @arg ptr inout the input string array + @arg dest where to store the result + @arg dest_size max size of @c dest + @arg copied_len the actual length of the data copied + @arg start_ptr pointer to the start of input + @arg input_length the length of the incoming data + @arg copy_data copy the data or just skip the input + @arg from_cs character set in which @c ptr is encoded + @arg nchars_max maximum number of characters to read + @return status + @retval true parsing failed + @retval false parsing succeeded +*/ +bool parse_length_encoded_string(const char **ptr, + char *dest, uint dest_size, + uint *copied_len, + const char *start_ptr, uint input_length, + bool copy_data, + const CHARSET_INFO *from_cs, + uint nchars_max) +{ + ulong copy_length, data_length; + String_copier copier; + + copy_length= data_length= net_field_length((uchar **) ptr); + + /* we don't tolerate NULL as a length */ + if (data_length == NULL_LENGTH) + return true; + + if (*ptr - start_ptr + data_length > input_length) + return true; + + copy_length= copier.well_formed_copy(&my_charset_utf8mb3_bin, dest, dest_size, + from_cs, *ptr, data_length, nchars_max); + *copied_len= copy_length; + (*ptr)+= data_length; + + return false; +} + +/** + Take the nth attribute name/value pair + + Parse the attributes blob form the beginning, skipping the attributes + whose number is lower than the one we seek. + When we reach the attribute at an index we're looking for the values + are copied to the output parameters. + If parsing fails or no more attributes are found the function stops + and returns an error code. + + @arg connect_attrs pointer to the connect attributes blob + @arg connect_attrs_length length of @c connect_attrs + @arg connect_attrs_cs character set used to encode @c connect_attrs + @arg ordinal index of the attribute we need + @arg attr_name [out] buffer to receive the attribute name + @arg max_attr_name max size of @c attr_name in bytes + @arg attr_name_length [out] number of bytes written in @attr_name + @arg attr_value [out] buffer to receive the attribute name + @arg max_attr_value max size of @c attr_value in bytes + @arg attr_value_length [out] number of bytes written in @attr_value + @return status + @retval true requested attribute pair is found and copied + @retval false error. Either because of parsing or too few attributes. +*/ +bool read_nth_attr(const char *connect_attrs, + uint connect_attrs_length, + const CHARSET_INFO *connect_attrs_cs, + uint ordinal, + char *attr_name, uint max_attr_name, + uint *attr_name_length, + char *attr_value, uint max_attr_value, + uint *attr_value_length) +{ + uint idx; + const char *ptr; + + for (ptr= connect_attrs, idx= 0; + (uint)(ptr - connect_attrs) < connect_attrs_length && idx <= ordinal; + idx++) + { + uint copy_length; + /* do the copying only if we absolutely have to */ + bool fill_in_attr_name= idx == ordinal; + bool fill_in_attr_value= idx == ordinal; + + /* read the key */ + if (parse_length_encoded_string(&ptr, + attr_name, max_attr_name, ©_length, + connect_attrs, + connect_attrs_length, + fill_in_attr_name, + connect_attrs_cs, 32) || + !copy_length + ) + return false; + + if (idx == ordinal) + *attr_name_length= copy_length; + + /* read the value */ + if (parse_length_encoded_string(&ptr, + attr_value, max_attr_value, ©_length, + connect_attrs, + connect_attrs_length, + fill_in_attr_value, + connect_attrs_cs, 1024)) + return false; + + if (idx == ordinal) + *attr_value_length= copy_length; + + if (idx == ordinal) + return true; + } + + return false; +} + +void table_session_connect::make_row(PFS_thread *pfs, uint ordinal) +{ + pfs_optimistic_state lock; + pfs_optimistic_state session_lock; + PFS_thread_class *safe_class; + const CHARSET_INFO *cs; + + m_row_exists= false; + + /* Protect this reader against thread termination */ + pfs->m_lock.begin_optimistic_lock(&lock); + /* Protect this reader against writing on session attributes */ + pfs->m_session_lock.begin_optimistic_lock(&session_lock); + + safe_class= sanitize_thread_class(pfs->m_class); + if (unlikely(safe_class == NULL)) + return; + + /* Filtering threads must be done under the protection of the optimistic lock. */ + if (! thread_fits(pfs)) + return; + + /* Make a safe copy of the session attributes */ + + if (m_copy_session_connect_attrs == NULL) + return; + + m_copy_session_connect_attrs_length= pfs->m_session_connect_attrs_length; + + if (m_copy_session_connect_attrs_length > session_connect_attrs_size_per_thread) + return; + + memcpy(m_copy_session_connect_attrs, + pfs->m_session_connect_attrs, + m_copy_session_connect_attrs_length); + + cs= get_charset(pfs->m_session_connect_attrs_cs_number, MYF(0)); + if (cs == NULL) + return; + + if (! pfs->m_session_lock.end_optimistic_lock(& session_lock)) + return; + + if (! pfs->m_lock.end_optimistic_lock(& lock)) + return; + + /* + Now we have a safe copy of the data, + that will not change while parsing it + */ + + /* populate the row */ + if (read_nth_attr(m_copy_session_connect_attrs, + m_copy_session_connect_attrs_length, + cs, + ordinal, + m_row.m_attr_name, (uint) sizeof(m_row.m_attr_name), + &m_row.m_attr_name_length, + m_row.m_attr_value, (uint) sizeof(m_row.m_attr_value), + &m_row.m_attr_value_length)) + { + /* we don't expect internal threads to have connection attributes */ + if (pfs->m_processlist_id == 0) + return; + + m_row.m_ordinal_position= ordinal; + m_row.m_process_id= pfs->m_processlist_id; + + m_row_exists= true; + } +} + +int table_session_connect::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(!m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case FO_PROCESS_ID: + if (m_row.m_process_id != 0) + set_field_ulong(f, m_row.m_process_id); + else + f->set_null(); + break; + case FO_ATTR_NAME: + set_field_varchar_utf8(f, m_row.m_attr_name, + m_row.m_attr_name_length); + break; + case FO_ATTR_VALUE: + if (m_row.m_attr_value_length) + set_field_varchar_utf8(f, m_row.m_attr_value, + m_row.m_attr_value_length); + else + f->set_null(); + break; + case FO_ORDINAL_POSITION: + set_field_ulong(f, m_row.m_ordinal_position); + break; + default: + assert(false); + } + } + } + return 0; +} + +bool +table_session_connect::thread_fits(PFS_thread *thread) +{ + return true; +} + diff --git a/storage/perfschema/table_session_connect.h b/storage/perfschema/table_session_connect.h new file mode 100644 index 00000000..bcc42beb --- /dev/null +++ b/storage/perfschema/table_session_connect.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2012, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_SESSION_CONNECT_H +#define TABLE_SESSION_CONNECT_H + +#include "pfs_column_types.h" +#include "cursor_by_thread_connect_attr.h" +#include "table_helper.h" + +#define MAX_ATTR_NAME_CHARS 32 +#define MAX_ATTR_VALUE_CHARS 1024 +#define MAX_UTF8_BYTES 6 + +/** symbolic names for field offsets, keep in sync with field_types */ +enum field_offsets { + FO_PROCESS_ID, + FO_ATTR_NAME, + FO_ATTR_VALUE, + FO_ORDINAL_POSITION +}; + +/** + A row of PERFORMANCE_SCHEMA.SESSION_CONNECT_ATTRS and + PERFORMANCE_SCHEMA.SESSION_ACCOUNT_CONNECT_ATTRS. +*/ +struct row_session_connect_attrs +{ + /** Column PROCESS_ID. */ + ulong m_process_id; + /** Column ATTR_NAME. In UTF-8 */ + char m_attr_name[MAX_ATTR_NAME_CHARS * MAX_UTF8_BYTES]; + /** Length in bytes of @c m_attr_name. */ + uint m_attr_name_length; + /** Column ATTR_VALUE. In UTF-8 */ + char m_attr_value[MAX_ATTR_VALUE_CHARS * MAX_UTF8_BYTES]; + /** Length in bytes of @c m_attr_name. */ + uint m_attr_value_length; + /** Column ORDINAL_POSITION. */ + ulong m_ordinal_position; +}; + +/** Abstract table PERFORMANCE_SCHEMA.SESSION_CONNECT_ATTRS. */ +class table_session_connect : public cursor_by_thread_connect_attr +{ +protected: + table_session_connect(const PFS_engine_table_share *share); + +public: + ~table_session_connect(); + +protected: + virtual void make_row(PFS_thread *pfs, uint ordinal); + virtual bool thread_fits(PFS_thread *thread); + virtual int read_row_values(TABLE *table, unsigned char *buf, + Field **fields, bool read_all); +protected: + /** Current row. */ + row_session_connect_attrs m_row; + /** Safe copy of @c PFS_thread::m_session_connect_attrs. */ + char *m_copy_session_connect_attrs; + /** Safe copy of @c PFS_thread::m_session_connect_attrs_length. */ + uint m_copy_session_connect_attrs_length; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_session_connect_attrs.cc b/storage/perfschema/table_session_connect_attrs.cc new file mode 100644 index 00000000..8265a0f5 --- /dev/null +++ b/storage/perfschema/table_session_connect_attrs.cc @@ -0,0 +1,62 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include "table_session_connect_attrs.h" + +THR_LOCK table_session_connect_attrs::m_table_lock; + +PFS_engine_table_share_state +table_session_connect_attrs::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_session_connect_attrs::m_share= +{ + { C_STRING_WITH_LEN("session_connect_attrs") }, + &pfs_readonly_acl, + table_session_connect_attrs::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + cursor_by_thread_connect_attr::get_row_count, + sizeof(pos_connect_attr_by_thread_by_attr), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE session_connect_attrs(" + "PROCESSLIST_ID INT NOT NULL comment 'Session connection identifier.'," + "ATTR_NAME VARCHAR(32) NOT NULL comment 'Attribute name.'," + "ATTR_VALUE VARCHAR(1024) comment 'Attribute value.'," + "ORDINAL_POSITION INT comment 'Order in which attribute was added to the connection attributes.'" + ") CHARACTER SET utf8 COLLATE utf8_bin") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_session_connect_attrs::create() +{ + return new table_session_connect_attrs(); +} + +table_session_connect_attrs::table_session_connect_attrs() + : table_session_connect(&m_share) +{} diff --git a/storage/perfschema/table_session_connect_attrs.h b/storage/perfschema/table_session_connect_attrs.h new file mode 100644 index 00000000..43c929ea --- /dev/null +++ b/storage/perfschema/table_session_connect_attrs.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2012, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_SESSION_CONNECT_ATTRS_H +#define TABLE_SESSION_CONNECT_ATTRS_H + +#include "table_session_connect.h" +/** + \addtogroup Performance_schema_tables + @{ +*/ + +/** Table PERFORMANCE_SCHEMA.SESSION_CONNECT_ATTRS. */ +class table_session_connect_attrs : public table_session_connect +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + /** Table builder */ + static PFS_engine_table* create(); + +protected: + table_session_connect_attrs(); + +public: + ~table_session_connect_attrs() = default; + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_session_status.cc b/storage/perfschema/table_session_status.cc new file mode 100644 index 00000000..3baad528 --- /dev/null +++ b/storage/perfschema/table_session_status.cc @@ -0,0 +1,192 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_session_status.cc + Table SESSION_STATUS (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "table_session_status.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "pfs_global.h" + +THR_LOCK table_session_status::m_table_lock; + +PFS_engine_table_share_state +table_session_status::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_session_status::m_share= +{ + { C_STRING_WITH_LEN("session_status") }, + &pfs_readonly_world_acl, + table_session_status::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_session_status::get_row_count, + sizeof(pos_t), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE session_status(" + "VARIABLE_NAME VARCHAR(64) not null comment 'The session status variable name.'," + "VARIABLE_VALUE VARCHAR(1024) comment 'The session status variable value.')") }, + true, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_session_status::create(void) +{ + return new table_session_status(); +} + +ha_rows table_session_status::get_row_count(void) +{ + mysql_mutex_lock(&LOCK_status); + ha_rows status_var_count= all_status_vars.elements; + mysql_mutex_unlock(&LOCK_status); + return status_var_count; +} + +table_session_status::table_session_status() + : PFS_engine_table(&m_share, &m_pos), + m_status_cache(false), m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_session_status::reset_position(void) +{ + m_pos.m_index = 0; + m_next_pos.m_index = 0; +} + +int table_session_status::rnd_init(bool scan) +{ + /* Build a cache of all status variables for this thread. */ + m_status_cache.materialize_all(current_thd); + + /* Record the current number of status variables to detect subsequent changes. */ + ulonglong status_version= m_status_cache.get_status_array_version(); + + /* + The table context holds the current version of the global status array. + If scan == true, then allocate a new context from mem_root and store in TLS. + If scan == false, then restore from TLS. + */ + m_context= (table_session_status_context *)current_thd->alloc(sizeof(table_session_status_context)); + new(m_context) table_session_status_context(status_version, !scan); + return 0; +} + +int table_session_status::rnd_next(void) +{ + for (m_pos.set_at(&m_next_pos); + m_pos.m_index < m_status_cache.size(); + m_pos.next()) + { + if (m_status_cache.is_materialized()) + { + const Status_variable *stat_var= m_status_cache.get(m_pos.m_index); + if (stat_var != NULL) + { + make_row(stat_var); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + return HA_ERR_END_OF_FILE; +} + +int +table_session_status::rnd_pos(const void *pos) +{ + /* If global status array has changed, do nothing. */ // TODO: warning + if (!m_context->versions_match()) + return HA_ERR_RECORD_DELETED; + + set_position(pos); + assert(m_pos.m_index < m_status_cache.size()); + + if (m_status_cache.is_materialized()) + { + const Status_variable *stat_var= m_status_cache.get(m_pos.m_index); + if (stat_var != NULL) + { + make_row(stat_var); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_session_status +::make_row(const Status_variable *status_var) +{ + m_row_exists= false; + m_row.m_variable_name.make_row(status_var->m_name, status_var->m_name_length); + m_row.m_variable_value.make_row(status_var); + m_row_exists= true; +} + +int table_session_status +::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(!m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* VARIABLE_NAME */ + set_field_varchar_utf8(f, m_row.m_variable_name.m_str, m_row.m_variable_name.m_length); + break; + case 1: /* VARIABLE_VALUE */ + m_row.m_variable_value.set_field(f); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_session_status.h b/storage/perfschema/table_session_status.h new file mode 100644 index 00000000..0b3e16c6 --- /dev/null +++ b/storage/perfschema/table_session_status.h @@ -0,0 +1,120 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef TABLE_SESSION_STATUS_H +#define TABLE_SESSION_STATUS_H + +/** + @file storage/perfschema/table_session_status.h + Table SESSION_STATUS (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" +#include "pfs_variable.h" +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.SESSION_STATUS. +*/ +struct row_session_status +{ + /** Column THREAD_ID. */ + ulonglong m_thread_internal_id; + /** Column VARIABLE_NAME. */ + PFS_variable_name_row m_variable_name; + /** Column VARIABLE_VALUE. */ + PFS_variable_value_row m_variable_value; +}; + +/** + Store and retrieve table state information for queries that reinstantiate + the table object. +*/ +class table_session_status_context : public PFS_table_context +{ +public: + table_session_status_context(ulonglong current_version, bool restore) : + PFS_table_context(current_version, restore, THR_PFS_SS) { } +}; + +/** Table PERFORMANCE_SCHEMA.SESSION_STATUS. */ +class table_session_status : public PFS_engine_table +{ + typedef PFS_simple_index pos_t; + +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + table_session_status(); + +public: + ~table_session_status() + {} + +protected: + void make_row(const Status_variable *status_var); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current THD variables. */ + PFS_status_variable_cache m_status_cache; + /** Current row. */ + row_session_status m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + + /** Table context with global status array version. */ + table_session_status_context *m_context; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_session_variables.cc b/storage/perfschema/table_session_variables.cc new file mode 100644 index 00000000..b3b751c9 --- /dev/null +++ b/storage/perfschema/table_session_variables.cc @@ -0,0 +1,193 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_session_variables.cc + Table SESSION_VARIABLES (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "table_session_variables.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "pfs_global.h" + +THR_LOCK table_session_variables::m_table_lock; + +PFS_engine_table_share_state +table_session_variables::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_session_variables::m_share= +{ + { C_STRING_WITH_LEN("session_variables") }, + &pfs_readonly_world_acl, + table_session_variables::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_session_variables::get_row_count, + sizeof(pos_t), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE session_variables(" + "VARIABLE_NAME VARCHAR(64) not null," + "VARIABLE_VALUE VARCHAR(1024))") }, + true, /* m_perpetual */ + &m_share_state +}; + +PFS_engine_table* +table_session_variables::create(void) +{ + return new table_session_variables(); +} + +ha_rows table_session_variables::get_row_count(void) +{ + mysql_mutex_lock(&LOCK_plugin_delete); + mysql_prlock_rdlock(&LOCK_system_variables_hash); + ha_rows system_var_count= get_system_variable_hash_records(); + mysql_prlock_unlock(&LOCK_system_variables_hash); + mysql_mutex_unlock(&LOCK_plugin_delete); + return system_var_count; +} + +table_session_variables::table_session_variables() + : PFS_engine_table(&m_share, &m_pos), + m_sysvar_cache(false), m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_session_variables::reset_position(void) +{ + m_pos.m_index = 0; + m_next_pos.m_index = 0; +} + +int table_session_variables::rnd_init(bool scan) +{ + /* Build a cache of system variables for this thread. */ + m_sysvar_cache.materialize_all(current_thd); + + /* Record the version of the system variable hash. */ + ulonglong hash_version= m_sysvar_cache.get_sysvar_hash_version(); + + /* + The table context holds the current version of the system variable hash. + If scan == true, then allocate a new context from mem_root and store in TLS. + If scan == false, then restore from TLS. + */ + m_context= (table_session_variables_context *)current_thd->alloc(sizeof(table_session_variables_context)); + new(m_context) table_session_variables_context(hash_version, !scan); + return 0; +} + +int table_session_variables::rnd_next(void) +{ + for (m_pos.set_at(&m_next_pos); + m_pos.m_index < m_sysvar_cache.size(); + m_pos.next()) + { + if (m_sysvar_cache.is_materialized()) + { + const System_variable *system_var= m_sysvar_cache.get(m_pos.m_index); + if (system_var != NULL) + { + make_row(system_var); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + return HA_ERR_END_OF_FILE; +} + +int table_session_variables::rnd_pos(const void *pos) +{ + /* If system variable hash changes, do nothing. */ + if (!m_context->versions_match()) + return HA_ERR_RECORD_DELETED; + + set_position(pos); + assert(m_pos.m_index < m_sysvar_cache.size()); + + if (m_sysvar_cache.is_materialized()) + { + const System_variable *system_var= m_sysvar_cache.get(m_pos.m_index); + if (system_var != NULL) + { + make_row(system_var); + return 0; + } + } + return HA_ERR_RECORD_DELETED; +} + +void table_session_variables +::make_row(const System_variable *system_var) +{ + m_row_exists= false; + if (system_var->is_null() || system_var->is_ignored()) + return; + m_row.m_variable_name.make_row(system_var->m_name, system_var->m_name_length); + m_row.m_variable_value.make_row(system_var); + m_row_exists= true; +} + +int table_session_variables +::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(!m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* VARIABLE_NAME */ + set_field_varchar_utf8(f, m_row.m_variable_name.m_str, m_row.m_variable_name.m_length); + break; + case 1: /* VARIABLE_VALUE */ + m_row.m_variable_value.set_field(f); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_session_variables.h b/storage/perfschema/table_session_variables.h new file mode 100644 index 00000000..f46d9967 --- /dev/null +++ b/storage/perfschema/table_session_variables.h @@ -0,0 +1,118 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef TABLE_SESSION_VARIABLES_H +#define TABLE_SESSION_VARIABLES_H + +/** + @file storage/perfschema/table_session_variables.h + Table SESSION_VARIABLES (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" +#include "pfs_variable.h" +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + Store and retrieve table state information during queries that reinstantiate + the table object. +*/ +class table_session_variables_context : public PFS_table_context +{ +public: + table_session_variables_context(ulonglong hash_version, bool restore) : + PFS_table_context(hash_version, restore, THR_PFS_SV) {} +}; + +/** + A row of table + PERFORMANCE_SCHEMA.SESSION_VARIABLES. +*/ +struct row_session_variables +{ + /** Column VARIABLE_NAME. */ + PFS_variable_name_row m_variable_name; + /** Column VARIABLE_VALUE. */ + PFS_variable_value_row m_variable_value; +}; + +/** Table PERFORMANCE_SCHEMA.SESSION_VARIABLES. */ +class table_session_variables : public PFS_engine_table +{ + typedef PFS_simple_index pos_t; + +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + table_session_variables(); + +public: + ~table_session_variables() + {} + +protected: + void make_row(const System_variable *system_var); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current THD variables. */ + PFS_system_variable_cache m_sysvar_cache; + /** Current row. */ + row_session_variables m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + + /** Table context with system variable hash version. */ + table_session_variables_context *m_context; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_setup_actors.cc b/storage/perfschema/table_setup_actors.cc new file mode 100644 index 00000000..38a7de43 --- /dev/null +++ b/storage/perfschema/table_setup_actors.cc @@ -0,0 +1,319 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_setup_actors.cc + Table SETUP_ACTORS (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "pfs_setup_actor.h" +#include "table_setup_actors.h" +#include "pfs_global.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_setup_actors::m_table_lock; + +PFS_engine_table_share_state +table_setup_actors::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_setup_actors::m_share= +{ + { C_STRING_WITH_LEN("setup_actors") }, + &pfs_editable_acl, + table_setup_actors::create, + table_setup_actors::write_row, + table_setup_actors::delete_all_rows, + table_setup_actors::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE setup_actors(" + "HOST CHAR(" HOSTNAME_LENGTH_STR ") collate utf8_bin default '%' not null comment 'Host name, either a literal, or the % wildcard representing any host.'," + "USER CHAR(" USERNAME_CHAR_LENGTH_STR ") collate utf8_bin default '%' not null comment 'User name, either a literal or the % wildcard representing any name.'," + "ROLE CHAR(" USERNAME_CHAR_LENGTH_STR ") collate utf8_bin default '%' not null comment 'Unused'," + "ENABLED ENUM('YES', 'NO') not null default 'YES' comment 'Whether to enable instrumentation for foreground threads matched by the row.'," + "HISTORY ENUM('YES', 'NO') not null default 'YES' comment 'Whether to log historical events for foreground threads matched by the row.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_setup_actors::create() +{ + return new table_setup_actors(); +} + +int table_setup_actors::write_row(TABLE *table, const unsigned char *buf, + Field **fields) +{ + Field *f; + String user_data("%", 1, &my_charset_utf8mb3_bin); + String host_data("%", 1, &my_charset_utf8mb3_bin); + String role_data("%", 1, &my_charset_utf8mb3_bin); + String *user= &user_data; + String *host= &host_data; + String *role= &role_data; + enum_yes_no enabled_value= ENUM_YES; + enum_yes_no history_value= ENUM_YES; + bool enabled; + bool history; + + for (; (f= *fields) ; fields++) + { + if (bitmap_is_set(table->write_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* HOST */ + host= get_field_char_utf8(f, &host_data); + break; + case 1: /* USER */ + user= get_field_char_utf8(f, &user_data); + break; + case 2: /* ROLE */ + role= get_field_char_utf8(f, &role_data); + break; + case 3: /* ENABLED */ + enabled_value= (enum_yes_no) get_field_enum(f); + break; + case 4: /* HISTORY */ + history_value= (enum_yes_no) get_field_enum(f); + break; + default: + assert(false); + } + } + } + + /* Reject illegal enum values in ENABLED */ + if ((enabled_value != ENUM_YES) && (enabled_value != ENUM_NO)) + return HA_ERR_NO_REFERENCED_ROW; + + /* Reject illegal enum values in HISTORY */ + if ((history_value != ENUM_YES) && (history_value != ENUM_NO)) + return HA_ERR_NO_REFERENCED_ROW; + + /* Reject if any of user/host/role is not provided */ + if (user->length() == 0 || host->length() == 0 || role->length() == 0) + return HA_ERR_WRONG_COMMAND; + + enabled= (enabled_value == ENUM_YES) ? true : false; + history= (history_value == ENUM_YES) ? true : false; + + return insert_setup_actor(user, host, role, enabled, history); +} + +int table_setup_actors::delete_all_rows(void) +{ + return reset_setup_actor(); +} + +ha_rows table_setup_actors::get_row_count(void) +{ + return global_setup_actor_container.get_row_count(); +} + +table_setup_actors::table_setup_actors() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_setup_actors::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_setup_actors::rnd_next() +{ + PFS_setup_actor *pfs; + + m_pos.set_at(&m_next_pos); + PFS_setup_actor_iterator it= global_setup_actor_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_setup_actors::rnd_pos(const void *pos) +{ + PFS_setup_actor *pfs; + + set_position(pos); + + pfs= global_setup_actor_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_setup_actors::make_row(PFS_setup_actor *pfs) +{ + pfs_optimistic_state lock; + + m_row_exists= false; + + pfs->m_lock.begin_optimistic_lock(&lock); + + m_row.m_hostname_length= pfs->m_hostname_length; + if (unlikely((m_row.m_hostname_length == 0) || + (m_row.m_hostname_length > sizeof(m_row.m_hostname)))) + return; + memcpy(m_row.m_hostname, pfs->m_hostname, m_row.m_hostname_length); + + m_row.m_username_length= pfs->m_username_length; + if (unlikely((m_row.m_username_length == 0) || + (m_row.m_username_length > sizeof(m_row.m_username)))) + return; + memcpy(m_row.m_username, pfs->m_username, m_row.m_username_length); + + m_row.m_rolename_length= pfs->m_rolename_length; + if (unlikely((m_row.m_rolename_length == 0) || + (m_row.m_rolename_length > sizeof(m_row.m_rolename)))) + return; + memcpy(m_row.m_rolename, pfs->m_rolename, m_row.m_rolename_length); + + m_row.m_enabled_ptr= &pfs->m_enabled; + m_row.m_history_ptr= &pfs->m_history; + + if (pfs->m_lock.end_optimistic_lock(&lock)) + m_row_exists= true; +} + +int table_setup_actors::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* HOST */ + set_field_char_utf8(f, m_row.m_hostname, m_row.m_hostname_length); + break; + case 1: /* USER */ + set_field_char_utf8(f, m_row.m_username, m_row.m_username_length); + break; + case 2: /* ROLE */ + set_field_char_utf8(f, m_row.m_rolename, m_row.m_rolename_length); + break; + case 3: /* ENABLED */ + set_field_enum(f, (*m_row.m_enabled_ptr) ? ENUM_YES : ENUM_NO); + break; + case 4: /* HISTORY */ + set_field_enum(f, (*m_row.m_history_ptr) ? ENUM_YES : ENUM_NO); + break; + default: + assert(false); + } + } + } + + return 0; +} + +int table_setup_actors::update_row_values(TABLE *table, + const unsigned char *old_buf, + const unsigned char *new_buf, + Field **fields) +{ + int result; + Field *f; + enum_yes_no value; + + for (; (f= *fields) ; fields++) + { + if (bitmap_is_set(table->write_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* HOST */ + case 1: /* USER */ + case 2: /* ROLE */ + return HA_ERR_WRONG_COMMAND; + case 3: /* ENABLED */ + value= (enum_yes_no) get_field_enum(f); + /* Reject illegal enum values in ENABLED */ + if ((value != ENUM_YES) && (value != ENUM_NO)) + return HA_ERR_NO_REFERENCED_ROW; + *m_row.m_enabled_ptr= (value == ENUM_YES) ? true : false; + break; + case 4: /* HISTORY */ + value= (enum_yes_no) get_field_enum(f); + /* Reject illegal enum values in HISTORY */ + if ((value != ENUM_YES) && (value != ENUM_NO)) + return HA_ERR_NO_REFERENCED_ROW; + *m_row.m_history_ptr= (value == ENUM_YES) ? true : false; + break; + default: + assert(false); + } + } + } + + result= update_setup_actors_derived_flags(); + return result; +} + +int table_setup_actors::delete_row_values(TABLE *table, + const unsigned char *buf, + Field **fields) +{ + assert(m_row_exists); + + CHARSET_INFO *cs= &my_charset_utf8mb3_bin; + String user(m_row.m_username, m_row.m_username_length, cs); + String role(m_row.m_rolename, m_row.m_rolename_length, cs); + String host(m_row.m_hostname, m_row.m_hostname_length, cs); + + return delete_setup_actor(&user, &host, &role); +} + diff --git a/storage/perfschema/table_setup_actors.h b/storage/perfschema/table_setup_actors.h new file mode 100644 index 00000000..1909d41e --- /dev/null +++ b/storage/perfschema/table_setup_actors.h @@ -0,0 +1,115 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_SETUP_ACTORS_H +#define TABLE_SETUP_ACTORS_H + +/** + @file storage/perfschema/table_setup_actors.h + Table SETUP_ACTORS (declarations). +*/ + +#include "pfs_engine_table.h" + +struct PFS_setup_actor; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.SETUP_ACTORS. */ +struct row_setup_actors +{ + /** Column HOST. */ + char m_hostname[HOSTNAME_LENGTH]; + /** Length in bytes of @c m_hostname. */ + uint m_hostname_length; + /** Column USER. */ + char m_username[USERNAME_LENGTH]; + /** Length in bytes of @c m_username. */ + uint m_username_length; + /** Column ROLE. */ + char m_rolename[16]; + /** Length in bytes of @c m_rolename. */ + uint m_rolename_length; + /** Column ENABLED. */ + bool *m_enabled_ptr; + /** Column HISTORY. */ + bool *m_history_ptr; +}; + +/** Table PERFORMANCE_SCHEMA.SETUP_ACTORS. */ +class table_setup_actors : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + /** Table builder. */ + static PFS_engine_table* create(); + static int write_row(TABLE *table, const unsigned char *buf, Field **fields); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + virtual int update_row_values(TABLE *table, + const unsigned char *old_buf, + const unsigned char *new_buf, + Field **fields); + + virtual int delete_row_values(TABLE *table, + const unsigned char *buf, + Field **fields); + + table_setup_actors(); + +public: + ~table_setup_actors() = default; + +private: + void make_row(PFS_setup_actor *actor); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_setup_actors m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_setup_consumers.cc b/storage/perfschema/table_setup_consumers.cc new file mode 100644 index 00000000..3fc4004e --- /dev/null +++ b/storage/perfschema/table_setup_consumers.cc @@ -0,0 +1,282 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_setup_consumers.cc + Table SETUP_CONSUMERS (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "table_setup_consumers.h" +#include "pfs_instr.h" +#include "pfs_events_waits.h" +#include "pfs_digest.h" +#include "field.h" + +#define COUNT_SETUP_CONSUMERS 15 + +static row_setup_consumers all_setup_consumers_data[COUNT_SETUP_CONSUMERS]= +{ + { + { C_STRING_WITH_LEN("events_stages_current") }, + &flag_events_stages_current, + false, + false + }, + { + { C_STRING_WITH_LEN("events_stages_history") }, + &flag_events_stages_history, + false, + true + }, + { + { C_STRING_WITH_LEN("events_stages_history_long") }, + &flag_events_stages_history_long, + false, + true + }, + { + { C_STRING_WITH_LEN("events_statements_current") }, + &flag_events_statements_current, + false, + false + }, + { + { C_STRING_WITH_LEN("events_statements_history") }, + &flag_events_statements_history, + false, + true + }, + { + { C_STRING_WITH_LEN("events_statements_history_long") }, + &flag_events_statements_history_long, + false, + true + }, + { + { C_STRING_WITH_LEN("events_transactions_current") }, + &flag_events_transactions_current, + false, + false + }, + { + { C_STRING_WITH_LEN("events_transactions_history") }, + &flag_events_transactions_history, + false, + true + }, + { + { C_STRING_WITH_LEN("events_transactions_history_long") }, + &flag_events_transactions_history_long, + false, + true + }, + { + { C_STRING_WITH_LEN("events_waits_current") }, + &flag_events_waits_current, + false, + false + }, + { + { C_STRING_WITH_LEN("events_waits_history") }, + &flag_events_waits_history, + false, + true + }, + { + { C_STRING_WITH_LEN("events_waits_history_long") }, + &flag_events_waits_history_long, + false, + true + }, + { + { C_STRING_WITH_LEN("global_instrumentation") }, + &flag_global_instrumentation, + true, + true + }, + { + { C_STRING_WITH_LEN("thread_instrumentation") }, + &flag_thread_instrumentation, + false, + true + }, + { + { C_STRING_WITH_LEN("statements_digest") }, + &flag_statements_digest, + false, + false + } +}; + +THR_LOCK table_setup_consumers::m_table_lock; + +PFS_engine_table_share_state +table_setup_consumers::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_setup_consumers::m_share= +{ + { C_STRING_WITH_LEN("setup_consumers") }, + &pfs_updatable_acl, + table_setup_consumers::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_setup_consumers::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE setup_consumers(" + "NAME VARCHAR(64) not null comment 'Consumer name'," + "ENABLED ENUM ('YES', 'NO') not null comment 'YES or NO for whether or not the consumer is enabled. You can modify this column to ensure that event information is added, or is not added.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_setup_consumers::create(void) +{ + return new table_setup_consumers(); +} + +ha_rows +table_setup_consumers::get_row_count(void) +{ + return COUNT_SETUP_CONSUMERS; +} + +table_setup_consumers::table_setup_consumers() + : PFS_engine_table(&m_share, &m_pos), + m_row(NULL), m_pos(0), m_next_pos(0) +{} + +void table_setup_consumers::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_setup_consumers::rnd_next(void) +{ + int result; + + m_pos.set_at(&m_next_pos); + + if (m_pos.m_index < COUNT_SETUP_CONSUMERS) + { + m_row= &all_setup_consumers_data[m_pos.m_index]; + m_next_pos.set_after(&m_pos); + result= 0; + } + else + { + m_row= NULL; + result= HA_ERR_END_OF_FILE; + } + + return result; +} + +int table_setup_consumers::rnd_pos(const void *pos) +{ + set_position(pos); + assert(m_pos.m_index < COUNT_SETUP_CONSUMERS); + m_row= &all_setup_consumers_data[m_pos.m_index]; + return 0; +} + +int table_setup_consumers::read_row_values(TABLE *table, + unsigned char *, + Field **fields, + bool read_all) +{ + Field *f; + + assert(m_row); + + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* NAME */ + set_field_varchar_utf8(f, m_row->m_name.str,(uint) m_row->m_name.length); + break; + case 1: /* ENABLED */ + set_field_enum(f, (*m_row->m_enabled_ptr) ? ENUM_YES : ENUM_NO); + break; + default: + assert(false); + } + } + } + + return 0; +} + +int table_setup_consumers::update_row_values(TABLE *table, + const unsigned char *, + const unsigned char *, + Field **fields) +{ + Field *f; + enum_yes_no value; + + assert(m_row); + + for (; (f= *fields) ; fields++) + { + if (bitmap_is_set(table->write_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* NAME */ + return HA_ERR_WRONG_COMMAND; + case 1: /* ENABLED */ + { + value= (enum_yes_no) get_field_enum(f); + *m_row->m_enabled_ptr= (value == ENUM_YES) ? true : false; + break; + } + default: + assert(false); + } + } + } + + if (m_row->m_instrument_refresh) + update_instruments_derived_flags(); + + if (m_row->m_thread_refresh) + update_thread_derived_flags(); + + return 0; +} + + diff --git a/storage/perfschema/table_setup_consumers.h b/storage/perfschema/table_setup_consumers.h new file mode 100644 index 00000000..c7386d7a --- /dev/null +++ b/storage/perfschema/table_setup_consumers.h @@ -0,0 +1,95 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_SETUP_CONSUMERS_H +#define TABLE_SETUP_CONSUMERS_H + +/** + @file storage/perfschema/table_setup_consumers.h + Table SETUP_CONSUMERS (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.SETUP_CONSUMERS. */ +struct row_setup_consumers +{ + /** Column NAME. */ + LEX_STRING m_name; + /** Column ENABLED. */ + bool *m_enabled_ptr; + /** Hidden column, instrument refresh. */ + bool m_instrument_refresh; + /** Hidden column, thread refresh. */ + bool m_thread_refresh; +}; + +/** Table PERFORMANCE_SCHEMA.SETUP_CONSUMERS. */ +class table_setup_consumers : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + virtual int update_row_values(TABLE *table, + const unsigned char *old_buf, + const unsigned char *new_buf, + Field **fields); + + table_setup_consumers(); + +public: + ~table_setup_consumers() = default; + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_setup_consumers *m_row; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_setup_instruments.cc b/storage/perfschema/table_setup_instruments.cc new file mode 100644 index 00000000..1e3cd964 --- /dev/null +++ b/storage/perfschema/table_setup_instruments.cc @@ -0,0 +1,379 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_setup_instruments.cc + Table SETUP_INSTRUMENTS (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_builtin_memory.h" +#include "pfs_instr.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_setup_instruments.h" +#include "pfs_global.h" +#include "pfs_setup_object.h" +#include "field.h" + +THR_LOCK table_setup_instruments::m_table_lock; + +PFS_engine_table_share_state +table_setup_instruments::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_setup_instruments::m_share= +{ + { C_STRING_WITH_LEN("setup_instruments") }, + &pfs_updatable_acl, + table_setup_instruments::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_setup_instruments::get_row_count, + sizeof(pos_setup_instruments), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE setup_instruments(" + "NAME VARCHAR(128) not null comment 'Instrument name'," + "ENABLED ENUM ('YES', 'NO') not null comment 'Whether or not the instrument is enabled. It can be disabled, and the instrument will produce no events.'," + "TIMED ENUM ('YES', 'NO') not null comment 'Whether or not the instrument is timed. It can be set, but if disabled, events produced by the instrument will have NULL values for the corresponding TIMER_START, TIMER_END, and TIMER_WAIT values.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_setup_instruments::create(void) +{ + return new table_setup_instruments(); +} + +ha_rows +table_setup_instruments::get_row_count(void) +{ + return wait_class_max + + stage_class_max + + statement_class_max + + transaction_class_max + + memory_class_max; +} + +table_setup_instruments::table_setup_instruments() + : PFS_engine_table(&m_share, &m_pos), + m_pos(), m_next_pos() +{} + +void table_setup_instruments::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_setup_instruments::rnd_next(void) +{ + PFS_instr_class *instr_class= NULL; + PFS_builtin_memory_class *pfs_builtin; + bool update_enabled; + bool update_timed; + + /* Do not advertise hard coded instruments when disabled. */ + if (! pfs_initialized) + return HA_ERR_END_OF_FILE; + + for (m_pos.set_at(&m_next_pos); + m_pos.has_more_view(); + m_pos.next_view()) + { + update_enabled= true; + update_timed= true; + + switch (m_pos.m_index_1) + { + case pos_setup_instruments::VIEW_MUTEX: + instr_class= find_mutex_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_RWLOCK: + instr_class= find_rwlock_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_COND: + instr_class= find_cond_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_THREAD: + /* Not used yet */ + break; + case pos_setup_instruments::VIEW_FILE: + instr_class= find_file_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_TABLE: + instr_class= find_table_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_STAGE: + instr_class= find_stage_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_STATEMENT: + instr_class= find_statement_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_TRANSACTION: + instr_class= find_transaction_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_SOCKET: + instr_class= find_socket_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_IDLE: + instr_class= find_idle_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_BUILTIN_MEMORY: + update_enabled= false; + update_timed= false; + pfs_builtin= find_builtin_memory_class(m_pos.m_index_2); + if (pfs_builtin != NULL) + instr_class= & pfs_builtin->m_class; + else + instr_class= NULL; + break; + case pos_setup_instruments::VIEW_MEMORY: + update_timed= false; + instr_class= find_memory_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_METADATA: + instr_class= find_metadata_class(m_pos.m_index_2); + break; + } + if (instr_class) + { + make_row(instr_class, update_enabled, update_timed); + m_next_pos.set_after(&m_pos); + return 0; + } + } + + return HA_ERR_END_OF_FILE; +} + +int table_setup_instruments::rnd_pos(const void *pos) +{ + PFS_instr_class *instr_class= NULL; + PFS_builtin_memory_class *pfs_builtin; + bool update_enabled; + bool update_timed; + + /* Do not advertise hard coded instruments when disabled. */ + if (! pfs_initialized) + return HA_ERR_END_OF_FILE; + + set_position(pos); + + update_enabled= true; + update_timed= true; + + switch (m_pos.m_index_1) + { + case pos_setup_instruments::VIEW_MUTEX: + instr_class= find_mutex_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_RWLOCK: + instr_class= find_rwlock_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_COND: + instr_class= find_cond_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_THREAD: + /* Not used yet */ + break; + case pos_setup_instruments::VIEW_FILE: + instr_class= find_file_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_TABLE: + instr_class= find_table_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_STAGE: + instr_class= find_stage_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_STATEMENT: + instr_class= find_statement_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_TRANSACTION: + instr_class= find_transaction_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_SOCKET: + instr_class= find_socket_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_IDLE: + instr_class= find_idle_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_BUILTIN_MEMORY: + update_enabled= false; + update_timed= false; + pfs_builtin= find_builtin_memory_class(m_pos.m_index_2); + if (pfs_builtin != NULL) + instr_class= & pfs_builtin->m_class; + else + instr_class= NULL; + break; + case pos_setup_instruments::VIEW_MEMORY: + update_timed= false; + instr_class= find_memory_class(m_pos.m_index_2); + break; + case pos_setup_instruments::VIEW_METADATA: + instr_class= find_metadata_class(m_pos.m_index_2); + break; + } + if (instr_class) + { + make_row(instr_class, update_enabled, update_timed); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_setup_instruments::make_row(PFS_instr_class *klass, bool update_enabled, bool update_timed) +{ + m_row.m_instr_class= klass; + m_row.m_update_enabled= update_enabled; + m_row.m_update_timed= update_timed; +} + +int table_setup_instruments::read_row_values(TABLE *table, + unsigned char *, + Field **fields, + bool read_all) +{ + Field *f; + + assert(table->s->null_bytes == 0); + + /* + The row always exist, the instrument classes + are static and never disappear. + */ + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* NAME */ + set_field_varchar_utf8(f, m_row.m_instr_class->m_name, m_row.m_instr_class->m_name_length); + break; + case 1: /* ENABLED */ + set_field_enum(f, m_row.m_instr_class->m_enabled ? ENUM_YES : ENUM_NO); + break; + case 2: /* TIMED */ + set_field_enum(f, m_row.m_instr_class->m_timed ? ENUM_YES : ENUM_NO); + break; + default: + assert(false); + } + } + } + + return 0; +} + +int table_setup_instruments::update_row_values(TABLE *table, + const unsigned char *, + const unsigned char *, + Field **fields) +{ + Field *f; + enum_yes_no value; + + for (; (f= *fields) ; fields++) + { + if (bitmap_is_set(table->write_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* NAME */ + return HA_ERR_WRONG_COMMAND; + case 1: /* ENABLED */ + /* Do not raise error if m_update_enabled is false, silently ignore. */ + if (m_row.m_update_enabled) + { + value= (enum_yes_no) get_field_enum(f); + m_row.m_instr_class->m_enabled= (value == ENUM_YES) ? true : false; + } + break; + case 2: /* TIMED */ + /* Do not raise error if m_update_timed is false, silently ignore. */ + if (m_row.m_update_timed) + { + value= (enum_yes_no) get_field_enum(f); + m_row.m_instr_class->m_timed= (value == ENUM_YES) ? true : false; + } + break; + default: + assert(false); + } + } + } + + switch (m_pos.m_index_1) + { + case pos_setup_instruments::VIEW_MUTEX: + update_mutex_derived_flags(); + break; + case pos_setup_instruments::VIEW_RWLOCK: + update_rwlock_derived_flags(); + break; + case pos_setup_instruments::VIEW_COND: + update_cond_derived_flags(); + break; + case pos_setup_instruments::VIEW_THREAD: + /* Not used yet */ + break; + case pos_setup_instruments::VIEW_FILE: + update_file_derived_flags(); + break; + case pos_setup_instruments::VIEW_TABLE: + update_table_derived_flags(); + break; + case pos_setup_instruments::VIEW_STAGE: + case pos_setup_instruments::VIEW_STATEMENT: + case pos_setup_instruments::VIEW_TRANSACTION: + /* No flag to update. */ + break; + case pos_setup_instruments::VIEW_SOCKET: + update_socket_derived_flags(); + break; + case pos_setup_instruments::VIEW_IDLE: + /* No flag to update. */ + break; + case pos_setup_instruments::VIEW_BUILTIN_MEMORY: + case pos_setup_instruments::VIEW_MEMORY: + /* No flag to update. */ + break; + case pos_setup_instruments::VIEW_METADATA: + update_metadata_derived_flags(); + break; + default: + assert(false); + break; + } + + return 0; +} + diff --git a/storage/perfschema/table_setup_instruments.h b/storage/perfschema/table_setup_instruments.h new file mode 100644 index 00000000..9ea14285 --- /dev/null +++ b/storage/perfschema/table_setup_instruments.h @@ -0,0 +1,135 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_SETUP_INSTRUMENTS_H +#define TABLE_SETUP_INSTRUMENTS_H + +/** + @file storage/perfschema/table_setup_instruments.h + Table SETUP_INSTRUMENTS (declarations). +*/ + +#include "pfs_instr_class.h" +#include "pfs_engine_table.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.SETUP_INSTRUMENTS. */ +struct row_setup_instruments +{ + /** Columns NAME, ENABLED, TIMED. */ + PFS_instr_class *m_instr_class; + /** True if column ENABLED can be updated. */ + bool m_update_enabled; + /** True if column TIMED can be updated. */ + bool m_update_timed; +}; + +/** Position of a cursor on PERFORMANCE_SCHEMA.SETUP_INSTRUMENTS. */ +struct pos_setup_instruments : public PFS_double_index +{ + static const uint FIRST_VIEW= 1; + static const uint VIEW_MUTEX= 1; + static const uint VIEW_RWLOCK= 2; + static const uint VIEW_COND= 3; + static const uint VIEW_THREAD= 4; + static const uint VIEW_FILE= 5; + static const uint VIEW_TABLE= 6; + static const uint VIEW_STAGE= 7; + static const uint VIEW_STATEMENT= 8; + static const uint VIEW_TRANSACTION=9; + static const uint VIEW_SOCKET= 10; + static const uint VIEW_IDLE= 11; + static const uint VIEW_BUILTIN_MEMORY= 12; + static const uint VIEW_MEMORY= 13; + static const uint VIEW_METADATA= 14; + static const uint LAST_VIEW= 14; + + pos_setup_instruments() + : PFS_double_index(FIRST_VIEW, 1) + {} + + inline void reset(void) + { + m_index_1= FIRST_VIEW; + m_index_2= 1; + } + + inline bool has_more_view(void) + { return (m_index_1 <= LAST_VIEW); } + + inline void next_view(void) + { + m_index_1++; + m_index_2= 1; + } +}; + +/** Table PERFORMANCE_SCHEMA.SETUP_INSTRUMENTS. */ +class table_setup_instruments : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + virtual int update_row_values(TABLE *table, + const unsigned char *old_buf, + const unsigned char *new_buf, + Field **fields); + + table_setup_instruments(); + +public: + ~table_setup_instruments() = default; + +private: + void make_row(PFS_instr_class *klass, bool update_enabled, bool update_timed); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_setup_instruments m_row; + /** Current position. */ + pos_setup_instruments m_pos; + /** Next position. */ + pos_setup_instruments m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_setup_objects.cc b/storage/perfschema/table_setup_objects.cc new file mode 100644 index 00000000..dbe95361 --- /dev/null +++ b/storage/perfschema/table_setup_objects.cc @@ -0,0 +1,340 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_setup_objects.cc + Table SETUP_OBJECTS (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "pfs_setup_object.h" +#include "table_setup_objects.h" +#include "table_helper.h" +#include "pfs_global.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_setup_objects::m_table_lock; +PFS_engine_table_share_state +table_setup_objects::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_setup_objects::m_share= +{ + { C_STRING_WITH_LEN("setup_objects") }, + &pfs_editable_acl, + table_setup_objects::create, + table_setup_objects::write_row, + table_setup_objects::delete_all_rows, + table_setup_objects::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE setup_objects(" + "OBJECT_TYPE ENUM ('EVENT','FUNCTION','PROCEDURE','TABLE','TRIGGER') not null default 'TABLE' comment 'Type of object to instrument.'," + "OBJECT_SCHEMA VARCHAR(64) default '%' comment 'Schema containing the object, either the literal or % for any schema.'," + "OBJECT_NAME VARCHAR(64) not null default '%' comment 'Name of the instrumented object, either the literal or % for any object.'," + "ENABLED ENUM ('YES', 'NO') not null default 'YES' comment 'Whether the object''s events are instrumented or not. Can be disabled, in which case monitoring is not enabled for those objects.'," + "TIMED ENUM ('YES', 'NO') not null default 'YES' comment 'Whether the object''s events are timed or not. Can be modified.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +int update_derived_flags() +{ + PFS_thread *thread= PFS_thread::get_current_thread(); + if (unlikely(thread == NULL)) + return HA_ERR_OUT_OF_MEM; + + update_table_share_derived_flags(thread); + update_program_share_derived_flags(thread); + update_table_derived_flags(); + return 0; +} + +PFS_engine_table* table_setup_objects::create(void) +{ + return new table_setup_objects(); +} + +int table_setup_objects::write_row(TABLE *table, const unsigned char *buf, + Field **fields) +{ + int result; + Field *f; + enum_object_type object_type= OBJECT_TYPE_TABLE; + String object_schema_data("%", 1, &my_charset_utf8mb3_bin); + String object_name_data("%", 1, &my_charset_utf8mb3_bin); + String *object_schema= &object_schema_data; + String *object_name= &object_name_data; + enum_yes_no enabled_value= ENUM_YES; + enum_yes_no timed_value= ENUM_YES; + bool enabled= true; + bool timed= true; + + for (; (f= *fields) ; fields++) + { + if (bitmap_is_set(table->write_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* OBJECT_TYPE */ + object_type= (enum_object_type) get_field_enum(f); + break; + case 1: /* OBJECT_SCHEMA */ + object_schema= get_field_varchar_utf8(f, &object_schema_data); + break; + case 2: /* OBJECT_NAME */ + object_name= get_field_varchar_utf8(f, &object_name_data); + break; + case 3: /* ENABLED */ + enabled_value= (enum_yes_no) get_field_enum(f); + break; + case 4: /* TIMED */ + timed_value= (enum_yes_no) get_field_enum(f); + break; + default: + assert(false); + } + } + } + + /* Reject illegal enum values in OBJECT_TYPE */ + if (object_type < FIRST_OBJECT_TYPE || + object_type > LAST_OBJECT_TYPE || + object_type == OBJECT_TYPE_TEMPORARY_TABLE) + return HA_ERR_NO_REFERENCED_ROW; + + /* Reject illegal enum values in ENABLED */ + if ((enabled_value != ENUM_YES) && (enabled_value != ENUM_NO)) + return HA_ERR_NO_REFERENCED_ROW; + + /* Reject illegal enum values in TIMED */ + if ((timed_value != ENUM_YES) && (timed_value != ENUM_NO)) + return HA_ERR_NO_REFERENCED_ROW; + + enabled= (enabled_value == ENUM_YES) ? true : false; + timed= (timed_value == ENUM_YES) ? true : false; + + result= insert_setup_object(object_type, object_schema, object_name, + enabled, timed); + if (result == 0) + result= update_derived_flags(); + return result; +} + +int table_setup_objects::delete_all_rows(void) +{ + int result= reset_setup_object(); + if (result == 0) + result= update_derived_flags(); + return result; +} + +ha_rows table_setup_objects::get_row_count(void) +{ + return global_setup_object_container.get_row_count(); +} + +table_setup_objects::table_setup_objects() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_setup_objects::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_setup_objects::rnd_next(void) +{ + PFS_setup_object *pfs; + + m_pos.set_at(&m_next_pos); + PFS_setup_object_iterator it= global_setup_object_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_setup_objects::rnd_pos(const void *pos) +{ + PFS_setup_object *pfs; + + set_position(pos); + + pfs= global_setup_object_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_setup_objects::make_row(PFS_setup_object *pfs) +{ + pfs_optimistic_state lock; + + m_row_exists= false; + + pfs->m_lock.begin_optimistic_lock(&lock); + + m_row.m_object_type= pfs->get_object_type(); + memcpy(m_row.m_schema_name, pfs->m_schema_name, pfs->m_schema_name_length); + m_row.m_schema_name_length= pfs->m_schema_name_length; + memcpy(m_row.m_object_name, pfs->m_object_name, pfs->m_object_name_length); + m_row.m_object_name_length= pfs->m_object_name_length; + m_row.m_enabled_ptr= &pfs->m_enabled; + m_row.m_timed_ptr= &pfs->m_timed; + + if (pfs->m_lock.end_optimistic_lock(&lock)) + m_row_exists= true; +} + +int table_setup_objects::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* OBJECT_TYPE */ + set_field_enum(f, m_row.m_object_type); + break; + case 1: /* OBJECT_SCHEMA */ + if (m_row.m_schema_name_length) + set_field_varchar_utf8(f, m_row.m_schema_name, + m_row.m_schema_name_length); + else + f->set_null(); + break; + case 2: /* OBJECT_NAME */ + if (m_row.m_object_name_length) + set_field_varchar_utf8(f, m_row.m_object_name, + m_row.m_object_name_length); + else + f->set_null(); + break; + case 3: /* ENABLED */ + set_field_enum(f, (*m_row.m_enabled_ptr) ? ENUM_YES : ENUM_NO); + break; + case 4: /* TIMED */ + set_field_enum(f, (*m_row.m_timed_ptr) ? ENUM_YES : ENUM_NO); + break; + default: + assert(false); + } + } + } + + return 0; +} + +int table_setup_objects::update_row_values(TABLE *table, + const unsigned char *, + const unsigned char *, + Field **fields) +{ + int result; + Field *f; + enum_yes_no value; + + for (; (f= *fields) ; fields++) + { + if (bitmap_is_set(table->write_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* OBJECT_TYPE */ + case 1: /* OBJECT_SCHEMA */ + case 2: /* OBJECT_NAME */ + return HA_ERR_WRONG_COMMAND; + case 3: /* ENABLED */ + value= (enum_yes_no) get_field_enum(f); + /* Reject illegal enum values in ENABLED */ + if ((value != ENUM_YES) && (value != ENUM_NO)) + return HA_ERR_NO_REFERENCED_ROW; + *m_row.m_enabled_ptr= (value == ENUM_YES) ? true : false; + break; + case 4: /* TIMED */ + value= (enum_yes_no) get_field_enum(f); + /* Reject illegal enum values in TIMED */ + if ((value != ENUM_YES) && (value != ENUM_NO)) + return HA_ERR_NO_REFERENCED_ROW; + *m_row.m_timed_ptr= (value == ENUM_YES) ? true : false; + break; + default: + assert(false); + } + } + } + + result= update_derived_flags(); + return result; +} + +int table_setup_objects::delete_row_values(TABLE *table, + const unsigned char *buf, + Field **fields) +{ + assert(m_row_exists); + + CHARSET_INFO *cs= &my_charset_utf8mb3_bin; + enum_object_type object_type= OBJECT_TYPE_TABLE; + String object_schema(m_row.m_schema_name, m_row.m_schema_name_length, cs); + String object_name(m_row.m_object_name, m_row.m_object_name_length, cs); + + int result= delete_setup_object(object_type, &object_schema, &object_name); + + if (result == 0) + result= update_derived_flags(); + return result; +} + diff --git a/storage/perfschema/table_setup_objects.h b/storage/perfschema/table_setup_objects.h new file mode 100644 index 00000000..32853b36 --- /dev/null +++ b/storage/perfschema/table_setup_objects.h @@ -0,0 +1,114 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_SETUP_OBJECTS_H +#define TABLE_SETUP_OBJECTS_H + +/** + @file storage/perfschema/table_setup_objects.h + Table SETUP_OBJECTS (declarations). +*/ + +#include "pfs_engine_table.h" +#include "table_helper.h" + +struct PFS_setup_object; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.SETUP_OBJECTS. */ +struct row_setup_objects +{ + /** Column OBJECT_TYPE. */ + enum_object_type m_object_type; + /** Column SCHEMA_NAME. */ + char m_schema_name[NAME_LEN]; + /** Length in bytes of @c m_schema_name. */ + uint m_schema_name_length; + /** Column OBJECT_NAME. */ + char m_object_name[NAME_LEN]; + /** Length in bytes of @c m_object_name. */ + uint m_object_name_length; + /** Column ENABLED. */ + bool *m_enabled_ptr; + /** Column TIMED. */ + bool *m_timed_ptr; +}; + +/** Table PERFORMANCE_SCHEMA.SETUP_OBJECTS. */ +class table_setup_objects : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + /** Table builder. */ + static PFS_engine_table* create(); + static int write_row(TABLE *table, const unsigned char *buf, Field **fields); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + virtual int update_row_values(TABLE *table, + const unsigned char *old_buf, + const unsigned char *new_buf, + Field **fields); + + virtual int delete_row_values(TABLE *table, + const unsigned char *buf, + Field **fields); + + table_setup_objects(); + +public: + ~table_setup_objects() = default; + +private: + void make_row(PFS_setup_object *pfs); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_setup_objects m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_setup_timers.cc b/storage/perfschema/table_setup_timers.cc new file mode 100644 index 00000000..3c6f8451 --- /dev/null +++ b/storage/perfschema/table_setup_timers.cc @@ -0,0 +1,212 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_setup_timers.cc + Table SETUP_TIMERS (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "table_setup_timers.h" +#include "pfs_column_values.h" +#include "pfs_timer.h" +#include "field.h" +#include "derror.h" /* ER_THD */ + +#define COUNT_SETUP_TIMERS 5 + +static row_setup_timers all_setup_timers_data[COUNT_SETUP_TIMERS]= +{ + { + { C_STRING_WITH_LEN("idle") }, + &idle_timer + }, + { + { C_STRING_WITH_LEN("wait") }, + &wait_timer + }, + { + { C_STRING_WITH_LEN("stage") }, + &stage_timer + }, + { + { C_STRING_WITH_LEN("statement") }, + &statement_timer + }, + { + { C_STRING_WITH_LEN("transaction") }, + &transaction_timer + } +}; + +THR_LOCK table_setup_timers::m_table_lock; + +PFS_engine_table_share_state +table_setup_timers::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_setup_timers::m_share= +{ + { C_STRING_WITH_LEN("setup_timers") }, + &pfs_updatable_acl, + table_setup_timers::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_setup_timers::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE setup_timers(" + "NAME VARCHAR(64) not null comment 'Type of instrument the timer is used for.'," + "TIMER_NAME ENUM ('CYCLE', 'NANOSECOND', 'MICROSECOND', 'MILLISECOND', 'TICK') not null comment 'Timer applying to the instrument type. Can be modified.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_setup_timers::create(void) +{ + THD *thd = current_thd; + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_WARN, + ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT, + ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT), + "performance_schema.setup_timers"); + + return new table_setup_timers(); +} + +ha_rows +table_setup_timers::get_row_count(void) +{ + return COUNT_SETUP_TIMERS; +} + +table_setup_timers::table_setup_timers() + : PFS_engine_table(&m_share, &m_pos), + m_row(NULL), m_pos(0), m_next_pos(0) +{} + +void table_setup_timers::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_setup_timers::rnd_next(void) +{ + int result; + + m_pos.set_at(&m_next_pos); + + if (m_pos.m_index < COUNT_SETUP_TIMERS) + { + m_row= &all_setup_timers_data[m_pos.m_index]; + m_next_pos.set_after(&m_pos); + result= 0; + } + else + { + m_row= NULL; + result= HA_ERR_END_OF_FILE; + } + + return result; +} + +int table_setup_timers::rnd_pos(const void *pos) +{ + set_position(pos); + assert(m_pos.m_index < COUNT_SETUP_TIMERS); + m_row= &all_setup_timers_data[m_pos.m_index]; + return 0; +} + +int table_setup_timers::read_row_values(TABLE *table, + unsigned char *, + Field **fields, + bool read_all) +{ + Field *f; + + assert(m_row); + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* NAME */ + set_field_varchar_utf8(f, m_row->m_name.str,(uint) m_row->m_name.length); + break; + case 1: /* TIMER_NAME */ + set_field_enum(f, *(m_row->m_timer_name_ptr)); + break; + default: + assert(false); + } + } + } + + return 0; +} + +int table_setup_timers::update_row_values(TABLE *table, + const unsigned char *, + const unsigned char *, + Field **fields) +{ + Field *f; + longlong value; + + assert(m_row); + + for (; (f= *fields) ; fields++) + { + if (bitmap_is_set(table->write_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* NAME */ + return HA_ERR_WRONG_COMMAND; + case 1: /* TIMER_NAME */ + value= get_field_enum(f); + if ((value >= FIRST_TIMER_NAME) && (value <= LAST_TIMER_NAME)) + *(m_row->m_timer_name_ptr)= (enum_timer_name) value; + else + return HA_ERR_WRONG_COMMAND; + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_setup_timers.h b/storage/perfschema/table_setup_timers.h new file mode 100644 index 00000000..e606fcbf --- /dev/null +++ b/storage/perfschema/table_setup_timers.h @@ -0,0 +1,91 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_SETUP_TIMERS_H +#define TABLE_SETUP_TIMERS_H + +/** + @file storage/perfschema/table_setup_timers.h + Table SETUP_TIMERS (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of table PERFORMANCE_SCHEMA.SETUP_TIMERS. */ +struct row_setup_timers +{ + /** Column NAME. */ + LEX_STRING m_name; + /** Column TIMER_NAME. */ + enum_timer_name *m_timer_name_ptr; +}; + +/** Table PERFORMANCE_SCHEMA.SETUP_TIMERS. */ +class table_setup_timers : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + virtual int update_row_values(TABLE *table, + const unsigned char *old_buf, + const unsigned char *new_buf, + Field **fields); + + table_setup_timers(); + +public: + ~table_setup_timers() = default; + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_setup_timers *m_row; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_socket_instances.cc b/storage/perfschema/table_socket_instances.cc new file mode 100644 index 00000000..2435d19d --- /dev/null +++ b/storage/perfschema/table_socket_instances.cc @@ -0,0 +1,215 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_socket_instances.cc + Table SOCKET_INSTANCES (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_socket_instances.h" +#include "pfs_global.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_socket_instances::m_table_lock; + +PFS_engine_table_share_state +table_socket_instances::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_socket_instances::m_share= +{ + { C_STRING_WITH_LEN("socket_instances") }, + &pfs_readonly_acl, + table_socket_instances::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_socket_instances::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE socket_instances(" + "EVENT_NAME VARCHAR(128) not null comment 'NAME from the setup_instruments table, and the name of the wait/io/socket/* instrument that produced the event.'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null comment 'Memory address of the object.'," + "THREAD_ID BIGINT unsigned comment 'Thread identifier that the server assigns to each socket.'," + "SOCKET_ID INTEGER not null comment 'The socket''s internal file handle.'," + "IP VARCHAR(64) not null comment 'Client IP address. Blank for Unix socket file, otherwise an IPv4 or IPv6 address. Together with the PORT identifies the connection.'," + "PORT INTEGER not null comment 'TCP/IP port number, from 0 to 65535. Together with the IP identifies the connection.'," + "STATE ENUM('IDLE','ACTIVE') not null comment 'Socket status, either IDLE if waiting to receive a request from a client, or ACTIVE')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_socket_instances::create(void) +{ + return new table_socket_instances(); +} + +ha_rows +table_socket_instances::get_row_count(void) +{ + return global_socket_container.get_row_count(); +} + +table_socket_instances::table_socket_instances() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_socket_instances::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_socket_instances::rnd_next(void) +{ + PFS_socket *pfs; + + m_pos.set_at(&m_next_pos); + PFS_socket_iterator it= global_socket_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_socket_instances::rnd_pos(const void *pos) +{ + PFS_socket *pfs; + + set_position(pos); + + pfs= global_socket_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_socket_instances::make_row(PFS_socket *pfs) +{ + pfs_optimistic_state lock; + PFS_socket_class *safe_class; + + m_row_exists= false; + + /* Protect this reader against a socket delete */ + pfs->m_lock.begin_optimistic_lock(&lock); + + safe_class= sanitize_socket_class(pfs->m_class); + if (unlikely(safe_class == NULL)) + return; + + /** Extract ip address and port from raw address */ + m_row.m_ip_length= pfs_get_socket_address(m_row.m_ip, sizeof(m_row.m_ip), + &m_row.m_port, + &pfs->m_sock_addr, pfs->m_addr_len); + m_row.m_event_name= safe_class->m_name; + m_row.m_event_name_length= safe_class->m_name_length; + m_row.m_identity= pfs->m_identity; + m_row.m_fd= pfs->m_fd; + m_row.m_state= (pfs->m_idle ? PSI_SOCKET_STATE_IDLE + : PSI_SOCKET_STATE_ACTIVE); + PFS_thread *safe_thread= sanitize_thread(pfs->m_thread_owner); + + if (safe_thread != NULL) + { + m_row.m_thread_id= safe_thread->m_thread_internal_id; + m_row.m_thread_id_set= true; + } + else + m_row.m_thread_id_set= false; + + + if (pfs->m_lock.end_optimistic_lock(&lock)) + m_row_exists= true; +} + +int table_socket_instances::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(!m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* EVENT_NAME */ + set_field_varchar_utf8(f, m_row.m_event_name, m_row.m_event_name_length); + break; + case 1: /* OBJECT_INSTANCE_BEGIN */ + set_field_ulonglong(f, (intptr)m_row.m_identity); + break; + case 2: /* THREAD_ID */ + if (m_row.m_thread_id_set) + set_field_ulonglong(f, m_row.m_thread_id); + else + f->set_null(); + break; + case 3: /* SOCKET_ID */ + set_field_ulong(f, m_row.m_fd); + break; + case 4: /* IP */ + set_field_varchar_utf8(f, m_row.m_ip, m_row.m_ip_length); + break; + case 5: /* PORT */ + set_field_ulong(f, m_row.m_port); + break; + case 6: /* STATE */ + set_field_enum(f, m_row.m_state); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_socket_instances.h b/storage/perfschema/table_socket_instances.h new file mode 100644 index 00000000..c8a4a5b6 --- /dev/null +++ b/storage/perfschema/table_socket_instances.h @@ -0,0 +1,108 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_SOCKET_INSTANCES_H +#define TABLE_SOCKET_INSTANCES_H + +/** + @file storage/perfschema/table_socket_instances.h + Table SOCKET_INSTANCES (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.SOCKET_INSTANCES. */ +struct row_socket_instances +{ + /** Column EVENT_NAME. */ + const char *m_event_name; + /** Length in bytes of @c m_event_name. */ + uint m_event_name_length; + /** Column OBJECT_INSTANCE_BEGIN */ + const void *m_identity; + /** Column THREAD_ID */ + ulonglong m_thread_id; + /** True if thread_is is set */ + bool m_thread_id_set; + /** Column SOCKET_ID */ + uint m_fd; + /** Socket ip address, IPV4 or IPV6 */ + char m_ip[INET6_ADDRSTRLEN+1]; + /** Length in bytes of @c m_ip. */ + uint m_ip_length; + /** Column PORT */ + uint m_port; + /** Socket state: ACTIVE or IDLE */ + PSI_socket_state m_state; + + row_socket_instances() {m_thread_id_set= false;} +}; + +/** Table PERFORMANCE_SCHEMA.SOCKET_INSTANCES. */ +class table_socket_instances : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_socket_instances(); + +public: + ~table_socket_instances() = default; + +private: + void make_row(PFS_socket *pfs); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_socket_instances m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_socket_summary_by_event_name.cc b/storage/perfschema/table_socket_summary_by_event_name.cc new file mode 100644 index 00000000..222294cf --- /dev/null +++ b/storage/perfschema/table_socket_summary_by_event_name.cc @@ -0,0 +1,262 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_socket_summary_by_event_name.cc + Table SOCKET_EVENT_NAMES (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_socket_summary_by_event_name.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "field.h" + +THR_LOCK table_socket_summary_by_event_name::m_table_lock; + +PFS_engine_table_share_state +table_socket_summary_by_event_name::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_socket_summary_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("socket_summary_by_event_name") }, + &pfs_readonly_acl, + table_socket_summary_by_event_name::create, + NULL, /* write_row */ + table_socket_summary_by_event_name::delete_all_rows, + table_socket_summary_by_event_name::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE socket_summary_by_event_name(" + "EVENT_NAME VARCHAR(128) not null comment 'Socket instrument.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.'," + "COUNT_READ BIGINT unsigned not null comment 'Number of all read operations, including RECV, RECVFROM, and RECVMSG.'," + "SUM_TIMER_READ BIGINT unsigned not null comment 'Total wait time of all read operations that are timed.'," + "MIN_TIMER_READ BIGINT unsigned not null comment 'Minimum wait time of all read operations that are timed.'," + "AVG_TIMER_READ BIGINT unsigned not null comment 'Average wait time of all read operations that are timed.'," + "MAX_TIMER_READ BIGINT unsigned not null comment 'Maximum wait time of all read operations that are timed.'," + "SUM_NUMBER_OF_BYTES_READ BIGINT unsigned not null comment 'Bytes read by read operations.'," + "COUNT_WRITE BIGINT unsigned not null comment 'Number of all write operations, including SEND, SENDTO, and SENDMSG.'," + "SUM_TIMER_WRITE BIGINT unsigned not null comment 'Total wait time of all write operations that are timed.'," + "MIN_TIMER_WRITE BIGINT unsigned not null comment 'Minimum wait time of all write operations that are timed.'," + "AVG_TIMER_WRITE BIGINT unsigned not null comment 'Average wait time of all write operations that are timed.'," + "MAX_TIMER_WRITE BIGINT unsigned not null comment 'Maximum wait time of all write operations that are timed.'," + "SUM_NUMBER_OF_BYTES_WRITE BIGINT unsigned not null comment 'Bytes written by write operations.'," + "COUNT_MISC BIGINT unsigned not null comment 'Number of all miscellaneous operations not counted above, including CONNECT, LISTEN, ACCEPT, CLOSE, and SHUTDOWN.'," + "SUM_TIMER_MISC BIGINT unsigned not null comment 'Total wait time of all miscellaneous operations that are timed.'," + "MIN_TIMER_MISC BIGINT unsigned not null comment 'Minimum wait time of all miscellaneous operations that are timed.'," + "AVG_TIMER_MISC BIGINT unsigned not null comment 'Average wait time of all miscellaneous operations that are timed.'," + "MAX_TIMER_MISC BIGINT unsigned not null comment 'Maximum wait time of all miscellaneous operations that are timed.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_socket_summary_by_event_name::create(void) +{ + return new table_socket_summary_by_event_name(); +} + +table_socket_summary_by_event_name::table_socket_summary_by_event_name() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(1), m_next_pos(1) +{} + +int table_socket_summary_by_event_name::delete_all_rows(void) +{ + reset_socket_instance_io(); + reset_socket_class_io(); + return 0; +} + +ha_rows +table_socket_summary_by_event_name::get_row_count(void) +{ + return socket_class_max; +} + +void table_socket_summary_by_event_name::reset_position(void) +{ + m_pos.m_index= 1; + m_next_pos.m_index= 1; +} + +int table_socket_summary_by_event_name::rnd_next(void) +{ + PFS_socket_class *socket_class; + + m_pos.set_at(&m_next_pos); + + socket_class= find_socket_class(m_pos.m_index); + if (socket_class) + { + make_row(socket_class); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_socket_summary_by_event_name::rnd_pos(const void *pos) +{ + PFS_socket_class *socket_class; + + set_position(pos); + + socket_class= find_socket_class(m_pos.m_index); + if (socket_class) + { + make_row(socket_class); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_socket_summary_by_event_name::make_row(PFS_socket_class *socket_class) +{ + m_row.m_event_name.make_row(socket_class); + + PFS_instance_socket_io_stat_visitor visitor; + PFS_instance_iterator::visit_socket_instances(socket_class, &visitor); + + time_normalizer *normalizer= time_normalizer::get(wait_timer); + + /* Collect timer and byte count stats */ + m_row.m_io_stat.set(normalizer, &visitor.m_socket_io_stat); + m_row_exists= true; +} + +int table_socket_summary_by_event_name::read_row_values(TABLE *table, + unsigned char *, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(!m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + case 1: /* COUNT_STAR */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_count); + break; + case 2: /* SUM_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_sum); + break; + case 3: /* MIN_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_min); + break; + case 4: /* AVG_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_avg); + break; + case 5: /* MAX_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_max); + break; + + case 6: /* COUNT_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_count); + break; + case 7: /* SUM_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_sum); + break; + case 8: /* MIN_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_min); + break; + case 9: /* AVG_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_avg); + break; + case 10: /* MAX_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_max); + break; + case 11: /* SUM_NUMBER_OF_BYTES_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_bytes); + break; + + case 12: /* COUNT_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_count); + break; + case 13: /* SUM_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_sum); + break; + case 14: /* MIN_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_min); + break; + case 15: /* AVG_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_avg); + break; + case 16: /* MAX_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_max); + break; + case 17: /* SUM_NUMBER_OF_BYTES_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_bytes); + break; + + case 18: /* COUNT_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_count); + break; + case 19: /* SUM_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_sum); + break; + case 20: /* MIN_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_min); + break; + case 21: /* AVG_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_avg); + break; + case 22: /* MAX_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_max); + break; + + default: + assert(false); + break; + } + } // if + } // for + + return 0; +} + diff --git a/storage/perfschema/table_socket_summary_by_event_name.h b/storage/perfschema/table_socket_summary_by_event_name.h new file mode 100644 index 00000000..726d16ab --- /dev/null +++ b/storage/perfschema/table_socket_summary_by_event_name.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_SOCKET_SUMMARY_BY_EVENT_NAME_H +#define TABLE_SOCKET_SUMMARY_BY_EVENT_NAME_H + +/** + @file storage/perfschema/table_socket_summary_by_event_name.h + Table SOCKET_SUMMARY_BY_EVENT_NAME (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.SOCKET_SUMMARY_BY_EVENT_NAME. +*/ +struct row_socket_summary_by_event_name +{ + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER and NUMBER_OF_BYTES for each operation. */ + PFS_socket_io_stat_row m_io_stat; +}; + +/** Table PERFORMANCE_SCHEMA.SOCKET_SUMMARY_BY_EVENT_NAME. */ +class table_socket_summary_by_event_name : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_socket_summary_by_event_name(); + +public: + ~table_socket_summary_by_event_name() = default; + +private: + void make_row(PFS_socket_class *socket_class); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_socket_summary_by_event_name m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_socket_summary_by_instance.cc b/storage/perfschema/table_socket_summary_by_instance.cc new file mode 100644 index 00000000..eb0568c0 --- /dev/null +++ b/storage/perfschema/table_socket_summary_by_instance.cc @@ -0,0 +1,278 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_socket_summary_by_instance.cc + Table SOCKET_SUMMARY_BY_INSTANCE (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_socket_summary_by_instance.h" +#include "pfs_global.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_socket_summary_by_instance::m_table_lock; + +PFS_engine_table_share_state +table_socket_summary_by_instance::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_socket_summary_by_instance::m_share= +{ + { C_STRING_WITH_LEN("socket_summary_by_instance") }, + &pfs_readonly_acl, + table_socket_summary_by_instance::create, + NULL, /* write_row */ + table_socket_summary_by_instance::delete_all_rows, + table_socket_summary_by_instance::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE socket_summary_by_instance(" + "EVENT_NAME VARCHAR(128) not null comment 'Socket instrument.'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null comment 'Address in memory.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.'," + "COUNT_READ BIGINT unsigned not null comment 'Number of all read operations, including RECV, RECVFROM, and RECVMSG.'," + "SUM_TIMER_READ BIGINT unsigned not null comment 'Total wait time of all read operations that are timed.'," + "MIN_TIMER_READ BIGINT unsigned not null comment 'Minimum wait time of all read operations that are timed.'," + "AVG_TIMER_READ BIGINT unsigned not null comment 'Average wait time of all read operations that are timed.'," + "MAX_TIMER_READ BIGINT unsigned not null comment 'Maximum wait time of all read operations that are timed.'," + "SUM_NUMBER_OF_BYTES_READ BIGINT unsigned not null comment 'Bytes read by read operations.'," + "COUNT_WRITE BIGINT unsigned not null comment 'Number of all write operations, including SEND, SENDTO, and SENDMSG.'," + "SUM_TIMER_WRITE BIGINT unsigned not null comment 'Total wait time of all write operations that are timed.'," + "MIN_TIMER_WRITE BIGINT unsigned not null comment 'Minimum wait time of all write operations that are timed.'," + "AVG_TIMER_WRITE BIGINT unsigned not null comment 'Average wait time of all write operations that are timed.'," + "MAX_TIMER_WRITE BIGINT unsigned not null comment 'Maximum wait time of all write operations that are timed.'," + "SUM_NUMBER_OF_BYTES_WRITE BIGINT unsigned not null comment 'Bytes written by write operations.'," + "COUNT_MISC BIGINT unsigned not null comment 'Number of all miscellaneous operations not counted above, including CONNECT, LISTEN, ACCEPT, CLOSE, and SHUTDOWN.'," + "SUM_TIMER_MISC BIGINT unsigned not null comment 'Total wait time of all miscellaneous operations that are timed.'," + "MIN_TIMER_MISC BIGINT unsigned not null comment 'Minimum wait time of all miscellaneous operations that are timed.'," + "AVG_TIMER_MISC BIGINT unsigned not null comment 'Average wait time of all miscellaneous operations that are timed.'," + "MAX_TIMER_MISC BIGINT unsigned not null comment 'Maximum wait time of all miscellaneous operations that are timed.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_socket_summary_by_instance::create(void) +{ + return new table_socket_summary_by_instance(); +} + +table_socket_summary_by_instance::table_socket_summary_by_instance() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +int table_socket_summary_by_instance::delete_all_rows(void) +{ + reset_socket_instance_io(); + return 0; +} + +ha_rows +table_socket_summary_by_instance::get_row_count(void) +{ + return global_socket_container.get_row_count(); +} + +void table_socket_summary_by_instance::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_socket_summary_by_instance::rnd_next(void) +{ + PFS_socket *pfs; + + m_pos.set_at(&m_next_pos); + PFS_socket_iterator it= global_socket_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_socket_summary_by_instance::rnd_pos(const void *pos) +{ + PFS_socket *pfs; + + set_position(pos); + + pfs= global_socket_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_socket_summary_by_instance::make_row(PFS_socket *pfs) +{ + pfs_optimistic_state lock; + PFS_socket_class *safe_class; + + m_row_exists= false; + + /* Protect this reader against a socket delete */ + pfs->m_lock.begin_optimistic_lock(&lock); + + safe_class= sanitize_socket_class(pfs->m_class); + if (unlikely(safe_class == NULL)) + return; + + m_row.m_event_name.make_row(safe_class); + m_row.m_identity= pfs->m_identity; + + time_normalizer *normalizer= time_normalizer::get(wait_timer); + + /* Collect timer and byte count stats */ + m_row.m_io_stat.set(normalizer, &pfs->m_socket_stat.m_io_stat); + + if (!pfs->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; +} + +int table_socket_summary_by_instance::read_row_values(TABLE *table, + unsigned char *, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(!m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* EVENT_NAME */ + m_row.m_event_name.set_field(f); + break; + case 1: /* OBJECT_INSTANCE */ + set_field_ulonglong(f, (intptr)m_row.m_identity); + break; + + case 2:/* COUNT_STAR */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_count); + break; + case 3:/* SUM_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_sum); + break; + case 4: /* MIN_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_min); + break; + case 5: /* AVG_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_avg); + break; + case 6: /* MAX_TIMER_WAIT */ + set_field_ulonglong(f, m_row.m_io_stat.m_all.m_waits.m_max); + break; + + case 7: /* COUNT_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_count); + break; + case 8: /* SUM_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_sum); + break; + case 9: /* MIN_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_min); + break; + case 10: /* AVG_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_avg); + break; + case 11: /* MAX_TIMER_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_waits.m_max); + break; + case 12: /* SUM_NUMBER_OF_BYTES_READ */ + set_field_ulonglong(f, m_row.m_io_stat.m_read.m_bytes); + break; + + case 13: /* COUNT_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_count); + break; + case 14: /* SUM_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_sum); + break; + case 15: /* MIN_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_min); + break; + case 16: /* AVG_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_avg); + break; + case 17: /* MAX_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_waits.m_max); + break; + case 18: /* SUM_NUMBER_OF_BYTES_WRITE */ + set_field_ulonglong(f, m_row.m_io_stat.m_write.m_bytes); + break; + + case 19: /* COUNT_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_count); + break; + case 20: /* SUM_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_sum); + break; + case 21: /* MIN_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_min); + break; + case 22: /* AVG_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_avg); + break; + case 23: /* MAX_TIMER_MISC */ + set_field_ulonglong(f, m_row.m_io_stat.m_misc.m_waits.m_max); + break; + default: + assert(false); + break; + } + } + } + + return 0; +} diff --git a/storage/perfschema/table_socket_summary_by_instance.h b/storage/perfschema/table_socket_summary_by_instance.h new file mode 100644 index 00000000..21e811ba --- /dev/null +++ b/storage/perfschema/table_socket_summary_by_instance.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_SOCKET_SUMMARY_BY_INSTANCE_H +#define TABLE_SOCKET_SUMMARY_BY_INSTANCE_H + +/** + @file storage/perfschema/table_socket_summary_by_instance.h + Table SOCKET_SUMMARY_BY_INSTANCE (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.SOCKET_SUMMARY_BY_INSTANCE. +*/ +struct row_socket_summary_by_instance +{ + /** Column EVENT_NAME. */ + PFS_event_name_row m_event_name; + + /** Column OBJECT_INSTANCE_BEGIN */ + const void *m_identity; + + /** Columns COUNT_STAR, SUM/MIN/AVG/MAX TIMER and NUMBER_OF_BYTES for each operation. */ + PFS_socket_io_stat_row m_io_stat; +}; + +/** Table PERFORMANCE_SCHEMA.SOCKET_SUMMARY_BY_INSTANCE. */ +class table_socket_summary_by_instance : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_socket_summary_by_instance(); + +public: + ~table_socket_summary_by_instance() = default; + +private: + void make_row(PFS_socket *pfs); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_socket_summary_by_instance m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_status_by_account.cc b/storage/perfschema/table_status_by_account.cc new file mode 100644 index 00000000..55a6fc10 --- /dev/null +++ b/storage/perfschema/table_status_by_account.cc @@ -0,0 +1,253 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_status_by_account.cc + Table STATUS_BY_ACCOUNT (implementation). +*/ + +#include "my_global.h" +#include "table_status_by_account.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "pfs_global.h" +#include "pfs_account.h" + +THR_LOCK table_status_by_account::m_table_lock; + +PFS_engine_table_share_state +table_status_by_account::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_status_by_account::m_share= +{ + { C_STRING_WITH_LEN("status_by_account") }, + &pfs_truncatable_acl, + table_status_by_account::create, + NULL, /* write_row */ + table_status_by_account::delete_all_rows, + table_status_by_account::get_row_count, + sizeof(pos_t), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE status_by_account(" + "USER CHAR(32) collate utf8_bin default null comment 'User for which the status variable is reported.'," + "HOST CHAR(60) collate utf8_bin default null comment 'Host for which the status variable is reported.'," + "VARIABLE_NAME VARCHAR(64) not null comment 'Status variable name.'," + "VARIABLE_VALUE VARCHAR(1024) comment 'Aggregated status variable value.' )") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_status_by_account::create(void) +{ + return new table_status_by_account(); +} + +int table_status_by_account::delete_all_rows(void) +{ + mysql_mutex_lock(&LOCK_status); + reset_status_by_thread(); + reset_status_by_account(); + mysql_mutex_unlock(&LOCK_status); + return 0; +} + +ha_rows table_status_by_account::get_row_count(void) +{ + mysql_mutex_lock(&LOCK_status); + size_t status_var_count= all_status_vars.elements; + mysql_mutex_unlock(&LOCK_status); + return (global_account_container.get_row_count() * status_var_count); +} + +table_status_by_account::table_status_by_account() + : PFS_engine_table(&m_share, &m_pos), + m_status_cache(true), m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_status_by_account::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_status_by_account::rnd_init(bool scan) +{ + if (show_compatibility_56) + return 0; + + /* + Build array of SHOW_VARs from the global status array prior to materializing + threads in rnd_next() or rnd_pos(). + */ + m_status_cache.initialize_client_session(); + + /* Use the current number of status variables to detect changes. */ + ulonglong status_version= m_status_cache.get_status_array_version(); + + /* + The table context holds the current version of the global status array + and a record of which accounts were materialized. If scan == true, then + allocate a new context from mem_root and store in TLS. If scan == false, + then restore from TLS. + */ + m_context= (table_status_by_account_context *)current_thd->alloc(sizeof(table_status_by_account_context)); + new(m_context) table_status_by_account_context(status_version, !scan); + return 0; +} + +int table_status_by_account::rnd_next(void) +{ + if (show_compatibility_56) + return HA_ERR_END_OF_FILE; + + /* If status array changes, exit with warning. */ // TODO: Issue warning + if (!m_context->versions_match()) + return HA_ERR_END_OF_FILE; + + /* + For each account, build a cache of status variables using totals from all + threads associated with the account. + */ + bool has_more_account= true; + + for (m_pos.set_at(&m_next_pos); + has_more_account; + m_pos.next_account()) + { + PFS_account *pfs_account= global_account_container.get(m_pos.m_index_1, &has_more_account); + + if (m_status_cache.materialize_account(pfs_account) == 0) + { + /* Mark this account as materialized. */ + m_context->set_item(m_pos.m_index_1); + + /* Get the next status variable. */ + const Status_variable *stat_var= m_status_cache.get(m_pos.m_index_2); + if (stat_var != NULL) + { + make_row(pfs_account, stat_var); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + return HA_ERR_END_OF_FILE; +} + +int +table_status_by_account::rnd_pos(const void *pos) +{ + if (show_compatibility_56) + return HA_ERR_RECORD_DELETED; + + /* If status array changes, exit with warning. */ // TODO: Issue warning + if (!m_context->versions_match()) + return HA_ERR_END_OF_FILE; + + set_position(pos); + assert(m_pos.m_index_1 < global_account_container.get_row_count()); + + PFS_account *pfs_account= global_account_container.get(m_pos.m_index_1); + + /* + Only materialize threads that were previously materialized by rnd_next(). + If a account cannot be rematerialized, then do nothing. + */ + if (m_context->is_item_set(m_pos.m_index_1) && + m_status_cache.materialize_account(pfs_account) == 0) + { + const Status_variable *stat_var= m_status_cache.get(m_pos.m_index_2); + if (stat_var != NULL) + { + make_row(pfs_account, stat_var); + return 0; + } + } + return HA_ERR_RECORD_DELETED; +} + +void table_status_by_account +::make_row(PFS_account *pfs_account, const Status_variable *status_var) +{ + pfs_optimistic_state lock; + m_row_exists= false; + pfs_account->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_account.make_row(pfs_account)) + return; + + m_row.m_variable_name.make_row(status_var->m_name, status_var->m_name_length); + m_row.m_variable_value.make_row(status_var); + + if (!pfs_account->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; +} + +int table_status_by_account +::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* USER */ + case 1: /* HOST */ + m_row.m_account.set_field(f->field_index, f); + break; + case 2: /* VARIABLE_NAME */ + set_field_varchar_utf8(f, m_row.m_variable_name.m_str, m_row.m_variable_name.m_length); + break; + case 3: /* VARIABLE_VALUE */ + m_row.m_variable_value.set_field(f); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_status_by_account.h b/storage/perfschema/table_status_by_account.h new file mode 100644 index 00000000..c8d270c5 --- /dev/null +++ b/storage/perfschema/table_status_by_account.h @@ -0,0 +1,157 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef TABLE_STATUS_BY_ACCOUNT_H +#define TABLE_STATUS_BY_ACCOUNT_H + +/** + @file storage/perfschema/table_status_by_account.h + Table STATUS_BY_ACCOUNT (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_account.h" +#include "pfs_account.h" +#include "pfs_host.h" +#include "table_helper.h" +#include "pfs_variable.h" +#include "pfs_buffer_container.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.STATUS_BY_ACCOUNT. +*/ +struct row_status_by_account +{ + /** Column USER, HOST. */ + PFS_account_row m_account; + /** Column VARIABLE_NAME. */ + PFS_variable_name_row m_variable_name; + /** Column VARIABLE_VALUE. */ + PFS_variable_value_row m_variable_value; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.STATUS_BY_ACCOUNT. + Index 1 on account (0 based) + Index 2 on status variable (0 based) +*/ +struct pos_status_by_account +: public PFS_double_index +{ + pos_status_by_account() + : PFS_double_index(0, 0) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } + + inline bool has_more_account(void) + { return (m_index_1 < global_account_container.get_row_count()); } + + inline void next_account(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** + Store and retrieve table state information for queries that reinstantiate + the table object. +*/ +class table_status_by_account_context : public PFS_table_context +{ +public: + table_status_by_account_context(ulonglong current_version, bool restore) : + PFS_table_context(current_version, global_account_container.get_row_count(), restore, THR_PFS_SBH) { } +}; + +/** Table PERFORMANCE_SCHEMA.STATUS_BY_ACCOUNT. */ +class table_status_by_account : public PFS_engine_table +{ + typedef pos_status_by_account pos_t; + +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + table_status_by_account(); + +public: + ~table_status_by_account() + {} + +protected: + int materialize(PFS_thread *pfs_thread); + void make_row(PFS_account *pfs_account, const Status_variable *status_var); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Status variable cache for one account. */ + PFS_status_variable_cache m_status_cache; + + /** Current row. */ + row_status_by_account m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + + /** Table context with global status array version and map of materialized threads. */ + table_status_by_account_context *m_context; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_status_by_host.cc b/storage/perfschema/table_status_by_host.cc new file mode 100644 index 00000000..52d723dc --- /dev/null +++ b/storage/perfschema/table_status_by_host.cc @@ -0,0 +1,252 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_status_by_host.cc + Table STATUS_BY_HOST (implementation). +*/ + +#include "my_global.h" +#include "table_status_by_host.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "pfs_global.h" +#include "pfs_account.h" + +THR_LOCK table_status_by_host::m_table_lock; + +PFS_engine_table_share_state +table_status_by_host::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_status_by_host::m_share= +{ + { C_STRING_WITH_LEN("status_by_host") }, + &pfs_truncatable_acl, + table_status_by_host::create, + NULL, /* write_row */ + table_status_by_host::delete_all_rows, + table_status_by_host::get_row_count, + sizeof(pos_t), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE status_by_host(" + "HOST CHAR(60) collate utf8_bin default null comment 'Host for which the status variable is reported.'," + "VARIABLE_NAME VARCHAR(64) not null comment 'Status variable name.'," + "VARIABLE_VALUE VARCHAR(1024) comment 'Aggregated status variable value.' )") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_status_by_host::create(void) +{ + return new table_status_by_host(); +} + +int table_status_by_host::delete_all_rows(void) +{ + mysql_mutex_lock(&LOCK_status); + reset_status_by_thread(); + reset_status_by_account(); + reset_status_by_host(); + mysql_mutex_unlock(&LOCK_status); + return 0; +} + +ha_rows table_status_by_host::get_row_count(void) +{ + mysql_mutex_lock(&LOCK_status); + size_t status_var_count= all_status_vars.elements; + mysql_mutex_unlock(&LOCK_status); + return (global_host_container.get_row_count() * status_var_count); +} + +table_status_by_host::table_status_by_host() + : PFS_engine_table(&m_share, &m_pos), + m_status_cache(true), m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_status_by_host::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_status_by_host::rnd_init(bool scan) +{ + if (show_compatibility_56) + return 0; + + /* + Build array of SHOW_VARs from the global status array prior to materializing + threads in rnd_next() or rnd_pos(). + */ + m_status_cache.initialize_client_session(); + + /* Use the current number of status variables to detect changes. */ + ulonglong status_version= m_status_cache.get_status_array_version(); + + /* + The table context holds the current version of the global status array + and a record of which hosts were materialized. If scan == true, then + allocate a new context from mem_root and store in TLS. If scan == false, + then restore from TLS. + */ + m_context= (table_status_by_host_context *)current_thd->alloc(sizeof(table_status_by_host_context)); + new(m_context) table_status_by_host_context(status_version, !scan); + return 0; +} + +int table_status_by_host::rnd_next(void) +{ + if (show_compatibility_56) + return HA_ERR_END_OF_FILE; + + /* If status array changes, exit with warning. */ // TODO: Issue warning + if (!m_context->versions_match()) + return HA_ERR_END_OF_FILE; + + /* + For each user, build a cache of status variables using totals from all + threads associated with the host. + */ + bool has_more_host= true; + + for (m_pos.set_at(&m_next_pos); + has_more_host; + m_pos.next_host()) + { + PFS_host *pfs_host= global_host_container.get(m_pos.m_index_1, &has_more_host); + + if (m_status_cache.materialize_host(pfs_host) == 0) + { + /* Mark this host as materialized. */ + m_context->set_item(m_pos.m_index_1); + + /* Get the next status variable. */ + const Status_variable *stat_var= m_status_cache.get(m_pos.m_index_2); + if (stat_var != NULL) + { + make_row(pfs_host, stat_var); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + return HA_ERR_END_OF_FILE; +} + +int +table_status_by_host::rnd_pos(const void *pos) +{ + if (show_compatibility_56) + return 0; + + /* If status array changes, exit with warning. */ // TODO: Issue warning + if (!m_context->versions_match()) + return HA_ERR_END_OF_FILE; + + set_position(pos); + assert(m_pos.m_index_1 < global_host_container.get_row_count()); + + PFS_host *pfs_host= global_host_container.get(m_pos.m_index_1); + + /* + Only materialize threads that were previously materialized by rnd_next(). + If a host cannot be rematerialized, then do nothing. + */ + if (m_context->is_item_set(m_pos.m_index_1) && + m_status_cache.materialize_host(pfs_host) == 0) + { + const Status_variable *stat_var= m_status_cache.get(m_pos.m_index_2); + if (stat_var != NULL) + { + make_row(pfs_host, stat_var); + return 0; + } + } + return HA_ERR_RECORD_DELETED; +} + +void table_status_by_host +::make_row(PFS_host *pfs_host, const Status_variable *status_var) +{ + pfs_optimistic_state lock; + m_row_exists= false; + pfs_host->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_host.make_row(pfs_host)) + return; + + m_row.m_variable_name.make_row(status_var->m_name, status_var->m_name_length); + m_row.m_variable_value.make_row(status_var); + + if (!pfs_host->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; +} + +int table_status_by_host +::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* HOST */ + m_row.m_host.set_field(f); + break; + case 1: /* VARIABLE_NAME */ + set_field_varchar_utf8(f, m_row.m_variable_name.m_str, m_row.m_variable_name.m_length); + break; + case 2: /* VARIABLE_VALUE */ + m_row.m_variable_value.set_field(f); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_status_by_host.h b/storage/perfschema/table_status_by_host.h new file mode 100644 index 00000000..4e28966c --- /dev/null +++ b/storage/perfschema/table_status_by_host.h @@ -0,0 +1,155 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef TABLE_STATUS_BY_HOST_H +#define TABLE_STATUS_BY_HOST_H + +/** + @file storage/perfschema/table_status_by_host.h + Table STATUS_BY_HOST (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_host.h" +#include "table_helper.h" +#include "pfs_variable.h" +#include "pfs_buffer_container.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.STATUS_BY_HOST. +*/ +struct row_status_by_host +{ + /** Column HOST */ + PFS_host_row m_host; + /** Column VARIABLE_NAME. */ + PFS_variable_name_row m_variable_name; + /** Column VARIABLE_VALUE. */ + PFS_variable_value_row m_variable_value; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.STATUS_BY_HOST. + Index 1 on host (0 based) + Index 2 on status variable (0 based) +*/ +struct pos_status_by_host +: public PFS_double_index +{ + pos_status_by_host() + : PFS_double_index(0, 0) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } + + inline bool has_more_host(void) + { return (m_index_1 < global_host_container.get_row_count()); } + + inline void next_host(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** + Store and retrieve table state information for queries that reinstantiate + the table object. +*/ +class table_status_by_host_context : public PFS_table_context +{ +public: + table_status_by_host_context(ulonglong current_version, bool restore) : + PFS_table_context(current_version, global_host_container.get_row_count(), restore, THR_PFS_SBH) { } +}; + +/** Table PERFORMANCE_SCHEMA.STATUS_BY_HOST. */ +class table_status_by_host : public PFS_engine_table +{ + typedef pos_status_by_host pos_t; + +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + table_status_by_host(); + +public: + ~table_status_by_host() + {} + +protected: + int materialize(PFS_thread *thread); + void make_row(PFS_host *pfs_host, const Status_variable *status_var); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Status variable cache for one host. */ + PFS_status_variable_cache m_status_cache; + + /** Current row. */ + row_status_by_host m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + + /** Table context with global status array version and map of materialized threads. */ + table_status_by_host_context *m_context; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_status_by_thread.cc b/storage/perfschema/table_status_by_thread.cc new file mode 100644 index 00000000..ea1218a0 --- /dev/null +++ b/storage/perfschema/table_status_by_thread.cc @@ -0,0 +1,246 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_status_by_thread.cc + Table STATUS_BY_THREAD (implementation). +*/ + +#include "my_global.h" +#include "table_status_by_thread.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "pfs_global.h" + +THR_LOCK table_status_by_thread::m_table_lock; + +PFS_engine_table_share_state +table_status_by_thread::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_status_by_thread::m_share= +{ + { C_STRING_WITH_LEN("status_by_thread") }, + &pfs_truncatable_acl, + table_status_by_thread::create, + NULL, /* write_row */ + table_status_by_thread::delete_all_rows, + table_status_by_thread::get_row_count, + sizeof(pos_t), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE status_by_thread(" + "THREAD_ID BIGINT unsigned not null comment 'The thread identifier of the session in which the status variable is defined.'," + "VARIABLE_NAME VARCHAR(64) not null comment 'Status variable name.'," + "VARIABLE_VALUE VARCHAR(1024) comment 'Aggregated status variable value.' )") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_status_by_thread::create(void) +{ + return new table_status_by_thread(); +} + +int table_status_by_thread::delete_all_rows(void) +{ + /* Lock required to aggregate to global_status_var. */ + mysql_mutex_lock(&LOCK_status); + + reset_status_by_thread(); + + mysql_mutex_unlock(&LOCK_status); + return 0; +} + +ha_rows table_status_by_thread::get_row_count(void) +{ + mysql_mutex_lock(&LOCK_status); + size_t status_var_count= all_status_vars.elements; + mysql_mutex_unlock(&LOCK_status); + return (global_thread_container.get_row_count() * status_var_count); +} + +table_status_by_thread::table_status_by_thread() + : PFS_engine_table(&m_share, &m_pos), + m_status_cache(true), m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_status_by_thread::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_status_by_thread::rnd_init(bool scan) +{ + if (show_compatibility_56) + return 0; + + /* + Build array of SHOW_VARs from the global status array prior to materializing + threads in rnd_next() or rnd_pos(). + */ + m_status_cache.initialize_session(); + + /* Record the current number of status variables to detect subsequent changes. */ + ulonglong status_version= m_status_cache.get_status_array_version(); + + /* + The table context holds the current version of the global status array + and a record of which threads were materialized. If scan == true, then + allocate a new context from mem_root and store in TLS. If scan == false, + then restore from TLS. + */ + m_context= (table_status_by_thread_context *)current_thd->alloc(sizeof(table_status_by_thread_context)); + new(m_context) table_status_by_thread_context(status_version, !scan); + return 0; +} + +int table_status_by_thread::rnd_next(void) +{ + if (show_compatibility_56) + return HA_ERR_END_OF_FILE; + + /* If global status array changes, exit with warning. */ // TODO: Issue warning + if (!m_context->versions_match()) + return HA_ERR_END_OF_FILE; + + bool has_more_thread= true; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + PFS_thread *pfs_thread= global_thread_container.get(m_pos.m_index_1, &has_more_thread); + if (m_status_cache.materialize_session(pfs_thread) == 0) + { + /* Mark this thread as materialized. */ + m_context->set_item(m_pos.m_index_1); + const Status_variable *stat_var= m_status_cache.get(m_pos.m_index_2); + if (stat_var != NULL) + { + make_row(pfs_thread, stat_var); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + return HA_ERR_END_OF_FILE; +} + +int +table_status_by_thread::rnd_pos(const void *pos) +{ + if (show_compatibility_56) + return HA_ERR_RECORD_DELETED; + + /* If global status array has changed, do nothing. */ + if (!m_context->versions_match()) + return HA_ERR_RECORD_DELETED; + + set_position(pos); + assert(m_pos.m_index_1 < global_thread_container.get_row_count()); + + PFS_thread *pfs_thread= global_thread_container.get(m_pos.m_index_1); + /* + Only materialize threads that were previously materialized by rnd_next(). + If a thread cannot be rematerialized, then do nothing. + */ + if (m_context->is_item_set(m_pos.m_index_1) && + m_status_cache.materialize_session(pfs_thread) == 0) + { + const Status_variable *stat_var= m_status_cache.get(m_pos.m_index_2); + if (stat_var != NULL) + { + make_row(pfs_thread, stat_var); + return 0; + } + } + return HA_ERR_RECORD_DELETED; +} + +void table_status_by_thread +::make_row(PFS_thread *thread, const Status_variable *status_var) +{ + pfs_optimistic_state lock; + m_row_exists= false; + if (status_var->is_null()) + return; + + /* Protect this reader against a thread termination */ + thread->m_lock.begin_optimistic_lock(&lock); + + m_row.m_thread_internal_id= thread->m_thread_internal_id; + m_row.m_variable_name.make_row(status_var->m_name, status_var->m_name_length); + m_row.m_variable_value.make_row(status_var); + + if (!thread->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; +} + +int table_status_by_thread +::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + 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: /* VARIABLE_NAME */ + set_field_varchar_utf8(f, m_row.m_variable_name.m_str, m_row.m_variable_name.m_length); + break; + case 2: /* VARIABLE_VALUE */ + m_row.m_variable_value.set_field(f); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_status_by_thread.h b/storage/perfschema/table_status_by_thread.h new file mode 100644 index 00000000..77049043 --- /dev/null +++ b/storage/perfschema/table_status_by_thread.h @@ -0,0 +1,152 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef TABLE_STATUS_BY_THREAD_H +#define TABLE_STATUS_BY_THREAD_H + +/** + @file storage/perfschema/table_status_by_thread.h + Table STATUS_BY_THREAD (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" +#include "pfs_variable.h" +#include "pfs_buffer_container.h" +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.STATUS_BY_THREAD. +*/ +struct row_status_by_thread +{ + /** Column THREAD_ID. */ + ulonglong m_thread_internal_id; + /** Column VARIABLE_NAME. */ + PFS_variable_name_row m_variable_name; + /** Column VARIABLE_VALUE. */ + PFS_variable_value_row m_variable_value; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.STATUS_BY_THREAD. + Index 1 on thread (0 based) + Index 2 on status variable (0 based) +*/ +struct pos_status_by_thread +: public PFS_double_index +{ + pos_status_by_thread() + : PFS_double_index(0, 0) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } + + inline bool has_more_thread(void) + { return (m_index_1 < global_thread_container.get_row_count()); } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** + Store and retrieve table state information for queries that reinstantiate + the table object. +*/ +class table_status_by_thread_context : public PFS_table_context +{ +public: + table_status_by_thread_context(ulonglong current_version, bool restore) : + PFS_table_context(current_version, global_thread_container.get_row_count(), restore, THR_PFS_SBT) { } +}; + +/** Table PERFORMANCE_SCHEMA.STATUS_BY_THREAD. */ +class table_status_by_thread : public PFS_engine_table +{ + typedef pos_status_by_thread pos_t; + +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + table_status_by_thread(); + +public: + ~table_status_by_thread() + {} + +protected: + int materialize(PFS_thread *thread); + void make_row(PFS_thread *thread, const Status_variable *status_var); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current THD variables. */ + PFS_status_variable_cache m_status_cache; + /** Current row. */ + row_status_by_thread m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + + /** Table context with global status array version and map of materialized threads. */ + table_status_by_thread_context *m_context; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_status_by_user.cc b/storage/perfschema/table_status_by_user.cc new file mode 100644 index 00000000..dcafa3a8 --- /dev/null +++ b/storage/perfschema/table_status_by_user.cc @@ -0,0 +1,253 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_status_by_user.cc + Table STATUS_BY_USER (implementation). +*/ + +#include "my_global.h" +#include "table_status_by_user.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "pfs_global.h" +#include "pfs_account.h" +#include "pfs_visitor.h" + +THR_LOCK table_status_by_user::m_table_lock; + +PFS_engine_table_share_state +table_status_by_user::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_status_by_user::m_share= +{ + { C_STRING_WITH_LEN("status_by_user") }, + &pfs_truncatable_acl, + table_status_by_user::create, + NULL, /* write_row */ + table_status_by_user::delete_all_rows, + table_status_by_user::get_row_count, + sizeof(pos_t), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE status_by_user(" + "USER CHAR(32) collate utf8_bin default null comment 'User for which the status variable is reported.'," + "VARIABLE_NAME VARCHAR(64) not null comment 'Status variable name.'," + "VARIABLE_VALUE VARCHAR(1024) comment 'Aggregated status variable value.' )") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_status_by_user::create(void) +{ + return new table_status_by_user(); +} + +int table_status_by_user::delete_all_rows(void) +{ + mysql_mutex_lock(&LOCK_status); + reset_status_by_thread(); + reset_status_by_account(); + reset_status_by_user(); + mysql_mutex_unlock(&LOCK_status); + return 0; +} + +ha_rows table_status_by_user::get_row_count(void) +{ + mysql_mutex_lock(&LOCK_status); + size_t status_var_count= all_status_vars.elements; + mysql_mutex_unlock(&LOCK_status); + return (global_user_container.get_row_count() * status_var_count); +} + +table_status_by_user::table_status_by_user() + : PFS_engine_table(&m_share, &m_pos), + m_status_cache(true), m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_status_by_user::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_status_by_user::rnd_init(bool scan) +{ + if (show_compatibility_56) + return 0; + + /* + Build array of SHOW_VARs from the global status array prior to materializing + threads in rnd_next() or rnd_pos(). + */ + m_status_cache.initialize_client_session(); + + /* Use the current number of status variables to detect changes. */ + ulonglong status_version= m_status_cache.get_status_array_version(); + + /* + The table context holds the current version of the global status array + and a record of which users were materialized. If scan == true, then + allocate a new context from mem_root and store in TLS. If scan == false, + then restore from TLS. + */ + m_context= (table_status_by_user_context *)current_thd->alloc(sizeof(table_status_by_user_context)); + new(m_context) table_status_by_user_context(status_version, !scan); + return 0; +} + +int table_status_by_user::rnd_next(void) +{ + if (show_compatibility_56) + return HA_ERR_END_OF_FILE; + + /* If status array changes, exit with warning. */ // TODO: Issue warning + if (!m_context->versions_match()) + return HA_ERR_END_OF_FILE; + + /* + For each user, build a cache of status variables using totals from all + threads associated with the user. + */ + bool has_more_user= true; + + for (m_pos.set_at(&m_next_pos); + has_more_user; + m_pos.next_user()) + { + PFS_user *pfs_user= global_user_container.get(m_pos.m_index_1, &has_more_user); + + if (m_status_cache.materialize_user(pfs_user) == 0) + { + /* Mark this user as materialized. */ + m_context->set_item(m_pos.m_index_1); + + /* Get the next status variable. */ + const Status_variable *stat_var= m_status_cache.get(m_pos.m_index_2); + if (stat_var != NULL) + { + make_row(pfs_user, stat_var); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + return HA_ERR_END_OF_FILE; +} + +int +table_status_by_user::rnd_pos(const void *pos) +{ + if (show_compatibility_56) + return HA_ERR_RECORD_DELETED; + + /* If status array changes, exit with warning. */ // TODO: Issue warning + if (!m_context->versions_match()) + return HA_ERR_END_OF_FILE; + + set_position(pos); + assert(m_pos.m_index_1 < global_user_container.get_row_count()); + + PFS_user *pfs_user= global_user_container.get(m_pos.m_index_1); + + /* + Only materialize threads that were previously materialized by rnd_next(). + If a user cannot be rematerialized, then do nothing. + */ + if (m_context->is_item_set(m_pos.m_index_1) && + m_status_cache.materialize_user(pfs_user) == 0) + { + const Status_variable *stat_var= m_status_cache.get(m_pos.m_index_2); + if (stat_var != NULL) + { + make_row(pfs_user, stat_var); + return 0; + } + } + return HA_ERR_RECORD_DELETED; +} + +void table_status_by_user +::make_row(PFS_user *user, const Status_variable *status_var) +{ + pfs_optimistic_state lock; + m_row_exists= false; + user->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_user.make_row(user)) + return; + + m_row.m_variable_name.make_row(status_var->m_name, status_var->m_name_length); + m_row.m_variable_value.make_row(status_var); + + if (!user->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; +} + +int table_status_by_user +::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* USER */ + m_row.m_user.set_field(f); + break; + case 1: /* VARIABLE_NAME */ + set_field_varchar_utf8(f, m_row.m_variable_name.m_str, m_row.m_variable_name.m_length); + break; + case 2: /* VARIABLE_VALUE */ + m_row.m_variable_value.set_field(f); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_status_by_user.h b/storage/perfschema/table_status_by_user.h new file mode 100644 index 00000000..1954b15d --- /dev/null +++ b/storage/perfschema/table_status_by_user.h @@ -0,0 +1,154 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef TABLE_STATUS_BY_USER_H +#define TABLE_STATUS_BY_USER_H + +/** + @file storage/perfschema/table_status_by_user.h + Table STATUS_BY_USER (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_user.h" +#include "table_helper.h" +#include "pfs_variable.h" +#include "pfs_buffer_container.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.STATUS_BY_USER. +*/ +struct row_status_by_user +{ + /** Column USER */ + PFS_user_row m_user; + /** Column VARIABLE_NAME. */ + PFS_variable_name_row m_variable_name; + /** Column VARIABLE_VALUE. */ + PFS_variable_value_row m_variable_value; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.STATUS_BY_USER. + Index 1 on user (0 based) + Index 2 on status variable (0 based) +*/ +struct pos_status_by_user +: public PFS_double_index +{ + pos_status_by_user() + : PFS_double_index(0, 0) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } + + inline bool has_more_user(void) + { return (m_index_1 < global_user_container.get_row_count()); } + + inline void next_user(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** + Store and retrieve table state information for queries that reinstantiate + the table object. +*/ +class table_status_by_user_context : public PFS_table_context +{ +public: + table_status_by_user_context(ulonglong current_version, bool restore) : + PFS_table_context(current_version, global_user_container.get_row_count(), restore, THR_PFS_SBU) { } +}; + +/** Table PERFORMANCE_SCHEMA.STATUS_BY_USER. */ +class table_status_by_user : public PFS_engine_table +{ + typedef pos_status_by_user pos_t; + +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + table_status_by_user(); + +public: + ~table_status_by_user() { } + +protected: + int materialize(PFS_thread *thread); + void make_row(PFS_user *user, const Status_variable *status_var); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Status variable cache for one user. */ + PFS_status_variable_cache m_status_cache; + + /** Current row. */ + row_status_by_user m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + + /** Table context with global status array version and map of materialized threads. */ + table_status_by_user_context *m_context; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_sync_instances.cc b/storage/perfschema/table_sync_instances.cc new file mode 100644 index 00000000..497f7fa1 --- /dev/null +++ b/storage/perfschema/table_sync_instances.cc @@ -0,0 +1,493 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_sync_instances.cc + Table MUTEX_INSTANCES, RWLOCK_INSTANCES + and COND_INSTANCES (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_sync_instances.h" +#include "pfs_global.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_mutex_instances::m_table_lock; + +PFS_engine_table_share_state +table_mutex_instances::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_mutex_instances::m_share= +{ + { C_STRING_WITH_LEN("mutex_instances") }, + &pfs_readonly_acl, + table_mutex_instances::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_mutex_instances::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE mutex_instances(" + "NAME VARCHAR(128) not null comment 'Instrument name associated with the mutex.'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null comment 'Memory address of the instrumented mutex.'," + "LOCKED_BY_THREAD_ID BIGINT unsigned comment 'The THREAD_ID of the locking thread if a thread has a mutex locked, otherwise NULL.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_mutex_instances::create(void) +{ + return new table_mutex_instances(); +} + +ha_rows +table_mutex_instances::get_row_count(void) +{ + return global_mutex_container.get_row_count(); +} + +table_mutex_instances::table_mutex_instances() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_mutex_instances::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_mutex_instances::rnd_next(void) +{ + PFS_mutex *pfs; + + m_pos.set_at(&m_next_pos); + PFS_mutex_iterator it= global_mutex_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_mutex_instances::rnd_pos(const void *pos) +{ + PFS_mutex *pfs; + + set_position(pos); + + pfs= global_mutex_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_mutex_instances::make_row(PFS_mutex *pfs) +{ + pfs_optimistic_state lock; + PFS_mutex_class *safe_class; + + m_row_exists= false; + + /* Protect this reader against a mutex destroy */ + pfs->m_lock.begin_optimistic_lock(&lock); + + safe_class= sanitize_mutex_class(pfs->m_class); + if (unlikely(safe_class == NULL)) + return; + + m_row.m_name= safe_class->m_name; + m_row.m_name_length= safe_class->m_name_length; + m_row.m_identity= pfs->m_identity; + + /* Protect this reader against a mutex unlock */ + PFS_thread *safe_owner= sanitize_thread(pfs->m_owner); + if (safe_owner) + { + m_row.m_locked_by_thread_id= safe_owner->m_thread_internal_id; + m_row.m_locked= true; + } + else + m_row.m_locked= false; + + if (pfs->m_lock.end_optimistic_lock(&lock)) + m_row_exists= true; +} + +int table_mutex_instances::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* NAME */ + set_field_varchar_utf8(f, m_row.m_name, m_row.m_name_length); + break; + case 1: /* OBJECT_INSTANCE */ + set_field_ulonglong(f, (intptr) m_row.m_identity); + break; + case 2: /* LOCKED_BY_THREAD_ID */ + if (m_row.m_locked) + set_field_ulonglong(f, m_row.m_locked_by_thread_id); + else + f->set_null(); + break; + default: + assert(false); + } + } + } + + return 0; +} + +THR_LOCK table_rwlock_instances::m_table_lock; + +PFS_engine_table_share_state +table_rwlock_instances::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_rwlock_instances::m_share= +{ + { C_STRING_WITH_LEN("rwlock_instances") }, + &pfs_readonly_acl, + table_rwlock_instances::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_rwlock_instances::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE rwlock_instances(" + "NAME VARCHAR(128) not null comment 'Instrument name associated with the read write lock'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null comment 'Address in memory of the instrumented lock'," + "WRITE_LOCKED_BY_THREAD_ID BIGINT unsigned comment 'THREAD_ID of the locking thread if locked in write (exclusive) mode, otherwise NULL.'," + "READ_LOCKED_BY_COUNT INTEGER unsigned not null comment 'Count of current read locks held')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_rwlock_instances::create(void) +{ + return new table_rwlock_instances(); +} + +ha_rows +table_rwlock_instances::get_row_count(void) +{ + return global_rwlock_container.get_row_count(); +} + +table_rwlock_instances::table_rwlock_instances() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_rwlock_instances::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_rwlock_instances::rnd_next(void) +{ + PFS_rwlock *pfs; + + m_pos.set_at(&m_next_pos); + PFS_rwlock_iterator it= global_rwlock_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_rwlock_instances::rnd_pos(const void *pos) +{ + PFS_rwlock *pfs; + + set_position(pos); + + pfs= global_rwlock_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_rwlock_instances::make_row(PFS_rwlock *pfs) +{ + pfs_optimistic_state lock; + PFS_rwlock_class *safe_class; + + m_row_exists= false; + + /* Protect this reader against a rwlock destroy */ + pfs->m_lock.begin_optimistic_lock(&lock); + + safe_class= sanitize_rwlock_class(pfs->m_class); + if (unlikely(safe_class == NULL)) + return; + + m_row.m_name= safe_class->m_name; + m_row.m_name_length= safe_class->m_name_length; + m_row.m_identity= pfs->m_identity; + + /* Protect this reader against a rwlock unlock in the writer */ + PFS_thread *safe_writer= sanitize_thread(pfs->m_writer); + if (safe_writer) + { + m_row.m_write_locked_by_thread_id= safe_writer->m_thread_internal_id; + m_row.m_readers= 0; + m_row.m_write_locked= true; + } + else + { + m_row.m_readers= pfs->m_readers; + m_row.m_write_locked= false; + } + + if (pfs->m_lock.end_optimistic_lock(&lock)) + m_row_exists= true; +} + +int table_rwlock_instances::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* NAME */ + set_field_varchar_utf8(f, m_row.m_name, m_row.m_name_length); + break; + case 1: /* OBJECT_INSTANCE */ + set_field_ulonglong(f, (intptr) m_row.m_identity); + break; + case 2: /* WRITE_LOCKED_BY_THREAD_ID */ + if (m_row.m_write_locked) + set_field_ulonglong(f, m_row.m_write_locked_by_thread_id); + else + f->set_null(); + break; + case 3: /* READ_LOCKED_BY_COUNT */ + set_field_ulong(f, m_row.m_readers); + break; + default: + assert(false); + } + } + } + + return 0; +} + +THR_LOCK table_cond_instances::m_table_lock; + +PFS_engine_table_share_state +table_cond_instances::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_cond_instances::m_share= +{ + { C_STRING_WITH_LEN("cond_instances") }, + &pfs_readonly_acl, + table_cond_instances::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_cond_instances::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE cond_instances(" + "NAME VARCHAR(128) not null comment 'Client user name for the connection, or NULL if an internal thread.'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null comment 'Address in memory of the instrumented condition.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_cond_instances::create(void) +{ + return new table_cond_instances(); +} + +ha_rows +table_cond_instances::get_row_count(void) +{ + return global_cond_container.get_row_count(); +} + +table_cond_instances::table_cond_instances() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_cond_instances::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_cond_instances::rnd_next(void) +{ + PFS_cond *pfs; + + m_pos.set_at(&m_next_pos); + PFS_cond_iterator it= global_cond_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_cond_instances::rnd_pos(const void *pos) +{ + PFS_cond *pfs; + + set_position(pos); + + pfs= global_cond_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_cond_instances::make_row(PFS_cond *pfs) +{ + pfs_optimistic_state lock; + PFS_cond_class *safe_class; + + m_row_exists= false; + + /* Protect this reader against a cond destroy */ + pfs->m_lock.begin_optimistic_lock(&lock); + + safe_class= sanitize_cond_class(pfs->m_class); + if (unlikely(safe_class == NULL)) + return; + + m_row.m_name= safe_class->m_name; + m_row.m_name_length= safe_class->m_name_length; + m_row.m_identity= pfs->m_identity; + + if (pfs->m_lock.end_optimistic_lock(&lock)) + m_row_exists= true; +} + +int table_cond_instances::read_row_values(TABLE *table, + unsigned char *, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 0); + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* NAME */ + set_field_varchar_utf8(f, m_row.m_name, m_row.m_name_length); + break; + case 1: /* OBJECT_INSTANCE */ + set_field_ulonglong(f, (intptr) m_row.m_identity); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_sync_instances.h b/storage/perfschema/table_sync_instances.h new file mode 100644 index 00000000..75424d56 --- /dev/null +++ b/storage/perfschema/table_sync_instances.h @@ -0,0 +1,210 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_SYNC_INSTANCE_H +#define TABLE_SYNC_INSTANCE_H + +/** + @file storage/perfschema/table_sync_instances.h + Table MUTEX_INSTANCES, RWLOCK_INSTANCES and COND_INSTANCES (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" + +struct PFS_mutex; +struct PFS_rwlock; +struct PFS_cond; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of table PERFORMANCE_SCHEMA.MUTEX_INSTANCES. */ +struct row_mutex_instances +{ + /** Column NAME. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** Column OBJECT_INSTANCE_BEGIN. */ + const void *m_identity; + /** True if column LOCKED_BY_THREAD_ID is not null. */ + bool m_locked; + /** Column LOCKED_BY_THREAD_ID. */ + ulonglong m_locked_by_thread_id; +}; + +/** Table PERFORMANCE_SCHEMA.MUTEX_INSTANCES. */ +class table_mutex_instances : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_mutex_instances(); + +public: + ~table_mutex_instances() = default; + +private: + void make_row(PFS_mutex *pfs); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_mutex_instances m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** A row of table PERFORMANCE_SCHEMA.RWLOCK_INSTANCES. */ +struct row_rwlock_instances +{ + /** Column NAME. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** Column OBJECT_INSTANCE_BEGIN. */ + const void *m_identity; + /** True if column WRITE_LOCKED_BY_THREAD_ID is not null. */ + bool m_write_locked; + /** Column WRITE_LOCKED_BY_THREAD_ID. */ + ulonglong m_write_locked_by_thread_id; + /** Column READ_LOCKED_BY_COUNT. */ + ulong m_readers; +}; + +/** Table PERFORMANCE_SCHEMA.RWLOCK_INSTANCES. */ +class table_rwlock_instances : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_rwlock_instances(); + +public: + ~table_rwlock_instances() = default; + +private: + void make_row(PFS_rwlock *pfs); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_rwlock_instances m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** A row of table PERFORMANCE_SCHEMA.COND_INSTANCES. */ +struct row_cond_instances +{ + /** Column NAME. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** Column OBJECT_INSTANCE_BEGIN. */ + const void *m_identity; +}; + +/** Table PERFORMANCE_SCHEMA.COND_INSTANCES. */ +class table_cond_instances : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +private: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_cond_instances(); + +public: + ~table_cond_instances() = default; + +private: + void make_row(PFS_cond *pfs); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_cond_instances m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_table_handles.cc b/storage/perfschema/table_table_handles.cc new file mode 100644 index 00000000..7ea2b0a3 --- /dev/null +++ b/storage/perfschema/table_table_handles.cc @@ -0,0 +1,221 @@ +/* Copyright (c) 2012, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_table_handles.cc + Table TABLE_TABLE_HANDLES (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_table_handles.h" +#include "pfs_global.h" +#include "pfs_stat.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_table_handles::m_table_lock; + +PFS_engine_table_share_state +table_table_handles::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_table_handles::m_share= +{ + { C_STRING_WITH_LEN("table_handles") }, + &pfs_readonly_acl, + table_table_handles::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_table_handles::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE table_handles(" + "OBJECT_TYPE VARCHAR(64) not null comment 'The table opened by a table handle.'," + "OBJECT_SCHEMA VARCHAR(64) not null comment 'The schema that contains the object.'," + "OBJECT_NAME VARCHAR(64) not null comment 'The name of the instrumented object.'," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null comment 'The table handle address in memory.'," + "OWNER_THREAD_ID BIGINT unsigned comment 'The thread owning the table handle.'," + "OWNER_EVENT_ID BIGINT unsigned comment 'The event which caused the table handle to be opened.'," + "INTERNAL_LOCK VARCHAR(64) comment 'The table lock used at the SQL level.'," + "EXTERNAL_LOCK VARCHAR(64) comment 'The table lock used at the storage engine level.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_table_handles::create(void) +{ + return new table_table_handles(); +} + +ha_rows +table_table_handles::get_row_count(void) +{ + return global_table_container.get_row_count(); +} + +table_table_handles::table_table_handles() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_table_handles::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_table_handles::rnd_init(bool scan) +{ + return 0; +} + +int table_table_handles::rnd_next(void) +{ + PFS_table *pfs; + + m_pos.set_at(&m_next_pos); + PFS_table_iterator it= global_table_container.iterate(m_pos.m_index); + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int +table_table_handles::rnd_pos(const void *pos) +{ + PFS_table *pfs; + + set_position(pos); + + pfs= global_table_container.get(m_pos.m_index); + if (pfs != NULL) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_table_handles::make_row(PFS_table *table) +{ + pfs_optimistic_state lock; + PFS_table_share *share; + PFS_thread *thread; + + m_row_exists= false; + + table->m_lock.begin_optimistic_lock(&lock); + + share= sanitize_table_share(table->m_share); + if (share == NULL) + return; + + if (m_row.m_object.make_row(share)) + return; + + m_row.m_identity= table->m_identity; + + thread= sanitize_thread(table->m_thread_owner); + if (thread != NULL) + { + m_row.m_owner_thread_id= thread->m_thread_internal_id; + m_row.m_owner_event_id= table->m_owner_event_id; + } + else + { + m_row.m_owner_thread_id= 0; + m_row.m_owner_event_id= 0; + } + + m_row.m_internal_lock= table->m_internal_lock; + m_row.m_external_lock= table->m_external_lock; + + if (! table->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; +} + +int table_table_handles::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* OBJECT_TYPE */ + case 1: /* SCHEMA_NAME */ + case 2: /* OBJECT_NAME */ + m_row.m_object.set_field(f->field_index, f); + break; + case 3: /* OBJECT_INSTANCE_BEGIN */ + set_field_ulonglong(f, (intptr) m_row.m_identity); + break; + case 4: /* OWNER_THREAD_ID */ + set_field_ulonglong(f, m_row.m_owner_thread_id); + break; + case 5: /* OWNER_EVENT_ID */ + set_field_ulonglong(f, m_row.m_owner_event_id); + break; + case 6: /* INTERNAL_LOCK */ + set_field_lock_type(f, m_row.m_internal_lock); + break; + case 7: /* EXTERNAL_LOCK */ + set_field_lock_type(f, m_row.m_external_lock); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_table_handles.h b/storage/perfschema/table_table_handles.h new file mode 100644 index 00000000..7e184deb --- /dev/null +++ b/storage/perfschema/table_table_handles.h @@ -0,0 +1,109 @@ +/* Copyright (c) 2012, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_TABLE_HANDLES_H +#define TABLE_TABLE_HANDLES_H + +/** + @file storage/perfschema/table_table_handles.h + Table TABLE_HANDLES (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.TABLE_HANDLES. +*/ +struct row_table_handles +{ + /** Column OBJECT_TYPE, SCHEMA_NAME, OBJECT_NAME. */ + PFS_object_row m_object; + /** Column OBJECT_INSTANCE_BEGIN. */ + const void *m_identity; + /** Column OWNER_THREAD_ID. */ + ulonglong m_owner_thread_id; + /** Column OWNER_EVENT_ID. */ + ulonglong m_owner_event_id; + /** Column INTERNAL_LOCK. */ + PFS_TL_LOCK_TYPE m_internal_lock; + /** Column EXTERNAL_LOCK. */ + PFS_TL_LOCK_TYPE m_external_lock; +}; + +/** Table PERFORMANCE_SCHEMA.TABLE_HANDLES. */ +class table_table_handles : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_table_handles(); + +public: + ~table_table_handles() + {} + +protected: + void make_row(PFS_table *table); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_table_handles m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_threads.cc b/storage/perfschema/table_threads.cc new file mode 100644 index 00000000..92fcf96d --- /dev/null +++ b/storage/perfschema/table_threads.cc @@ -0,0 +1,372 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "my_global.h" +#include "my_thread.h" +#include "table_threads.h" +#include "sql_parse.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" + +THR_LOCK table_threads::m_table_lock; + +PFS_engine_table_share_state +table_threads::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_threads::m_share= +{ + { C_STRING_WITH_LEN("threads") }, + &pfs_updatable_acl, + table_threads::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + cursor_by_thread::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE threads(" + "THREAD_ID BIGINT unsigned not null comment 'A unique thread identifier.'," + "NAME VARCHAR(128) not null comment 'Name associated with the server''s thread instrumentation code, for example thread/sql/main for the server''s main() function, and thread/sql/one_connection for a user connection.'," + "TYPE VARCHAR(10) not null comment 'FOREGROUND or BACKGROUND, depending on the thread type. User connection threads are FOREGROUND, internal server threads are BACKGROUND.'," + "PROCESSLIST_ID BIGINT unsigned comment 'The PROCESSLIST.ID value for threads displayed in the INFORMATION_SCHEMA.PROCESSLIST table, or 0 for background threads. Also corresponds with the CONNECTION_ID() return value for the thread.'," + "PROCESSLIST_USER VARCHAR(" USERNAME_CHAR_LENGTH_STR ") comment 'Foreground thread user, or NULL for a background thread.'," + "PROCESSLIST_HOST VARCHAR(" HOSTNAME_LENGTH_STR ") comment 'Foreground thread host, or NULL for a background thread.'," + "PROCESSLIST_DB VARCHAR(64) comment 'Thread''s default database, or NULL if none exists.'," + "PROCESSLIST_COMMAND VARCHAR(16) comment 'Type of command executed by the thread. These correspond to the the COM_xxx client/server protocol commands, and the Com_xxx status variables. See Thread Command Values.'," + "PROCESSLIST_TIME BIGINT comment 'Time in seconds the thread has been in its current state.'," + "PROCESSLIST_STATE VARCHAR(64) comment 'Action, event or state indicating what the thread is doing.'," + "PROCESSLIST_INFO LONGTEXT comment 'Statement being executed by the thread, or NULL if a statement is not being executed. If a statement results in calling other statements, such as for a stored procedure, the innermost statement from the stored procedure is shown here.'," + "PARENT_THREAD_ID BIGINT unsigned comment 'THREAD_ID of the parent thread, if any. Subthreads can for example be spawned as a result of INSERT DELAYED statements.'," + "ROLE VARCHAR(64) comment 'Unused.'," + "INSTRUMENTED ENUM ('YES', 'NO') not null comment 'YES or NO for Whether the thread is instrumented or not. For foreground threads, the initial value is determined by whether there''s a user/host match in the setup_actors table. Subthreads are again matched, while for background threads, this will be set to YES by default. To monitor events that the thread executes, INSTRUMENTED must be YES and the thread_instrumentation consumer in the setup_consumers table must also be YES.'," + "HISTORY ENUM ('YES', 'NO') not null comment 'Whether to log historical events for the thread.'," + "CONNECTION_TYPE VARCHAR(16) comment 'The protocol used to establish the connection, or NULL for background threads.'," + "THREAD_OS_ID BIGINT unsigned comment 'The thread or task identifier as defined by the underlying operating system, if there is one.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_threads::create() +{ + return new table_threads(); +} + +table_threads::table_threads() + : cursor_by_thread(& m_share), + m_row_exists(false) +{} + +void table_threads::make_row(PFS_thread *pfs) +{ + pfs_optimistic_state lock; + pfs_optimistic_state session_lock; + pfs_optimistic_state stmt_lock; + PFS_stage_class *stage_class; + PFS_thread_class *safe_class; + + m_row_exists= false; + + /* Protect this reader against thread termination */ + pfs->m_lock.begin_optimistic_lock(&lock); + + safe_class= sanitize_thread_class(pfs->m_class); + if (unlikely(safe_class == NULL)) + return; + + m_row.m_thread_internal_id= pfs->m_thread_internal_id; + m_row.m_parent_thread_internal_id= pfs->m_parent_thread_internal_id; + m_row.m_processlist_id= pfs->m_processlist_id; + m_row.m_thread_os_id= pfs->m_thread_os_id; + m_row.m_name= safe_class->m_name; + m_row.m_name_length= safe_class->m_name_length; + + /* Protect this reader against session attribute changes */ + pfs->m_session_lock.begin_optimistic_lock(&session_lock); + + m_row.m_username_length= pfs->m_username_length; + if (unlikely(m_row.m_username_length > sizeof(m_row.m_username))) + return; + if (m_row.m_username_length != 0) + memcpy(m_row.m_username, pfs->m_username, m_row.m_username_length); + + m_row.m_hostname_length= pfs->m_hostname_length; + if (unlikely(m_row.m_hostname_length > sizeof(m_row.m_hostname))) + return; + if (m_row.m_hostname_length != 0) + memcpy(m_row.m_hostname, pfs->m_hostname, m_row.m_hostname_length); + + if (! pfs->m_session_lock.end_optimistic_lock(& session_lock)) + { + /* + One of the columns: + - PROCESSLIST_USER + - PROCESSLIST_HOST + is being updated. + Do not discard the entire row. + Do not loop waiting for a stable value. + Just return NULL values. + */ + m_row.m_username_length= 0; + m_row.m_hostname_length= 0; + } + + /* Protect this reader against statement attributes changes */ + pfs->m_stmt_lock.begin_optimistic_lock(&stmt_lock); + + m_row.m_dbname_length= pfs->m_dbname_length; + if (unlikely(m_row.m_dbname_length > sizeof(m_row.m_dbname))) + return; + if (m_row.m_dbname_length != 0) + memcpy(m_row.m_dbname, pfs->m_dbname, m_row.m_dbname_length); + + m_row.m_processlist_info_ptr= & pfs->m_processlist_info[0]; + m_row.m_processlist_info_length= pfs->m_processlist_info_length; + + if (! pfs->m_stmt_lock.end_optimistic_lock(& stmt_lock)) + { + /* + One of the columns: + - PROCESSLIST_DB + - PROCESSLIST_INFO + is being updated. + Do not discard the entire row. + Do not loop waiting for a stable value. + Just return NULL values. + */ + m_row.m_dbname_length= 0; + m_row.m_processlist_info_length= 0; + } + + /* Dirty read, sanitize the command. */ + m_row.m_command= pfs->m_command; + if ((m_row.m_command < 0) || (m_row.m_command > COM_END)) + m_row.m_command= COM_END; + + m_row.m_start_time= pfs->m_start_time; + + stage_class= find_stage_class(pfs->m_stage); + if (stage_class != NULL) + { + m_row.m_processlist_state_ptr= stage_class->m_name + stage_class->m_prefix_length; + m_row.m_processlist_state_length= stage_class->m_name_length - stage_class->m_prefix_length; + } + else + { + m_row.m_processlist_state_length= 0; + } + m_row.m_connection_type = pfs->m_connection_type; + + + m_row.m_enabled= pfs->m_enabled; + m_row.m_history= pfs->m_history; + m_row.m_psi= pfs; + + if (pfs->m_lock.end_optimistic_lock(& lock)) + m_row_exists= true; +} + +int table_threads::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + const char *str= NULL; + size_t len= 0; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 2); + buf[0]= 0; + buf[1]= 0; + + 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: /* NAME */ + set_field_varchar_utf8(f, m_row.m_name, m_row.m_name_length); + break; + case 2: /* TYPE */ + if (m_row.m_processlist_id != 0) + set_field_varchar_utf8(f, "FOREGROUND", 10); + else + set_field_varchar_utf8(f, "BACKGROUND", 10); + break; + case 3: /* PROCESSLIST_ID */ + if (m_row.m_processlist_id != 0) + set_field_ulonglong(f, m_row.m_processlist_id); + else + f->set_null(); + break; + case 4: /* PROCESSLIST_USER */ + if (m_row.m_username_length > 0) + set_field_varchar_utf8(f, m_row.m_username, + m_row.m_username_length); + else + f->set_null(); + break; + case 5: /* PROCESSLIST_HOST */ + if (m_row.m_hostname_length > 0) + set_field_varchar_utf8(f, m_row.m_hostname, + m_row.m_hostname_length); + else + f->set_null(); + break; + case 6: /* PROCESSLIST_DB */ + if (m_row.m_dbname_length > 0) + set_field_varchar_utf8(f, m_row.m_dbname, + m_row.m_dbname_length); + else + f->set_null(); + break; + case 7: /* PROCESSLIST_COMMAND */ + if (m_row.m_processlist_id != 0) + set_field_varchar_utf8(f, command_name[m_row.m_command].str, + (uint)command_name[m_row.m_command].length); + else + f->set_null(); + break; + case 8: /* PROCESSLIST_TIME */ + if (m_row.m_start_time) + { + time_t now= my_time(0); + ulonglong elapsed= (now > m_row.m_start_time ? now - m_row.m_start_time : 0); + set_field_ulonglong(f, elapsed); + } + else + f->set_null(); + break; + case 9: /* PROCESSLIST_STATE */ + /* This column's datatype is declared as varchar(64). Thread's state + message cannot be more than 64 characters. Otherwise, we will end up + in 'data truncated' warning/error (depends sql_mode setting) when + server is updating this column for those threads. To prevent this + kind of issue, an assert is added. + */ + assert(m_row.m_processlist_state_length <= f->char_length()); + if (m_row.m_processlist_state_length > 0) + set_field_varchar_utf8(f, m_row.m_processlist_state_ptr, + m_row.m_processlist_state_length); + else + f->set_null(); + break; + case 10: /* PROCESSLIST_INFO */ + if (m_row.m_processlist_info_length > 0) + set_field_longtext_utf8(f, m_row.m_processlist_info_ptr, + m_row.m_processlist_info_length); + else + f->set_null(); + break; + case 11: /* PARENT_THREAD_ID */ + if (m_row.m_parent_thread_internal_id != 0) + set_field_ulonglong(f, m_row.m_parent_thread_internal_id); + else + f->set_null(); + break; + case 12: /* ROLE */ + f->set_null(); + break; + case 13: /* INSTRUMENTED */ + set_field_enum(f, m_row.m_enabled ? ENUM_YES : ENUM_NO); + break; + case 14: /* HISTORY */ + set_field_enum(f, m_row.m_history ? ENUM_YES : ENUM_NO); + break; + case 15: /* CONNECTION_TYPE */ + str= vio_type_name(m_row.m_connection_type, & len); + if (len > 0) + set_field_varchar_utf8(f, str, (uint)len); + else + f->set_null(); + break; + case 16: /* THREAD_OS_ID */ + if (m_row.m_thread_os_id > 0) + set_field_ulonglong(f, m_row.m_thread_os_id); + else + f->set_null(); + break; + default: + assert(false); + } + } + } + return 0; +} + +int table_threads::update_row_values(TABLE *table, + const unsigned char *old_buf, + const unsigned char *new_buf, + Field **fields) +{ + Field *f; + enum_yes_no value; + + for (; (f= *fields) ; fields++) + { + if (bitmap_is_set(table->write_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* THREAD_ID */ + case 1: /* NAME */ + case 2: /* TYPE */ + case 3: /* PROCESSLIST_ID */ + case 4: /* PROCESSLIST_USER */ + case 5: /* PROCESSLIST_HOST */ + case 6: /* PROCESSLIST_DB */ + case 7: /* PROCESSLIST_COMMAND */ + case 8: /* PROCESSLIST_TIME */ + case 9: /* PROCESSLIST_STATE */ + case 10: /* PROCESSLIST_INFO */ + case 11: /* PARENT_THREAD_ID */ + case 12: /* ROLE */ + return HA_ERR_WRONG_COMMAND; + case 13: /* INSTRUMENTED */ + value= (enum_yes_no) get_field_enum(f); + m_row.m_psi->set_enabled((value == ENUM_YES) ? true : false); + break; + case 14: /* HISTORY */ + value= (enum_yes_no) get_field_enum(f); + m_row.m_psi->set_history((value == ENUM_YES) ? true : false); + break; + case 15: /* CONNECTION_TYPE */ + case 16: /* THREAD_OS_ID */ + return HA_ERR_WRONG_COMMAND; + default: + assert(false); + } + } + } + return 0; +} + diff --git a/storage/perfschema/table_threads.h b/storage/perfschema/table_threads.h new file mode 100644 index 00000000..1f981ded --- /dev/null +++ b/storage/perfschema/table_threads.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_THREADS_H +#define TABLE_THREADS_H + +#include "pfs_column_types.h" +#include "cursor_by_thread.h" + +struct PFS_thread; + +/** + \addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of PERFORMANCE_SCHEMA.THREADS. +*/ +struct row_threads +{ + /** Column THREAD_ID. */ + ulonglong m_thread_internal_id; + /** Column PROCESSLIST_ID. */ + ulonglong m_processlist_id; + /** Column NAME. */ + const char* m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** Column PROCESSLIST_USER. */ + char m_username[USERNAME_LENGTH]; + /** Length in bytes of @c m_username. */ + uint m_username_length; + /** Column PROCESSLIST_HOST. */ + char m_hostname[HOSTNAME_LENGTH]; + /** Length in bytes of @c m_hostname. */ + uint m_hostname_length; + /** Column PROCESSLIST_DB. */ + char m_dbname[NAME_LEN]; + /** Length in bytes of @c m_dbname. */ + uint m_dbname_length; + /** Column PROCESSLIST_COMMAND. */ + int m_command; + /** Column PROCESSLIST_TIME. */ + time_t m_start_time; + /** Column PROCESSLIST_STATE. */ + const char* m_processlist_state_ptr; + /** Length in bytes of @c m_processlist_state_ptr. */ + uint m_processlist_state_length; + /** Column PROCESSLIST_INFO. */ + const char* m_processlist_info_ptr; + /** Length in bytes of @c m_processlist_info_ptr. */ + uint m_processlist_info_length; + /** Column INSTRUMENTED (read). */ + bool m_enabled; + /** Column HISTORY (read). */ + bool m_history; + /** INSTRUMENTED and HISTORY (write). */ + PFS_thread *m_psi; + /** Column PARENT_THREAD_ID. */ + ulonglong m_parent_thread_internal_id; + /** Column CONNECTION_TYPE. */ + enum_vio_type m_connection_type; + /** Column THREAD_OS_ID. */ + my_thread_os_id_t m_thread_os_id; +}; + +/** Table PERFORMANCE_SCHEMA.THREADS. */ +class table_threads : public cursor_by_thread +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + /** Table builder */ + static PFS_engine_table* create(); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + + virtual int update_row_values(TABLE *table, + const unsigned char *old_buf, + const unsigned char *new_buf, + Field **fields); + +protected: + table_threads(); + +public: + ~table_threads() = default; + +private: + virtual void make_row(PFS_thread *pfs); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_threads m_row; + /** True if the current row exists. */ + bool m_row_exists; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_tiws_by_index_usage.cc b/storage/perfschema/table_tiws_by_index_usage.cc new file mode 100644 index 00000000..80b62bbe --- /dev/null +++ b/storage/perfschema/table_tiws_by_index_usage.cc @@ -0,0 +1,378 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_tiws_by_index_usage.cc + Table TABLE_IO_WAITS_SUMMARY_BY_INDEX_USAGE (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_tiws_by_index_usage.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_tiws_by_index_usage::m_table_lock; + +PFS_engine_table_share_state +table_tiws_by_index_usage::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_tiws_by_index_usage::m_share= +{ + { C_STRING_WITH_LEN("table_io_waits_summary_by_index_usage") }, + &pfs_truncatable_acl, + table_tiws_by_index_usage::create, + NULL, /* write_row */ + table_tiws_by_index_usage::delete_all_rows, + table_tiws_by_index_usage::get_row_count, + sizeof(pos_tiws_by_index_usage), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE table_io_waits_summary_by_index_usage(" + "OBJECT_TYPE VARCHAR(64) comment 'TABLE in the case of all indexes.'," + "OBJECT_SCHEMA VARCHAR(64) comment 'Schema name.'," + "OBJECT_NAME VARCHAR(64) comment 'Table name.'," + "INDEX_NAME VARCHAR(64) comment 'Index name, or PRIMARY for the primary index, NULL for no index (inserts are counted in this case).'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events and the sum of the x_READ and x_WRITE columns.'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.'," + "COUNT_READ BIGINT unsigned not null comment 'Number of all read operations, and the sum of the equivalent x_FETCH columns.'," + "SUM_TIMER_READ BIGINT unsigned not null comment 'Total wait time of all read operations that are timed.'," + "MIN_TIMER_READ BIGINT unsigned not null comment 'Minimum wait time of all read operations that are timed.'," + "AVG_TIMER_READ BIGINT unsigned not null comment 'Average wait time of all read operations that are timed.'," + "MAX_TIMER_READ BIGINT unsigned not null comment 'Maximum wait time of all read operations that are timed.'," + "COUNT_WRITE BIGINT unsigned not null comment 'Number of all write operations, and the sum of the equivalent x_INSERT, x_UPDATE and x_DELETE columns.'," + "SUM_TIMER_WRITE BIGINT unsigned not null comment 'Total wait time of all write operations that are timed.'," + "MIN_TIMER_WRITE BIGINT unsigned not null comment 'Minimum wait time of all write operations that are timed.'," + "AVG_TIMER_WRITE BIGINT unsigned not null comment 'Average wait time of all write operations that are timed.'," + "MAX_TIMER_WRITE BIGINT unsigned not null comment 'Maximum wait time of all write operations that are timed.'," + "COUNT_FETCH BIGINT unsigned not null comment 'Number of all fetch operations.'," + "SUM_TIMER_FETCH BIGINT unsigned not null comment 'Total wait time of all fetch operations that are timed.'," + "MIN_TIMER_FETCH BIGINT unsigned not null comment 'Minimum wait time of all fetch operations that are timed.'," + "AVG_TIMER_FETCH BIGINT unsigned not null comment 'Average wait time of all fetch operations that are timed.'," + "MAX_TIMER_FETCH BIGINT unsigned not null comment 'Maximum wait time of all fetch operations that are timed.'," + "COUNT_INSERT BIGINT unsigned not null comment 'Number of all insert operations.'," + "SUM_TIMER_INSERT BIGINT unsigned not null comment 'Total wait time of all insert operations that are timed.'," + "MIN_TIMER_INSERT BIGINT unsigned not null comment 'Minimum wait time of all insert operations that are timed.'," + "AVG_TIMER_INSERT BIGINT unsigned not null comment 'Average wait time of all insert operations that are timed.'," + "MAX_TIMER_INSERT BIGINT unsigned not null comment 'Maximum wait time of all insert operations that are timed.'," + "COUNT_UPDATE BIGINT unsigned not null comment 'Number of all update operations.'," + "SUM_TIMER_UPDATE BIGINT unsigned not null comment 'Total wait time of all update operations that are timed.'," + "MIN_TIMER_UPDATE BIGINT unsigned not null comment 'Minimum wait time of all update operations that are timed.'," + "AVG_TIMER_UPDATE BIGINT unsigned not null comment 'Average wait time of all update operations that are timed.'," + "MAX_TIMER_UPDATE BIGINT unsigned not null comment 'Maximum wait time of all update operations that are timed.'," + "COUNT_DELETE BIGINT unsigned not null comment 'Number of all delete operations.'," + "SUM_TIMER_DELETE BIGINT unsigned not null comment 'Total wait time of all delete operations that are timed.'," + "MIN_TIMER_DELETE BIGINT unsigned not null comment 'Minimum wait time of all delete operations that are timed.'," + "AVG_TIMER_DELETE BIGINT unsigned not null comment 'Average wait time of all delete operations that are timed.'," + "MAX_TIMER_DELETE BIGINT unsigned not null comment 'Maximum wait time of all delete operations that are timed.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_tiws_by_index_usage::create(void) +{ + return new table_tiws_by_index_usage(); +} + +int +table_tiws_by_index_usage::delete_all_rows(void) +{ + reset_table_io_waits_by_table_handle(); + reset_table_io_waits_by_table(); + return 0; +} + +ha_rows +table_tiws_by_index_usage::get_row_count(void) +{ + return global_table_share_index_container.get_row_count(); +} + +table_tiws_by_index_usage::table_tiws_by_index_usage() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_tiws_by_index_usage::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_tiws_by_index_usage::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(wait_timer); + return 0; +} + +int table_tiws_by_index_usage::rnd_next(void) +{ + PFS_table_share *table_share; + bool has_more_table= true; + + for (m_pos.set_at(&m_next_pos); + has_more_table; + m_pos.next_table()) + { + table_share= global_table_share_container.get(m_pos.m_index_1, & has_more_table); + if (table_share != NULL) + { + if (table_share->m_enabled) + { + uint safe_key_count= sanitize_index_count(table_share->m_key_count); + if (m_pos.m_index_2 < safe_key_count) + { + make_row(table_share, m_pos.m_index_2); + m_next_pos.set_after(&m_pos); + return 0; + } + if (m_pos.m_index_2 <= MAX_INDEXES) + { + m_pos.m_index_2= MAX_INDEXES; + make_row(table_share, m_pos.m_index_2); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_tiws_by_index_usage::rnd_pos(const void *pos) +{ + PFS_table_share *table_share; + + set_position(pos); + + table_share= global_table_share_container.get(m_pos.m_index_1); + if (table_share != NULL) + { + if (table_share->m_enabled) + { + uint safe_key_count= sanitize_index_count(table_share->m_key_count); + if (m_pos.m_index_2 < safe_key_count) + { + make_row(table_share, m_pos.m_index_2); + return 0; + } + if (m_pos.m_index_2 == MAX_INDEXES) + { + make_row(table_share, m_pos.m_index_2); + return 0; + } + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_tiws_by_index_usage::make_row(PFS_table_share *pfs_share, + uint index) +{ + PFS_table_share_index *pfs_index; + pfs_optimistic_state lock; + + assert(index <= MAX_INDEXES); + + m_row_exists= false; + + pfs_share->m_lock.begin_optimistic_lock(&lock); + + PFS_index_io_stat_visitor visitor; + PFS_object_iterator::visit_table_indexes(pfs_share, index, & visitor); + + if (! visitor.m_stat.m_has_data) + { + pfs_index= pfs_share->find_index_stat(index); + if (pfs_index == NULL) + return; + } + else + { + pfs_index= pfs_share->find_index_stat(index); + } + + if (m_row.m_index.make_row(pfs_share, pfs_index, index)) + return; + + if (! pfs_share->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(m_normalizer, & visitor.m_stat); +} + +int table_tiws_by_index_usage::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* OBJECT_TYPE */ + case 1: /* SCHEMA_NAME */ + case 2: /* OBJECT_NAME */ + case 3: /* INDEX_NAME */ + m_row.m_index.set_field(f->field_index, f); + break; + case 4: /* COUNT_STAR */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_count); + break; + case 5: /* SUM */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_sum); + break; + case 6: /* MIN */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_min); + break; + case 7: /* AVG */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_avg); + break; + case 8: /* MAX */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_max); + break; + case 9: /* COUNT_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_count); + break; + case 10: /* SUM_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_sum); + break; + case 11: /* MIN_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_min); + break; + case 12: /* AVG_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_avg); + break; + case 13: /* MAX_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_max); + break; + case 14: /* COUNT_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_count); + break; + case 15: /* SUM_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_sum); + break; + case 16: /* MIN_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_min); + break; + case 17: /* AVG_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_avg); + break; + case 18: /* MAX_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_max); + break; + case 19: /* COUNT_FETCH */ + set_field_ulonglong(f, m_row.m_stat.m_fetch.m_count); + break; + case 20: /* SUM_FETCH */ + set_field_ulonglong(f, m_row.m_stat.m_fetch.m_sum); + break; + case 21: /* MIN_FETCH */ + set_field_ulonglong(f, m_row.m_stat.m_fetch.m_min); + break; + case 22: /* AVG_FETCH */ + set_field_ulonglong(f, m_row.m_stat.m_fetch.m_avg); + break; + case 23: /* MAX_FETCH */ + set_field_ulonglong(f, m_row.m_stat.m_fetch.m_max); + break; + case 24: /* COUNT_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_insert.m_count); + break; + case 25: /* SUM_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_insert.m_sum); + break; + case 26: /* MIN_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_insert.m_min); + break; + case 27: /* AVG_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_insert.m_avg); + break; + case 28: /* MAX_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_insert.m_max); + break; + case 29: /* COUNT_UPDATE */ + set_field_ulonglong(f, m_row.m_stat.m_update.m_count); + break; + case 30: /* SUM_UPDATE */ + set_field_ulonglong(f, m_row.m_stat.m_update.m_sum); + break; + case 31: /* MIN_UPDATE */ + set_field_ulonglong(f, m_row.m_stat.m_update.m_min); + break; + case 32: /* AVG_UPDATE */ + set_field_ulonglong(f, m_row.m_stat.m_update.m_avg); + break; + case 33: /* MAX_UPDATE */ + set_field_ulonglong(f, m_row.m_stat.m_update.m_max); + break; + case 34: /* COUNT_DELETE */ + set_field_ulonglong(f, m_row.m_stat.m_delete.m_count); + break; + case 35: /* SUM_DELETE */ + set_field_ulonglong(f, m_row.m_stat.m_delete.m_sum); + break; + case 36: /* MIN_DELETE */ + set_field_ulonglong(f, m_row.m_stat.m_delete.m_min); + break; + case 37: /* AVG_DELETE */ + set_field_ulonglong(f, m_row.m_stat.m_delete.m_avg); + break; + case 38: /* MAX_DELETE */ + set_field_ulonglong(f, m_row.m_stat.m_delete.m_max); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_tiws_by_index_usage.h b/storage/perfschema/table_tiws_by_index_usage.h new file mode 100644 index 00000000..6bc009fc --- /dev/null +++ b/storage/perfschema/table_tiws_by_index_usage.h @@ -0,0 +1,124 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_IO_WAIT_SUMMARY_BY_INDEX_USAGE_H +#define TABLE_IO_WAIT_SUMMARY_BY_INDEX_USAGE_H + +/** + @file storage/perfschema/table_tiws_by_index_usage.h + Table TABLE_IO_WAIT_SUMMARY_BY_INDEX_USAGE (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.TABLE_IO_WAIT_SUMMARY_BY_INDEX. +*/ +struct row_tiws_by_index_usage +{ + /** Column OBJECT_TYPE, SCHEMA_NAME, OBJECT_NAME, INDEX_NAME. */ + PFS_index_row m_index; + /** Columns COUNT/SUM/MIN/AVG/MAX (+_READ, +WRITE). */ + PFS_table_io_stat_row m_stat; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.TABLE_IO_WAIT_SUMMARY_BY_INDEX. + Index 1 on global_table_share_container (0 based) + Index 2 on index (0 based) +*/ +struct pos_tiws_by_index_usage : public PFS_double_index +{ + pos_tiws_by_index_usage() + : PFS_double_index(0, 0) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } + + inline void next_table(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** Table PERFORMANCE_SCHEMA.TABLE_IO_WAIT_SUMMARY_BY_INDEX. */ +class table_tiws_by_index_usage : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_tiws_by_index_usage(); + +public: + ~table_tiws_by_index_usage() = default; + +protected: + void make_row(PFS_table_share *table_share, uint index); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_tiws_by_index_usage m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_tiws_by_index_usage m_pos; + /** Next position. */ + pos_tiws_by_index_usage m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_tiws_by_table.cc b/storage/perfschema/table_tiws_by_table.cc new file mode 100644 index 00000000..ee4f54a3 --- /dev/null +++ b/storage/perfschema/table_tiws_by_table.cc @@ -0,0 +1,340 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_tiws_by_table.cc + Table TABLE_IO_WAITS_SUMMARY_BY_TABLE (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_tiws_by_table.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_tiws_by_table::m_table_lock; + +PFS_engine_table_share_state +table_tiws_by_table::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_tiws_by_table::m_share= +{ + { C_STRING_WITH_LEN("table_io_waits_summary_by_table") }, + &pfs_truncatable_acl, + table_tiws_by_table::create, + NULL, /* write_row */ + table_tiws_by_table::delete_all_rows, + table_tiws_by_table::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE table_io_waits_summary_by_table(" + "OBJECT_TYPE VARCHAR(64) comment 'Since this table records waits by table, always set to TABLE.'," + "OBJECT_SCHEMA VARCHAR(64) comment 'Schema name.'," + "OBJECT_NAME VARCHAR(64) comment 'Table name.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events and the sum of the x_READ and x_WRITE columns.'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.'," + "COUNT_READ BIGINT unsigned not null comment 'Number of all read operations, and the sum of the equivalent x_FETCH columns.'," + "SUM_TIMER_READ BIGINT unsigned not null comment 'Total wait time of all read operations that are timed.'," + "MIN_TIMER_READ BIGINT unsigned not null comment 'Minimum wait time of all read operations that are timed.'," + "AVG_TIMER_READ BIGINT unsigned not null comment 'Average wait time of all read operations that are timed.'," + "MAX_TIMER_READ BIGINT unsigned not null comment 'Maximum wait time of all read operations that are timed.'," + "COUNT_WRITE BIGINT unsigned not null comment 'Number of all write operations, and the sum of the equivalent x_INSERT, x_UPDATE and x_DELETE columns.'," + "SUM_TIMER_WRITE BIGINT unsigned not null comment 'Total wait time of all write operations that are timed.'," + "MIN_TIMER_WRITE BIGINT unsigned not null comment 'Minimum wait time of all write operations that are timed.'," + "AVG_TIMER_WRITE BIGINT unsigned not null comment 'Average wait time of all write operations that are timed.'," + "MAX_TIMER_WRITE BIGINT unsigned not null comment 'Maximum wait time of all write operations that are timed.'," + "COUNT_FETCH BIGINT unsigned not null comment 'Number of all fetch operations.'," + "SUM_TIMER_FETCH BIGINT unsigned not null comment 'Total wait time of all fetch operations that are timed.'," + "MIN_TIMER_FETCH BIGINT unsigned not null comment 'Minimum wait time of all fetch operations that are timed.'," + "AVG_TIMER_FETCH BIGINT unsigned not null comment 'Average wait time of all fetch operations that are timed.'," + "MAX_TIMER_FETCH BIGINT unsigned not null comment 'Maximum wait time of all fetch operations that are timed.'," + "COUNT_INSERT BIGINT unsigned not null comment 'Number of all insert operations.'," + "SUM_TIMER_INSERT BIGINT unsigned not null comment 'Total wait time of all insert operations that are timed.'," + "MIN_TIMER_INSERT BIGINT unsigned not null comment 'Minimum wait time of all insert operations that are timed.'," + "AVG_TIMER_INSERT BIGINT unsigned not null comment 'Average wait time of all insert operations that are timed.'," + "MAX_TIMER_INSERT BIGINT unsigned not null comment 'Maximum wait time of all insert operations that are timed.'," + "COUNT_UPDATE BIGINT unsigned not null comment 'Number of all update operations.'," + "SUM_TIMER_UPDATE BIGINT unsigned not null comment 'Total wait time of all update operations that are timed.'," + "MIN_TIMER_UPDATE BIGINT unsigned not null comment 'Minimum wait time of all update operations that are timed.'," + "AVG_TIMER_UPDATE BIGINT unsigned not null comment 'Average wait time of all update operations that are timed.'," + "MAX_TIMER_UPDATE BIGINT unsigned not null comment 'Maximum wait time of all update operations that are timed.'," + "COUNT_DELETE BIGINT unsigned not null comment 'Number of all delete operations.'," + "SUM_TIMER_DELETE BIGINT unsigned not null comment 'Total wait time of all delete operations that are timed.'," + "MIN_TIMER_DELETE BIGINT unsigned not null comment 'Minimum wait time of all delete operations that are timed.'," + "AVG_TIMER_DELETE BIGINT unsigned not null comment 'Average wait time of all delete operations that are timed.'," + "MAX_TIMER_DELETE BIGINT unsigned not null comment 'Maximum wait time of all delete operations that are timed.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_tiws_by_table::create(void) +{ + return new table_tiws_by_table(); +} + +int +table_tiws_by_table::delete_all_rows(void) +{ + reset_table_io_waits_by_table_handle(); + reset_table_io_waits_by_table(); + return 0; +} + +ha_rows +table_tiws_by_table::get_row_count(void) +{ + return global_table_share_container.get_row_count(); +} + +table_tiws_by_table::table_tiws_by_table() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_tiws_by_table::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_tiws_by_table::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(wait_timer); + return 0; +} + +int table_tiws_by_table::rnd_next(void) +{ + PFS_table_share *pfs; + + m_pos.set_at(&m_next_pos); + PFS_table_share_iterator it= global_table_share_container.iterate(m_pos.m_index); + do + { + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + if (pfs->m_enabled) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } while (pfs != NULL); + + return HA_ERR_END_OF_FILE; +} + +int +table_tiws_by_table::rnd_pos(const void *pos) +{ + PFS_table_share *pfs; + + set_position(pos); + + pfs= global_table_share_container.get(m_pos.m_index); + if (pfs != NULL) + { + if (pfs->m_enabled) + { + make_row(pfs); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_tiws_by_table::make_row(PFS_table_share *share) +{ + pfs_optimistic_state lock; + + m_row_exists= false; + + share->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_object.make_row(share)) + return; + + PFS_table_io_stat_visitor visitor; + PFS_object_iterator::visit_tables(share, & visitor); + + if (! share->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(m_normalizer, &visitor.m_stat); +} + +int table_tiws_by_table::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* OBJECT_TYPE */ + case 1: /* SCHEMA_NAME */ + case 2: /* OBJECT_NAME */ + m_row.m_object.set_field(f->field_index, f); + break; + case 3: /* COUNT_STAR */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_count); + break; + case 4: /* SUM */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_sum); + break; + case 5: /* MIN */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_min); + break; + case 6: /* AVG */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_avg); + break; + case 7: /* MAX */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_max); + break; + case 8: /* COUNT_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_count); + break; + case 9: /* SUM_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_sum); + break; + case 10: /* MIN_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_min); + break; + case 11: /* AVG_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_avg); + break; + case 12: /* MAX_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_max); + break; + case 13: /* COUNT_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_count); + break; + case 14: /* SUM_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_sum); + break; + case 15: /* MIN_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_min); + break; + case 16: /* AVG_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_avg); + break; + case 17: /* MAX_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_max); + break; + case 18: /* COUNT_FETCH */ + set_field_ulonglong(f, m_row.m_stat.m_fetch.m_count); + break; + case 19: /* SUM_FETCH */ + set_field_ulonglong(f, m_row.m_stat.m_fetch.m_sum); + break; + case 20: /* MIN_FETCH */ + set_field_ulonglong(f, m_row.m_stat.m_fetch.m_min); + break; + case 21: /* AVG_FETCH */ + set_field_ulonglong(f, m_row.m_stat.m_fetch.m_avg); + break; + case 22: /* MAX_FETCH */ + set_field_ulonglong(f, m_row.m_stat.m_fetch.m_max); + break; + case 23: /* COUNT_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_insert.m_count); + break; + case 24: /* SUM_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_insert.m_sum); + break; + case 25: /* MIN_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_insert.m_min); + break; + case 26: /* AVG_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_insert.m_avg); + break; + case 27: /* MAX_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_insert.m_max); + break; + case 28: /* COUNT_UPDATE */ + set_field_ulonglong(f, m_row.m_stat.m_update.m_count); + break; + case 29: /* SUM_UPDATE */ + set_field_ulonglong(f, m_row.m_stat.m_update.m_sum); + break; + case 30: /* MIN_UPDATE */ + set_field_ulonglong(f, m_row.m_stat.m_update.m_min); + break; + case 31: /* AVG_UPDATE */ + set_field_ulonglong(f, m_row.m_stat.m_update.m_avg); + break; + case 32: /* MAX_UPDATE */ + set_field_ulonglong(f, m_row.m_stat.m_update.m_max); + break; + case 33: /* COUNT_DELETE */ + set_field_ulonglong(f, m_row.m_stat.m_delete.m_count); + break; + case 34: /* SUM_DELETE */ + set_field_ulonglong(f, m_row.m_stat.m_delete.m_sum); + break; + case 35: /* MIN_DELETE */ + set_field_ulonglong(f, m_row.m_stat.m_delete.m_min); + break; + case 36: /* AVG_DELETE */ + set_field_ulonglong(f, m_row.m_stat.m_delete.m_avg); + break; + case 37: /* MAX_DELETE */ + set_field_ulonglong(f, m_row.m_stat.m_delete.m_max); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_tiws_by_table.h b/storage/perfschema/table_tiws_by_table.h new file mode 100644 index 00000000..2970ee64 --- /dev/null +++ b/storage/perfschema/table_tiws_by_table.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_IO_WAITS_SUMMARY_BY_TABLE_H +#define TABLE_IO_WAITS_SUMMARY_BY_TABLE_H + +/** + @file storage/perfschema/table_tiws_by_table.h + Table TABLE_IO_WAITS_SUMMARY_BY_TABLE (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.TABLE_IO_WAITS_SUMMARY_BY_TABLE. +*/ +struct row_tiws_by_table +{ + /** Column OBJECT_TYPE, SCHEMA_NAME, OBJECT_NAME. */ + PFS_object_row m_object; + /** Columns COUNT/SUM/MIN/AVG/MAX (+_READ, +WRITE). */ + PFS_table_io_stat_row m_stat; +}; + +/** Table PERFORMANCE_SCHEMA.TABLE_IO_WAITS_SUMMARY_BY_TABLE. */ +class table_tiws_by_table : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_tiws_by_table(); + +public: + ~table_tiws_by_table() = default; + +protected: + void make_row(PFS_table_share *table_share); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_tiws_by_table m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_tlws_by_table.cc b/storage/perfschema/table_tlws_by_table.cc new file mode 100644 index 00000000..709f22a1 --- /dev/null +++ b/storage/perfschema/table_tlws_by_table.cc @@ -0,0 +1,492 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/table_tlws_by_table.cc + Table TABLE_LOCK_WAITS_SUMMARY_BY_TABLE (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_tlws_by_table.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" +#include "field.h" + +THR_LOCK table_tlws_by_table::m_table_lock; + +PFS_engine_table_share_state +table_tlws_by_table::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_tlws_by_table::m_share= +{ + { C_STRING_WITH_LEN("table_lock_waits_summary_by_table") }, + &pfs_truncatable_acl, + table_tlws_by_table::create, + NULL, /* write_row */ + table_tlws_by_table::delete_all_rows, + table_tlws_by_table::get_row_count, + sizeof(PFS_simple_index), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE table_lock_waits_summary_by_table(" + "OBJECT_TYPE VARCHAR(64) comment 'Since this table records waits by table, always set to TABLE.'," + "OBJECT_SCHEMA VARCHAR(64) comment 'Schema name.'," + "OBJECT_NAME VARCHAR(64) comment 'Table name.'," + "COUNT_STAR BIGINT unsigned not null comment 'Number of summarized events and the sum of the x_READ and x_WRITE columns.'," + "SUM_TIMER_WAIT BIGINT unsigned not null comment 'Total wait time of the summarized events that are timed.'," + "MIN_TIMER_WAIT BIGINT unsigned not null comment 'Minimum wait time of the summarized events that are timed.'," + "AVG_TIMER_WAIT BIGINT unsigned not null comment 'Average wait time of the summarized events that are timed.'," + "MAX_TIMER_WAIT BIGINT unsigned not null comment 'Maximum wait time of the summarized events that are timed.'," + "COUNT_READ BIGINT unsigned not null comment 'Number of all read operations, and the sum of the equivalent x_READ_NORMAL, x_READ_WITH_SHARED_LOCKS, x_READ_HIGH_PRIORITY and x_READ_NO_INSERT columns.'," + "SUM_TIMER_READ BIGINT unsigned not null comment 'Total wait time of all read operations that are timed.'," + "MIN_TIMER_READ BIGINT unsigned not null comment 'Minimum wait time of all read operations that are timed.'," + "AVG_TIMER_READ BIGINT unsigned not null comment 'Average wait time of all read operations that are timed.'," + "MAX_TIMER_READ BIGINT unsigned not null comment 'Maximum wait time of all read operations that are timed.'," + "COUNT_WRITE BIGINT unsigned not null comment 'Number of all write operations, and the sum of the equivalent x_WRITE_ALLOW_WRITE, x_WRITE_CONCURRENT_INSERT, x_WRITE_DELAYED, x_WRITE_LOW_PRIORITY and x_WRITE_NORMAL columns.'," + "SUM_TIMER_WRITE BIGINT unsigned not null comment 'Total wait time of all write operations that are timed.'," + "MIN_TIMER_WRITE BIGINT unsigned not null comment 'Minimum wait time of all write operations that are timed.'," + "AVG_TIMER_WRITE BIGINT unsigned not null comment 'Average wait time of all write operations that are timed.'," + "MAX_TIMER_WRITE BIGINT unsigned not null comment 'Maximum wait time of all write operations that are timed.'," + "COUNT_READ_NORMAL BIGINT unsigned not null comment 'Number of all internal read normal locks.'," + "SUM_TIMER_READ_NORMAL BIGINT unsigned not null comment 'Total wait time of all internal read normal locks that are timed.'," + "MIN_TIMER_READ_NORMAL BIGINT unsigned not null comment 'Minimum wait time of all internal read normal locks that are timed.'," + "AVG_TIMER_READ_NORMAL BIGINT unsigned not null comment 'Average wait time of all internal read normal locks that are timed.'," + "MAX_TIMER_READ_NORMAL BIGINT unsigned not null comment 'Maximum wait time of all internal read normal locks that are timed.'," + "COUNT_READ_WITH_SHARED_LOCKS BIGINT unsigned not null comment 'Number of all internal read with shared locks.'," + "SUM_TIMER_READ_WITH_SHARED_LOCKS BIGINT unsigned not null comment 'Total wait time of all internal read with shared locks that are timed.'," + "MIN_TIMER_READ_WITH_SHARED_LOCKS BIGINT unsigned not null comment 'Minimum wait time of all internal read with shared locks that are timed.'," + "AVG_TIMER_READ_WITH_SHARED_LOCKS BIGINT unsigned not null comment 'Average wait time of all internal read with shared locks that are timed.'," + "MAX_TIMER_READ_WITH_SHARED_LOCKS BIGINT unsigned not null comment 'Maximum wait time of all internal read with shared locks that are timed.'," + "COUNT_READ_HIGH_PRIORITY BIGINT unsigned not null comment 'Number of all internal read high priority locks.'," + "SUM_TIMER_READ_HIGH_PRIORITY BIGINT unsigned not null comment 'Total wait time of all internal read high priority locks that are timed.'," + "MIN_TIMER_READ_HIGH_PRIORITY BIGINT unsigned not null comment 'Minimum wait time of all internal read high priority locks that are timed.'," + "AVG_TIMER_READ_HIGH_PRIORITY BIGINT unsigned not null comment 'Average wait time of all internal read high priority locks that are timed.'," + "MAX_TIMER_READ_HIGH_PRIORITY BIGINT unsigned not null comment 'Maximum wait time of all internal read high priority locks that are timed.'," + "COUNT_READ_NO_INSERT BIGINT unsigned not null comment 'Number of all internal read no insert locks.'," + "SUM_TIMER_READ_NO_INSERT BIGINT unsigned not null comment 'Total wait time of all internal read no insert locks that are timed.'," + "MIN_TIMER_READ_NO_INSERT BIGINT unsigned not null comment 'Minimum wait time of all internal read no insert locks that are timed.'," + "AVG_TIMER_READ_NO_INSERT BIGINT unsigned not null comment 'Average wait time of all internal read no insert locks that are timed.'," + "MAX_TIMER_READ_NO_INSERT BIGINT unsigned not null comment 'Maximum wait time of all internal read no insert locks that are timed.'," + "COUNT_READ_EXTERNAL BIGINT unsigned not null comment 'Number of all external read locks.'," + "SUM_TIMER_READ_EXTERNAL BIGINT unsigned not null comment 'Total wait time of all external read locks that are timed.'," + "MIN_TIMER_READ_EXTERNAL BIGINT unsigned not null comment 'Minimum wait time of all external read locks that are timed.'," + "AVG_TIMER_READ_EXTERNAL BIGINT unsigned not null comment 'Average wait time of all external read locks that are timed.'," + "MAX_TIMER_READ_EXTERNAL BIGINT unsigned not null comment 'Maximum wait time of all external read locks that are timed.'," + "COUNT_WRITE_ALLOW_WRITE BIGINT unsigned not null comment 'Number of all internal read normal locks.'," + "SUM_TIMER_WRITE_ALLOW_WRITE BIGINT unsigned not null comment 'Total wait time of all internal write allow write locks that are timed.'," + "MIN_TIMER_WRITE_ALLOW_WRITE BIGINT unsigned not null comment 'Minimum wait time of all internal write allow write locks that are timed.'," + "AVG_TIMER_WRITE_ALLOW_WRITE BIGINT unsigned not null comment 'Average wait time of all internal write allow write locks that are timed.'," + "MAX_TIMER_WRITE_ALLOW_WRITE BIGINT unsigned not null comment 'Maximum wait time of all internal write allow write locks that are timed.'," + "COUNT_WRITE_CONCURRENT_INSERT BIGINT unsigned not null comment 'Number of all internal concurrent insert write locks.'," + "SUM_TIMER_WRITE_CONCURRENT_INSERT BIGINT unsigned not null comment 'Total wait time of all internal concurrent insert write locks that are timed.'," + "MIN_TIMER_WRITE_CONCURRENT_INSERT BIGINT unsigned not null comment 'Minimum wait time of all internal concurrent insert write locks that are timed.'," + "AVG_TIMER_WRITE_CONCURRENT_INSERT BIGINT unsigned not null comment 'Average wait time of all internal concurrent insert write locks that are timed.'," + "MAX_TIMER_WRITE_CONCURRENT_INSERT BIGINT unsigned not null comment 'Maximum wait time of all internal concurrent insert write locks that are timed.'," + "COUNT_WRITE_DELAYED BIGINT unsigned not null comment 'Number of all internal write delayed locks.'," + "SUM_TIMER_WRITE_DELAYED BIGINT unsigned not null comment 'Total wait time of all internal write delayed locks that are timed.'," + "MIN_TIMER_WRITE_DELAYED BIGINT unsigned not null comment 'Minimum wait time of all internal write delayed locks that are timed.'," + "AVG_TIMER_WRITE_DELAYED BIGINT unsigned not null comment 'Average wait time of all internal write delayed locks that are timed.'," + "MAX_TIMER_WRITE_DELAYED BIGINT unsigned not null comment 'Maximum wait time of all internal write delayed locks that are timed.'," + "COUNT_WRITE_LOW_PRIORITY BIGINT unsigned not null comment 'Number of all internal write low priority locks.'," + "SUM_TIMER_WRITE_LOW_PRIORITY BIGINT unsigned not null comment 'Total wait time of all internal write low priority locks that are timed.'," + "MIN_TIMER_WRITE_LOW_PRIORITY BIGINT unsigned not null comment 'Minimum wait time of all internal write low priority locks that are timed.'," + "AVG_TIMER_WRITE_LOW_PRIORITY BIGINT unsigned not null comment 'Average wait time of all internal write low priority locks that are timed.'," + "MAX_TIMER_WRITE_LOW_PRIORITY BIGINT unsigned not null comment 'Maximum wait time of all internal write low priority locks that are timed.'," + "COUNT_WRITE_NORMAL BIGINT unsigned not null comment 'Number of all internal write normal locks.'," + "SUM_TIMER_WRITE_NORMAL BIGINT unsigned not null comment 'Total wait time of all internal write normal locks that are timed.'," + "MIN_TIMER_WRITE_NORMAL BIGINT unsigned not null comment 'Minimum wait time of all internal write normal locks that are timed.'," + "AVG_TIMER_WRITE_NORMAL BIGINT unsigned not null comment 'Average wait time of all internal write normal locks that are timed.'," + "MAX_TIMER_WRITE_NORMAL BIGINT unsigned not null comment 'Maximum wait time of all internal write normal locks that are timed.'," + "COUNT_WRITE_EXTERNAL BIGINT unsigned not null comment 'Number of all external write locks.'," + "SUM_TIMER_WRITE_EXTERNAL BIGINT unsigned not null comment 'Total wait time of all external write locks that are timed.'," + "MIN_TIMER_WRITE_EXTERNAL BIGINT unsigned not null comment 'Minimum wait time of all external write locks that are timed.'," + "AVG_TIMER_WRITE_EXTERNAL BIGINT unsigned not null comment 'Average wait time of all external write locks that are timed.'," + "MAX_TIMER_WRITE_EXTERNAL BIGINT unsigned not null comment 'Maximum wait time of all external write locks that are timed.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_tlws_by_table::create(void) +{ + return new table_tlws_by_table(); +} + +int +table_tlws_by_table::delete_all_rows(void) +{ + reset_table_lock_waits_by_table_handle(); + reset_table_lock_waits_by_table(); + return 0; +} + +ha_rows +table_tlws_by_table::get_row_count(void) +{ + return global_table_share_container.get_row_count(); +} + +table_tlws_by_table::table_tlws_by_table() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_tlws_by_table::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_tlws_by_table::rnd_init(bool scan) +{ + m_normalizer= time_normalizer::get(wait_timer); + return 0; +} + +int table_tlws_by_table::rnd_next(void) +{ + PFS_table_share *pfs; + + m_pos.set_at(&m_next_pos); + PFS_table_share_iterator it= global_table_share_container.iterate(m_pos.m_index); + do + { + pfs= it.scan_next(& m_pos.m_index); + if (pfs != NULL) + { + if (pfs->m_enabled) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } while (pfs != NULL); + + return HA_ERR_END_OF_FILE; +} + +int +table_tlws_by_table::rnd_pos(const void *pos) +{ + PFS_table_share *pfs; + + set_position(pos); + + pfs= global_table_share_container.get(m_pos.m_index); + if (pfs != NULL) + { + if (pfs->m_enabled) + { + make_row(pfs); + return 0; + } + } + + return HA_ERR_RECORD_DELETED; +} + +void table_tlws_by_table::make_row(PFS_table_share *share) +{ + pfs_optimistic_state lock; + + m_row_exists= false; + + share->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_object.make_row(share)) + return; + + PFS_table_lock_stat_visitor visitor; + PFS_object_iterator::visit_tables(share, & visitor); + + if (! share->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; + m_row.m_stat.set(m_normalizer, &visitor.m_stat); +} + +int table_tlws_by_table::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* OBJECT_TYPE */ + case 1: /* SCHEMA_NAME */ + case 2: /* OBJECT_NAME */ + m_row.m_object.set_field(f->field_index, f); + break; + case 3: /* COUNT_STAR */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_count); + break; + case 4: /* SUM_TIMER */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_sum); + break; + case 5: /* MIN_TIMER */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_min); + break; + case 6: /* AVG_TIMER */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_avg); + break; + case 7: /* MAX_TIMER */ + set_field_ulonglong(f, m_row.m_stat.m_all.m_max); + break; + case 8: /* COUNT_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_count); + break; + case 9: /* SUM_TIMER_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_sum); + break; + case 10: /* MIN_TIMER_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_min); + break; + case 11: /* AVG_TIMER_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_avg); + break; + case 12: /* MAX_TIMER_READ */ + set_field_ulonglong(f, m_row.m_stat.m_all_read.m_max); + break; + case 13: /* COUNT_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_count); + break; + case 14: /* SUM_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_sum); + break; + case 15: /* MIN_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_min); + break; + case 16: /* AVG_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_avg); + break; + case 17: /* MAX_TIMER_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_all_write.m_max); + break; + + case 18: /* COUNT_READ_NORMAL */ + set_field_ulonglong(f, m_row.m_stat.m_read_normal.m_count); + break; + case 19: /* SUM_TIMER_READ_NORMAL */ + set_field_ulonglong(f, m_row.m_stat.m_read_normal.m_sum); + break; + case 20: /* MIN_TIMER_READ_NORMAL */ + set_field_ulonglong(f, m_row.m_stat.m_read_normal.m_min); + break; + case 21: /* AVG_TIMER_READ_NORMAL */ + set_field_ulonglong(f, m_row.m_stat.m_read_normal.m_avg); + break; + case 22: /* MAX_TIMER_READ_NORMAL */ + set_field_ulonglong(f, m_row.m_stat.m_read_normal.m_max); + break; + + case 23: /* COUNT_READ_WITH_SHARED_LOCKS */ + set_field_ulonglong(f, m_row.m_stat.m_read_with_shared_locks.m_count); + break; + case 24: /* SUM_TIMER_READ_WITH_SHARED_LOCKS */ + set_field_ulonglong(f, m_row.m_stat.m_read_with_shared_locks.m_sum); + break; + case 25: /* MIN_TIMER_READ_WITH_SHARED_LOCKS */ + set_field_ulonglong(f, m_row.m_stat.m_read_with_shared_locks.m_min); + break; + case 26: /* AVG_TIMER_READ_WITH_SHARED_LOCKS */ + set_field_ulonglong(f, m_row.m_stat.m_read_with_shared_locks.m_avg); + break; + case 27: /* MAX_TIMER_READ_WITH_SHARED_LOCKS */ + set_field_ulonglong(f, m_row.m_stat.m_read_with_shared_locks.m_max); + break; + + case 28: /* COUNT_READ_HIGH_PRIORITY */ + set_field_ulonglong(f, m_row.m_stat.m_read_high_priority.m_count); + break; + case 29: /* SUM_TIMER_READ_HIGH_PRIORITY */ + set_field_ulonglong(f, m_row.m_stat.m_read_high_priority.m_sum); + break; + case 30: /* MIN_TIMER_READ_HIGH_PRIORITY */ + set_field_ulonglong(f, m_row.m_stat.m_read_high_priority.m_min); + break; + case 31: /* AVG_TIMER_READ_HIGH_PRIORITY */ + set_field_ulonglong(f, m_row.m_stat.m_read_high_priority.m_avg); + break; + case 32: /* MAX_TIMER_READ_HIGH_PRIORITY */ + set_field_ulonglong(f, m_row.m_stat.m_read_high_priority.m_max); + break; + + case 33: /* COUNT_READ_NO_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_read_no_insert.m_count); + break; + case 34: /* SUM_TIMER_READ_NO_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_read_no_insert.m_sum); + break; + case 35: /* MIN_TIMER_READ_NO_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_read_no_insert.m_min); + break; + case 36: /* AVG_TIMER_READ_NO_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_read_no_insert.m_avg); + break; + case 37: /* MAX_TIMER_READ_NO_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_read_no_insert.m_max); + break; + + case 38: /* COUNT_READ_EXTERNAL */ + set_field_ulonglong(f, m_row.m_stat.m_read_external.m_count); + break; + case 39: /* SUM_TIMER_READ_EXTERNAL */ + set_field_ulonglong(f, m_row.m_stat.m_read_external.m_sum); + break; + case 40: /* MIN_TIMER_READ_EXTERNAL */ + set_field_ulonglong(f, m_row.m_stat.m_read_external.m_min); + break; + case 41: /* AVG_TIMER_READ_EXTERNAL */ + set_field_ulonglong(f, m_row.m_stat.m_read_external.m_avg); + break; + case 42: /* MAX_TIMER_READ_EXTERNAL */ + set_field_ulonglong(f, m_row.m_stat.m_read_external.m_max); + break; + + case 43: /* COUNT_WRITE_ALLOW_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_write_allow_write.m_count); + break; + case 44: /* SUM_TIMER_WRITE_ALLOW_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_write_allow_write.m_sum); + break; + case 45: /* MIN_TIMER_WRITE_ALLOW_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_write_allow_write.m_min); + break; + case 46: /* AVG_TIMER_WRITE_ALLOW_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_write_allow_write.m_avg); + break; + case 47: /* MAX_TIMER_WRITE_ALLOW_WRITE */ + set_field_ulonglong(f, m_row.m_stat.m_write_allow_write.m_max); + break; + + case 48: /* COUNT_WRITE_CONCURRENT_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_write_concurrent_insert.m_count); + break; + case 49: /* SUM_TIMER_WRITE_CONCURRENT_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_write_concurrent_insert.m_sum); + break; + case 50: /* MIN_TIMER_WRITE_CONCURRENT_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_write_concurrent_insert.m_min); + break; + case 51: /* AVG_TIMER_WRITE_CONCURRENT_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_write_concurrent_insert.m_avg); + break; + case 52: /* MAX_TIMER_WRITE_CONCURRENT_INSERT */ + set_field_ulonglong(f, m_row.m_stat.m_write_concurrent_insert.m_max); + break; + + case 53: /* COUNT_WRITE_DELAYED */ + set_field_ulonglong(f, m_row.m_stat.m_write_delayed.m_count); + break; + case 54: /* SUM_TIMER_WRITE_DELAYED */ + set_field_ulonglong(f, m_row.m_stat.m_write_delayed.m_sum); + break; + case 55: /* MIN_TIMER_WRITE_DELAYED */ + set_field_ulonglong(f, m_row.m_stat.m_write_delayed.m_min); + break; + case 56: /* AVG_TIMER_WRITE_DELAYED */ + set_field_ulonglong(f, m_row.m_stat.m_write_delayed.m_avg); + break; + case 57: /* MAX_TIMER_WRITE_DELAYED */ + set_field_ulonglong(f, m_row.m_stat.m_write_delayed.m_max); + break; + + case 58: /* COUNT_WRITE_LOW_PRIORITY */ + set_field_ulonglong(f, m_row.m_stat.m_write_low_priority.m_count); + break; + case 59: /* SUM_TIMER_WRITE_LOW_PRIORITY */ + set_field_ulonglong(f, m_row.m_stat.m_write_low_priority.m_sum); + break; + case 60: /* MIN_TIMER_WRITE_LOW_PRIORITY */ + set_field_ulonglong(f, m_row.m_stat.m_write_low_priority.m_min); + break; + case 61: /* AVG_TIMER_WRITE_LOW_PRIORITY */ + set_field_ulonglong(f, m_row.m_stat.m_write_low_priority.m_avg); + break; + case 62: /* MAX_TIMER_WRITE_LOW_PRIORITY */ + set_field_ulonglong(f, m_row.m_stat.m_write_low_priority.m_max); + break; + + case 63: /* COUNT_WRITE_NORMAL */ + set_field_ulonglong(f, m_row.m_stat.m_write_normal.m_count); + break; + case 64: /* SUM_TIMER_WRITE_NORMAL */ + set_field_ulonglong(f, m_row.m_stat.m_write_normal.m_sum); + break; + case 65: /* MIN_TIMER_WRITE_NORMAL */ + set_field_ulonglong(f, m_row.m_stat.m_write_normal.m_min); + break; + case 66: /* AVG_TIMER_WRITE_NORMAL */ + set_field_ulonglong(f, m_row.m_stat.m_write_normal.m_avg); + break; + case 67: /* MAX_TIMER_WRITE_NORMAL */ + set_field_ulonglong(f, m_row.m_stat.m_write_normal.m_max); + break; + + case 68: /* COUNT_WRITE_EXTERNAL */ + set_field_ulonglong(f, m_row.m_stat.m_write_external.m_count); + break; + case 69: /* SUM_TIMER_WRITE_EXTERNAL */ + set_field_ulonglong(f, m_row.m_stat.m_write_external.m_sum); + break; + case 70: /* MIN_TIMER_WRITE_EXTERNAL */ + set_field_ulonglong(f, m_row.m_stat.m_write_external.m_min); + break; + case 71: /* AVG_TIMER_WRITE_EXTERNAL */ + set_field_ulonglong(f, m_row.m_stat.m_write_external.m_avg); + break; + case 72: /* MAX_TIMER_WRITE_EXTERNAL */ + set_field_ulonglong(f, m_row.m_stat.m_write_external.m_max); + break; + + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_tlws_by_table.h b/storage/perfschema/table_tlws_by_table.h new file mode 100644 index 00000000..5755e33f --- /dev/null +++ b/storage/perfschema/table_tlws_by_table.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_LOCK_WAITS_SUMMARY_BY_TABLE_H +#define TABLE_LOCK_WAITS_SUMMARY_BY_TABLE_H + +/** + @file storage/perfschema/table_tlws_by_table.h + Table TABLE_LOCK_WAITS_SUMMARY_BY_TABLE (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.TABLE_LOCK_WAITS_SUMMARY_BY_TABLE. +*/ +struct row_tlws_by_table +{ + /** Column OBJECT_TYPE, SCHEMA_NAME, OBJECT_NAME. */ + PFS_object_row m_object; + /** Columns COUNT/SUM/MIN/AVG/MAX READ/WRITE/READ_NORMAL/etc. */ + PFS_table_lock_stat_row m_stat; +}; + +/** Table PERFORMANCE_SCHEMA.TABLE_LOCK_WAITS_SUMMARY_BY_TABLE. */ +class table_tlws_by_table : public PFS_engine_table +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_tlws_by_table(); + +public: + ~table_tlws_by_table() = default; + +protected: + void make_row(PFS_table_share *table_share); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_tlws_by_table m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_users.cc b/storage/perfschema/table_users.cc new file mode 100644 index 00000000..b30c36e1 --- /dev/null +++ b/storage/perfschema/table_users.cc @@ -0,0 +1,156 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "my_global.h" +#include "my_thread.h" +#include "table_users.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_account.h" +#include "pfs_user.h" +#include "pfs_visitor.h" +#include "pfs_memory.h" +#include "pfs_status.h" +#include "field.h" + +THR_LOCK table_users::m_table_lock; + +PFS_engine_table_share_state +table_users::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_users::m_share= +{ + { C_STRING_WITH_LEN("users") }, + &pfs_truncatable_acl, + table_users::create, + NULL, /* write_row */ + table_users::delete_all_rows, + cursor_by_user::get_row_count, + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE users(" + "USER CHAR(" USERNAME_CHAR_LENGTH_STR ") collate utf8_bin default null comment 'The connection''s client user name for the connection, or NULL if an internal thread.'," + "CURRENT_CONNECTIONS bigint not null comment 'Current connections for the user.'," + "TOTAL_CONNECTIONS bigint not null comment 'Total connections for the user.')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* table_users::create() +{ + return new table_users(); +} + +int +table_users::delete_all_rows(void) +{ + reset_events_waits_by_thread(); + reset_events_waits_by_account(); + reset_events_waits_by_user(); + reset_events_stages_by_thread(); + reset_events_stages_by_account(); + reset_events_stages_by_user(); + reset_events_statements_by_thread(); + reset_events_statements_by_account(); + reset_events_statements_by_user(); + reset_events_transactions_by_thread(); + reset_events_transactions_by_account(); + reset_events_transactions_by_user(); + reset_memory_by_thread(); + reset_memory_by_account(); + reset_memory_by_user(); + reset_status_by_thread(); + reset_status_by_account(); + reset_status_by_user(); + purge_all_account(); + purge_all_user(); + return 0; +} + +table_users::table_users() + : cursor_by_user(& m_share), + m_row_exists(false) +{} + +void table_users::make_row(PFS_user *pfs) +{ + pfs_optimistic_state lock; + + m_row_exists= false; + pfs->m_lock.begin_optimistic_lock(&lock); + + if (m_row.m_user.make_row(pfs)) + return; + + PFS_connection_stat_visitor visitor; + PFS_connection_iterator::visit_user(pfs, + true, /* accounts */ + true, /* threads */ + false, /* THDs */ + & visitor); + + if (! pfs->m_lock.end_optimistic_lock(& lock)) + return; + + m_row.m_connection_stat.set(& visitor.m_stat); + m_row_exists= true; +} + +int table_users::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* USER */ + m_row.m_user.set_field(f); + break; + case 1: /* CURRENT_CONNECTIONS */ + case 2: /* TOTAL_CONNECTIONS */ + m_row.m_connection_stat.set_field(f->field_index - 1, f); + break; + default: + assert(false); + } + } + } + return 0; +} + diff --git a/storage/perfschema/table_users.h b/storage/perfschema/table_users.h new file mode 100644 index 00000000..9255ec95 --- /dev/null +++ b/storage/perfschema/table_users.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef TABLE_USERS_H +#define TABLE_USERS_H + +#include "pfs_column_types.h" +#include "cursor_by_user.h" +#include "table_helper.h" + +struct PFS_user; + +/** + \addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of PERFORMANCE_SCHEMA.USERS. +*/ +struct row_users +{ + /** Column USER. */ + PFS_user_row m_user; + /** Columns CURRENT_CONNECTIONS, TOTAL_CONNECTIONS. */ + PFS_connection_stat_row m_connection_stat; +}; + +/** Table PERFORMANCE_SCHEMA.USERS. */ +class table_users : public cursor_by_user +{ +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + /** Table builder */ + static PFS_engine_table* create(); + static int delete_all_rows(); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + +protected: + table_users(); + +public: + ~table_users() = default; + +private: + virtual void make_row(PFS_user *pfs); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + + /** Current row. */ + row_users m_row; + /** True if the current row exists. */ + bool m_row_exists; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_uvar_by_thread.cc b/storage/perfschema/table_uvar_by_thread.cc new file mode 100644 index 00000000..74b6165d --- /dev/null +++ b/storage/perfschema/table_uvar_by_thread.cc @@ -0,0 +1,336 @@ +/* Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_uvar_by_thread.cc + Table USER_VARIABLES_BY_THREAD (implementation). +*/ + +#include "my_global.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_uvar_by_thread.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "pfs_buffer_container.h" + +/* Iteration on THD from the sql layer. */ +#include "sql_class.h" +#include "mysqld_thd_manager.h" + +class Find_thd_user_var : public Find_THD_Impl +{ +public: + Find_thd_user_var(THD *unsafe_thd) + : m_unsafe_thd(unsafe_thd) + {} + + virtual bool operator()(THD *thd) + { + if (thd != m_unsafe_thd) + return false; + + if (thd->user_vars.records == 0) + return false; + + mysql_mutex_lock(&thd->LOCK_thd_data); + return true; + } + +private: + THD *m_unsafe_thd; +}; + +void User_variables::materialize(PFS_thread *pfs, THD *thd) +{ + reset(); + + m_pfs= pfs; + m_thread_internal_id= pfs->m_thread_internal_id; + m_array.reserve(thd->user_vars.records); + + user_var_entry *sql_uvar; + + uint index= 0; + User_variable empty; + + /* Protects thd->user_vars. */ + mysql_mutex_assert_owner(&thd->LOCK_thd_data); + + for (;;) + { + sql_uvar= reinterpret_cast<user_var_entry*> (my_hash_element(& thd->user_vars, index)); + if (sql_uvar == NULL) + break; + + /* + m_array is a container of objects (not pointers) + + Naive code can: + - build locally a new entry + - add it to the container + but this causes useless object construction, destruction, and deep copies. + + What we do here: + - add a dummy (empty) entry + - the container does a deep copy on something empty, + so that there is nothing to copy. + - get a reference to the entry added in the container + - complete -- in place -- the entry initialization + */ + m_array.push(empty); + User_variable & pfs_uvar= *m_array.back(); + + /* Copy VARIABLE_NAME */ + const char *name= sql_uvar->name.str; + size_t name_length= sql_uvar->name.length; + DBUG_ASSERT(name_length <= sizeof(pfs_uvar.m_name)); + pfs_uvar.m_name.make_row(name, name_length); + + /* Copy VARIABLE_VALUE */ + bool null_value; + String *str_value; + String str_buffer; + uint decimals= 0; + str_value= sql_uvar->val_str(& null_value, & str_buffer, decimals); + if (str_value != NULL) + { + pfs_uvar.m_value.make_row(str_value->ptr(), str_value->length()); + } + else + { + pfs_uvar.m_value.make_row(NULL, 0); + } + + index++; + } +} + +THR_LOCK table_uvar_by_thread::m_table_lock; + +PFS_engine_table_share_state +table_uvar_by_thread::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_uvar_by_thread::m_share= +{ + { C_STRING_WITH_LEN("user_variables_by_thread") }, + &pfs_readonly_acl, + table_uvar_by_thread::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_uvar_by_thread::get_row_count, + sizeof(pos_t), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE user_variables_by_thread(" + "THREAD_ID BIGINT unsigned not null comment 'The thread identifier of the session in which the variable is defined.'," + "VARIABLE_NAME VARCHAR(64) not null comment 'The variable name, without the leading @ character.'," + "VARIABLE_VALUE LONGBLOB comment 'The variable value')") }, + false, /* m_perpetual */ + false, /* m_optional */ + &m_share_state +}; + +PFS_engine_table* +table_uvar_by_thread::create(void) +{ + return new table_uvar_by_thread(); +} + +ha_rows +table_uvar_by_thread::get_row_count(void) +{ + /* + This is an estimate only, not a hard limit. + The row count is given as a multiple of thread_max, + so that a join between: + - table performance_schema.threads + - table performance_schema.user_variables_by_thread + will still evaluate relative table sizes correctly + when deciding a join order. + */ + return global_thread_container.get_row_count() * 10; +} + +table_uvar_by_thread::table_uvar_by_thread() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_uvar_by_thread::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_uvar_by_thread::rnd_next(void) +{ + PFS_thread *thread; + bool has_more_thread= true; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread); + if (thread != NULL) + { + if (materialize(thread) == 0) + { + const User_variable *uvar= m_THD_cache.get(m_pos.m_index_2); + if (uvar != NULL) + { + make_row(thread, uvar); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_uvar_by_thread::rnd_pos(const void *pos) +{ + PFS_thread *thread; + + set_position(pos); + + thread= global_thread_container.get(m_pos.m_index_1); + if (thread != NULL) + { + if (materialize(thread) == 0) + { + const User_variable *uvar= m_THD_cache.get(m_pos.m_index_2); + if (uvar != NULL) + { + make_row(thread, uvar); + return 0; + } + } + } + + return HA_ERR_RECORD_DELETED; +} + +int table_uvar_by_thread::materialize(PFS_thread *thread) +{ + if (m_THD_cache.is_materialized(thread)) + return 0; + + if (! thread->m_lock.is_populated()) + return 1; + + THD *unsafe_thd= thread->m_thd; + if (unsafe_thd == NULL) + return 1; + + Find_thd_user_var finder(unsafe_thd); + THD *safe_thd= Global_THD_manager::get_instance()->find_thd(&finder); + if (safe_thd == NULL) + return 1; + + m_THD_cache.materialize(thread, safe_thd); + mysql_mutex_unlock(&safe_thd->LOCK_thd_data); + return 0; +} + +void table_uvar_by_thread +::make_row(PFS_thread *thread, const User_variable *uvar) +{ + pfs_optimistic_state lock; + m_row_exists= false; + + /* Protect this reader against a thread termination */ + thread->m_lock.begin_optimistic_lock(&lock); + + m_row.m_thread_internal_id= thread->m_thread_internal_id; + + /* uvar is materialized, pointing to it directly. */ + m_row.m_variable_name= & uvar->m_name; + m_row.m_variable_value= & uvar->m_value; + + if (! thread->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; +} + +int table_uvar_by_thread +::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + assert(m_row.m_variable_name != NULL); + assert(m_row.m_variable_value != NULL); + + 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: /* VARIABLE_NAME */ + set_field_varchar_utf8(f, + m_row.m_variable_name->m_str, + m_row.m_variable_name->m_length); + break; + case 2: /* VARIABLE_VALUE */ + if (m_row.m_variable_value->get_value_length() > 0) + { + set_field_blob(f, + m_row.m_variable_value->get_value(), + static_cast<uint>(m_row.m_variable_value->get_value_length())); + } + else + { + f->set_null(); + } + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_uvar_by_thread.h b/storage/perfschema/table_uvar_by_thread.h new file mode 100644 index 00000000..61c52106 --- /dev/null +++ b/storage/perfschema/table_uvar_by_thread.h @@ -0,0 +1,196 @@ +/* Copyright (c) 2013, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef TABLE_UVAR_BY_THREAD_H +#define TABLE_UVAR_BY_THREAD_H + +/** + @file storage/perfschema/table_uvar_by_thread.h + Table USER_VARIABLES_BY_THREAD (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +struct User_variable +{ +public: + User_variable() + {} + + User_variable(const User_variable& uv) + : m_name(uv.m_name), m_value(uv.m_value) + {} + + ~User_variable() + {} + + PFS_variable_name_row m_name; + PFS_user_variable_value_row m_value; +}; + +class User_variables +{ + typedef Dynamic_array<User_variable> User_variable_array; + +public: + User_variables() + : m_pfs(NULL), m_thread_internal_id(0), m_array(PSI_INSTRUMENT_MEM) + { + } + + void reset() + { + m_pfs= NULL; + m_thread_internal_id= 0; + for (uint i=0; i < m_array.elements(); i++) + m_array.at(i).~User_variable(); + m_array.clear(); + } + + void materialize(PFS_thread *pfs, THD *thd); + + bool is_materialized(PFS_thread *pfs) + { + assert(pfs != NULL); + if (m_pfs != pfs) + return false; + if (m_thread_internal_id != pfs->m_thread_internal_id) + return false; + return true; + } + + const User_variable *get(uint index) const + { + if (index >= m_array.elements()) + return NULL; + + const User_variable *p= & m_array.at(index); + return p; + } + +private: + PFS_thread *m_pfs; + ulonglong m_thread_internal_id; + User_variable_array m_array; +}; + +/** + A row of table + PERFORMANCE_SCHEMA.USER_VARIABLES_BY_THREAD. +*/ +struct row_uvar_by_thread +{ + /** Column THREAD_ID. */ + ulonglong m_thread_internal_id; + /** Column VARIABLE_NAME. */ + const PFS_variable_name_row *m_variable_name; + /** Column VARIABLE_VALUE. */ + const PFS_user_variable_value_row *m_variable_value; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.USER_VARIABLES_BY_THREAD. + Index 1 on thread (0 based) + Index 2 on user variable (0 based) +*/ +struct pos_uvar_by_thread +: public PFS_double_index +{ + pos_uvar_by_thread() + : PFS_double_index(0, 0) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** Table PERFORMANCE_SCHEMA.USER_VARIABLES_BY_THREAD. */ +class table_uvar_by_thread : public PFS_engine_table +{ + typedef pos_uvar_by_thread pos_t; + +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_uvar_by_thread(); + +public: + ~table_uvar_by_thread() + { m_THD_cache.reset(); } + +protected: + int materialize(PFS_thread *thread); + void make_row(PFS_thread *thread, const User_variable *uvar); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current THD user variables. */ + User_variables m_THD_cache; + /** Current row. */ + row_uvar_by_thread m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_variables_by_thread.cc b/storage/perfschema/table_variables_by_thread.cc new file mode 100644 index 00000000..d673fa43 --- /dev/null +++ b/storage/perfschema/table_variables_by_thread.cc @@ -0,0 +1,235 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file storage/perfschema/table_variables_by_thread.cc + Table VARIABLES_BY_THREAD (implementation). +*/ + +#include "my_global.h" +#include "table_variables_by_thread.h" +#include "my_thread.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "pfs_global.h" + +THR_LOCK table_variables_by_thread::m_table_lock; + +PFS_engine_table_share_state +table_variables_by_thread::m_share_state = { + false /* m_checked */ +}; + +PFS_engine_table_share +table_variables_by_thread::m_share= +{ + { C_STRING_WITH_LEN("variables_by_thread") }, + &pfs_readonly_acl, + table_variables_by_thread::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + table_variables_by_thread::get_row_count, + sizeof(pos_t), + &m_table_lock, + { C_STRING_WITH_LEN("CREATE TABLE user_variables_by_thread(" + "THREAD_ID BIGINT unsigned not null," + "VARIABLE_NAME VARCHAR(64) not null," + "VARIABLE_VALUE VARCHAR(1024))") }, + true, /* m_perpetual */ + &m_share_state +}; + +PFS_engine_table* +table_variables_by_thread::create(void) +{ + return new table_variables_by_thread(); +} + +ha_rows table_variables_by_thread::get_row_count(void) +{ + mysql_mutex_lock(&LOCK_plugin_delete); + mysql_prlock_rdlock(&LOCK_system_variables_hash); + ulong system_var_count= get_system_variable_hash_records(); + mysql_prlock_unlock(&LOCK_system_variables_hash); + mysql_mutex_unlock(&LOCK_plugin_delete); + return (global_thread_container.get_row_count() * system_var_count); +} + +table_variables_by_thread::table_variables_by_thread() + : PFS_engine_table(&m_share, &m_pos), + m_sysvar_cache(true), m_row_exists(false), m_pos(), m_next_pos(), m_context(NULL) +{} + +void table_variables_by_thread::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_variables_by_thread::rnd_init(bool scan) +{ + /* + Build array of SHOW_VARs from system variable hash prior to materializing + threads in rnd_next() or rnd_pos(). + */ + m_sysvar_cache.initialize_session(); + + /* Record the version of the system variable hash. */ + ulonglong hash_version= m_sysvar_cache.get_sysvar_hash_version(); + + /* + The table context holds the current version of the system variable hash and + a record of which threads were materialized. + If scan == true, then allocate a new context from mem_root and store in TLS. + If scan == false, then restore from TLS. + */ + m_context= (table_variables_by_thread_context *)current_thd->alloc(sizeof(table_variables_by_thread_context)); + new(m_context) table_variables_by_thread_context(hash_version, !scan); + return 0; +} + +int table_variables_by_thread::rnd_next(void) +{ + /* If system variable hash changes, exit with warning. */ // TODO: Issue warning + if (!m_context->versions_match()) + return HA_ERR_END_OF_FILE; + + bool has_more_thread= true; + + for (m_pos.set_at(&m_next_pos); + has_more_thread; + m_pos.next_thread()) + { + PFS_thread *pfs_thread= global_thread_container.get(m_pos.m_index_1, &has_more_thread); + + /* Materialize all variables for the current thread. Assign a dedicated mem_root. */ + if (m_sysvar_cache.materialize_session(pfs_thread, true) == 0) + { + /* Mark this thread as materialized. */ + m_context->set_item(m_pos.m_index_1); + + const System_variable *system_var= m_sysvar_cache.get(m_pos.m_index_2); + if (system_var != NULL) + { + make_row(pfs_thread, system_var); + m_next_pos.set_after(&m_pos); + return 0; + } + } + } + return HA_ERR_END_OF_FILE; +} + +int +table_variables_by_thread::rnd_pos(const void *pos) +{ + /* If system variable hash changes, do nothing. */ + if (!m_context->versions_match()) + return HA_ERR_RECORD_DELETED; + + set_position(pos); + assert(m_pos.m_index_1 < global_thread_container.get_row_count()); + + PFS_thread *pfs_thread= global_thread_container.get(m_pos.m_index_1); + /* + Only materialize threads that were previously materialized by rnd_next(). + If a thread cannot be rematerialized, then do nothing. + Only materialize the requested system variable to avoid repeated + materialization of each thread, such as with ORDER BY variable_name. + */ + if (m_context->is_item_set(m_pos.m_index_1) && + /* Materialize only the requested variable. */ + m_sysvar_cache.materialize_session(pfs_thread, m_pos.m_index_2) == 0) + { + /* Get the first (and only) element from the cache. */ + const System_variable *system_var= m_sysvar_cache.get(); + if (system_var != NULL) + { + make_row(pfs_thread, system_var); + m_next_pos.set_after(&m_pos); + return 0; + } + } + return HA_ERR_RECORD_DELETED; +} + +void table_variables_by_thread +::make_row(PFS_thread *thread, const System_variable *system_var) +{ + pfs_optimistic_state lock; + m_row_exists= false; + if (system_var->is_null() || system_var->is_ignored()) + return; + + /* Protect this reader against a thread termination */ + thread->m_lock.begin_optimistic_lock(&lock); + + m_row.m_thread_internal_id= thread->m_thread_internal_id; + m_row.m_variable_name.make_row(system_var->m_name, system_var->m_name_length); + m_row.m_variable_value.make_row(system_var); + + if (!thread->m_lock.end_optimistic_lock(&lock)) + return; + + m_row_exists= true; +} + +int table_variables_by_thread +::read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all) +{ + Field *f; + + if (unlikely(! m_row_exists)) + return HA_ERR_RECORD_DELETED; + + /* Set the null bits */ + assert(table->s->null_bytes == 1); + buf[0]= 0; + + 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: /* VARIABLE_NAME */ + set_field_varchar_utf8(f, m_row.m_variable_name.m_str, m_row.m_variable_name.m_length); + break; + case 2: /* VARIABLE_VALUE */ + m_row.m_variable_value.set_field(f); + break; + default: + assert(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_variables_by_thread.h b/storage/perfschema/table_variables_by_thread.h new file mode 100644 index 00000000..99adcda4 --- /dev/null +++ b/storage/perfschema/table_variables_by_thread.h @@ -0,0 +1,152 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef TABLE_VARIABLES_BY_THREAD_H +#define TABLE_VARIABLES_BY_THREAD_H + +/** + @file storage/perfschema/table_variables_by_thread.h + Table VARIABLES_BY_THREAD (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "table_helper.h" +#include "pfs_variable.h" +#include "pfs_buffer_container.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.VARIABLES_BY_THREAD. +*/ +struct row_variables_by_thread +{ + /** Column THREAD_ID. */ + ulonglong m_thread_internal_id; + /** Column VARIABLE_NAME. */ + PFS_variable_name_row m_variable_name; + /** Column VARIABLE_VALUE. */ + PFS_variable_value_row m_variable_value; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.VARIABLES_BY_THREAD. + Index 1 on thread (0 based) + Index 2 on system variable (0 based) +*/ +struct pos_variables_by_thread +: public PFS_double_index +{ + pos_variables_by_thread() + : PFS_double_index(0, 0) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } + + inline bool has_more_thread(void) + { return (m_index_1 < global_thread_container.get_row_count()); } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** + Store and retrieve table state information during queries that reinstantiate + the table object. +*/ +class table_variables_by_thread_context : public PFS_table_context +{ +public: + table_variables_by_thread_context(ulonglong hash_version, bool restore) : + PFS_table_context(hash_version, global_thread_container.get_row_count(), restore, THR_PFS_VBT) { } +}; + +/** Table PERFORMANCE_SCHEMA.VARIABLES_BY_THREAD. */ +class table_variables_by_thread : public PFS_engine_table +{ + typedef pos_variables_by_thread pos_t; + +public: + static PFS_engine_table_share_state m_share_state; + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static ha_rows get_row_count(); + + virtual int rnd_init(bool scan); + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + table_variables_by_thread(); + +public: + ~table_variables_by_thread() + {} + +protected: + int materialize(PFS_thread *thread); + void make_row(PFS_thread *thread, const System_variable *system_var); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current THD variables. */ + PFS_system_variable_cache m_sysvar_cache; + /** Current row. */ + row_variables_by_thread m_row; + /** True if the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_t m_pos; + /** Next position. */ + pos_t m_next_pos; + + /** Table context with system variable hash version and map of materialized threads. */ + table_variables_by_thread_context *m_context; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/unittest/CMakeLists.txt b/storage/perfschema/unittest/CMakeLists.txt new file mode 100644 index 00000000..2a22990f --- /dev/null +++ b/storage/perfschema/unittest/CMakeLists.txt @@ -0,0 +1,40 @@ +# Copyright (c) 2009, 2023, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, +# as published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an additional +# permission to link the program and your derivative works with the +# separately licensed software that they have included with MySQL. +# +# 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/include/mysql + ${PCRE_INCLUDES} + ${CMAKE_SOURCE_DIR}/sql + ${SSL_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/unittest/mytap + ${CMAKE_SOURCE_DIR}/storage/perfschema) + +ADD_DEFINITIONS(-DMYSQL_SERVER ${SSL_DEFINES}) + +ADD_CONVENIENCE_LIBRARY(pfs_server_stubs pfs_server_stubs.cc) + +ADD_DEPENDENCIES(pfs_server_stubs GenError) + +MY_ADD_TESTS(pfs_instr_class pfs_instr_class-oom pfs_instr pfs_instr-oom + pfs_account-oom pfs_host-oom pfs_timer pfs_user-oom pfs_noop pfs + pfs_misc + EXT "cc" LINK_LIBRARIES perfschema mysys pfs_server_stubs) diff --git a/storage/perfschema/unittest/conf.txt b/storage/perfschema/unittest/conf.txt new file mode 100644 index 00000000..38d1e3d3 --- /dev/null +++ b/storage/perfschema/unittest/conf.txt @@ -0,0 +1,427 @@ +# Copyright (c) 2009, 2023, Oracle and/or its affiliates. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, +# as published by the Free Software Foundation. +# +# This program is also distributed with certain software (including +# but not limited to OpenSSL) that is licensed under separate terms, +# as designated in a particular file or component or in included license +# documentation. The authors of MySQL hereby grant you an additional +# permission to link the program and your derivative works with the +# separately licensed software that they have included with MySQL. +# +# 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA + +Performance schema test configurations. +(Used internally for performance testing) + +Configuration PERFSCHEMA-COMPILED-OUT +===================================== + +Description +----------- + +Reference for timings, server built without the performance schema. + +Compiling options +----------------- + +./configure --without-perfschema + +Server start options +-------------------- + +N/A + +Configuration +------------- + +N/A + +Pre-test queries +---------------- + +select version(); + +This is just to make sure the build is not including the performance schema. + +Post-test queries +----------------- + +N/A + +Configuration PERFSCHEMA-DISABLED +================================= + +Description +----------- + +Server built with the performance schema, +but the performance schema is disabled at startup. + +Compiling options +----------------- + +./configure --with-perfschema + +Server start options +-------------------- + +./mysqld --disable-performance-schema + +Configuration +------------- + +N/A + +Pre-test queries +---------------- + +select version(); +show engine performance_schema status; +show variables like "performance%"; +show status like "performance%"; +select * from performance_schema.PERFORMANCE_TIMERS; +select * from performance_schema.SETUP_CONSUMERS; +select * from performance_schema.SETUP_INSTRUMENTS; +select * from performance_schema.SETUP_TIMERS; + +Post-test queries +----------------- + +N/A + +Configuration PERFSCHEMA-ENABLED-STANDBY +======================================== + +Description +----------- + +Server built with the performance schema. +The performance schema is enabled at startup, +but configured to not record anything. +This is a "stanby" configuration, in the sense that the DBA can add +dynamically more setup options later to get data. + +Compiling options +----------------- + +./configure --with-perfschema + +Server start options +-------------------- + +./mysqld --enable-performance-schema + +Configuration +------------- + +UPDATE performance_schema.SETUP_INSTRUMENTS + set enabled='NO', timed='NO'; + +UPDATE performance_schema.SETUP_CONSUMERS + set enabled='NO'; + +Pre-test queries +---------------- + +select version(); +show engine performance_schema status; +show variables like "performance%"; +show status like "performance%"; +select * from performance_schema.PERFORMANCE_TIMERS; +select * from performance_schema.SETUP_CONSUMERS; +select * from performance_schema.SETUP_INSTRUMENTS; +select * from performance_schema.SETUP_TIMERS; + +Post-test queries +----------------- + +show engine performance_schema status; +show variables like "performance%"; +show status like "performance%"; + +Configuration PERFSCHEMA-ENABLED-CURRENT +======================================== + +Description +----------- + +Server built with the performance schema. +The performance schema is enabled at startup. +All instruments are enabled but not timed, +and only one consumer (EVENTS_WAITS_CURRENT) is set. + +Compiling options +----------------- + +./configure --with-perfschema + +Server start options +-------------------- + +./mysqld --enable-performance-schema + +Configuration +------------- + +UPDATE performance_schema.SETUP_INSTRUMENTS + set enabled='YES', timed='NO'; + +UPDATE performance_schema.SETUP_CONSUMERS + set enabled='NO'; + +UPDATE performance_schema.SETUP_CONSUMERS + set enabled='YES' where name='EVENTS_WAITS_CURRENT'; + +Pre-test queries +---------------- + +select version(); +show engine performance_schema status; +show variables like "performance%"; +show status like "performance%"; +select * from performance_schema.PERFORMANCE_TIMERS; +select * from performance_schema.SETUP_CONSUMERS; +select * from performance_schema.SETUP_INSTRUMENTS; +select * from performance_schema.SETUP_TIMERS; + +Post-test queries +----------------- + +show engine performance_schema status; +show variables like "performance%"; +show status like "performance%"; + +Configuration PERFSCHEMA-ENABLED-CURRENT-CYCLE +============================================== + +Description +----------- + +Server built with the performance schema. +The performance schema is enabled at startup. +All instruments are enabled and timed, +and only one consumer (EVENTS_WAITS_CURRENT) is set. +The timer used is CYCLE. + +Compiling options +----------------- + +./configure --with-perfschema + +Server start options +-------------------- + +./mysqld --enable-performance-schema + +Configuration +------------- + +UPDATE performance_schema.SETUP_INSTRUMENTS + set enabled='YES', timed='YES'; + +UPDATE performance_schema.SETUP_TIMERS + set timer_name='CYCLE'; + +UPDATE performance_schema.SETUP_CONSUMERS + set enabled='NO'; + +UPDATE performance_schema.SETUP_CONSUMERS + set enabled='YES' where name='EVENTS_WAITS_CURRENT'; + +Pre-test queries +---------------- + +select version(); +show engine performance_schema status; +show variables like "performance%"; +show status like "performance%"; +select * from performance_schema.PERFORMANCE_TIMERS; +select * from performance_schema.SETUP_CONSUMERS; +select * from performance_schema.SETUP_INSTRUMENTS; +select * from performance_schema.SETUP_TIMERS; + +Post-test queries +----------------- + +show engine performance_schema status; +show variables like "performance%"; +show status like "performance%"; + +Configuration PERFSCHEMA-ENABLED-HISTORY-CYCLE +============================================== + +Description +----------- + +Server built with the performance schema. +The performance schema is enabled at startup. +All instruments are enabled and timed, in CYCLE. +Two consumers (EVENTS_WAITS_CURRENT, EVENTS_WAITS_HISTORY) are set. + +Compiling options +----------------- + +./configure --with-perfschema + +Server start options +-------------------- + +./mysqld --enable-performance-schema + +Configuration +------------- + +UPDATE performance_schema.SETUP_INSTRUMENTS + set enabled='YES', timed='YES'; + +UPDATE performance_schema.SETUP_TIMERS + set timer_name='CYCLE'; + +UPDATE performance_schema.SETUP_CONSUMERS + set enabled='NO'; + +UPDATE performance_schema.SETUP_CONSUMERS + set enabled='YES' where name='EVENTS_WAITS_CURRENT'; + +UPDATE performance_schema.SETUP_CONSUMERS + set enabled='YES' where name='EVENTS_WAITS_HISTORY'; + +Pre-test queries +---------------- + +select version(); +show engine performance_schema status; +show variables like "performance%"; +show status like "performance%"; +select * from performance_schema.PERFORMANCE_TIMERS; +select * from performance_schema.SETUP_CONSUMERS; +select * from performance_schema.SETUP_INSTRUMENTS; +select * from performance_schema.SETUP_TIMERS; + +Post-test queries +----------------- + +show engine performance_schema status; +show variables like "performance%"; +show status like "performance%"; + +Configuration PERFSCHEMA-ENABLED-HISTORY_LONG-CYCLE +=================================================== + +Description +----------- + +Server built with the performance schema. +The performance schema is enabled at startup. +All instruments are enabled and timed, in CYCLE. +Two consumers (EVENTS_WAITS_CURRENT, EVENTS_WAITS_HISTORY_LONG) are set. + +Compiling options +----------------- + +./configure --with-perfschema + +Server start options +-------------------- + +./mysqld --enable-performance-schema + +Configuration +------------- + +UPDATE performance_schema.SETUP_INSTRUMENTS + set enabled='YES', timed='YES'; + +UPDATE performance_schema.SETUP_TIMERS + set timer_name='CYCLE'; + +UPDATE performance_schema.SETUP_CONSUMERS + set enabled='NO'; + +UPDATE performance_schema.SETUP_CONSUMERS + set enabled='YES' where name='EVENTS_WAITS_CURRENT'; + +UPDATE performance_schema.SETUP_CONSUMERS + set enabled='YES' where name='EVENTS_WAITS_HISTORY_LONG'; + +Pre-test queries +---------------- + +select version(); +show engine performance_schema status; +show variables like "performance%"; +show status like "performance%"; +select * from performance_schema.PERFORMANCE_TIMERS; +select * from performance_schema.SETUP_CONSUMERS; +select * from performance_schema.SETUP_INSTRUMENTS; +select * from performance_schema.SETUP_TIMERS; + +Post-test queries +----------------- + +show engine performance_schema status; +show variables like "performance%"; +show status like "performance%"; + +Configuration PERFSCHEMA-ENABLED-BIGBANG-CYCLE +============================================== + +Description +----------- + +Server built with the performance schema. +The performance schema is enabled at startup. +All instruments are enabled and timed, in CYCLE. +All possible consumers are enabled. + +Compiling options +----------------- + +./configure --with-perfschema + +Server start options +-------------------- + +./mysqld --enable-performance-schema + +Configuration +------------- + +UPDATE performance_schema.SETUP_INSTRUMENTS + set enabled='YES', timed='YES'; + +UPDATE performance_schema.SETUP_TIMERS + set timer_name='CYCLE'; + +UPDATE performance_schema.SETUP_CONSUMERS + set enabled='YES'; + +Pre-test queries +---------------- + +select version(); +show engine performance_schema status; +show variables like "performance%"; +show status like "performance%"; +select * from performance_schema.PERFORMANCE_TIMERS; +select * from performance_schema.SETUP_CONSUMERS; +select * from performance_schema.SETUP_INSTRUMENTS; +select * from performance_schema.SETUP_TIMERS; + +Post-test queries +----------------- + +show engine performance_schema status; +show variables like "performance%"; +show status like "performance%"; + diff --git a/storage/perfschema/unittest/pfs-t.cc b/storage/perfschema/unittest/pfs-t.cc new file mode 100644 index 00000000..981792f9 --- /dev/null +++ b/storage/perfschema/unittest/pfs-t.cc @@ -0,0 +1,1909 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_thread.h> +#include <pfs_server.h> +#include <pfs_instr_class.h> +#include <pfs_instr.h> +#include <pfs_global.h> +#include <pfs_buffer_container.h> +#include <tap.h> + +#include <string.h> +#include <memory.h> + +#include "stub_print_error.h" +#include "stub_pfs_defaults.h" +#include "stub_global_status_var.h" + +void unload_performance_schema(); + +/* test helpers, to simulate the setup */ + +void setup_thread(PSI_thread *t, bool enabled) +{ + PFS_thread *t2= (PFS_thread*) t; + t2->m_enabled= enabled; +} + +/* test helpers, to inspect data */ + +PFS_file* lookup_file_by_name(const char* name) +{ + PFS_file *pfs; + size_t len= strlen(name); + size_t dirlen; + const char *filename; + size_t filename_length; + + PFS_file_iterator it= global_file_container.iterate(); + pfs= it.scan_next(); + + while (pfs != NULL) + { + /* + When a file "foo" is instrumented, the name is normalized + to "/path/to/current/directory/foo", so we remove the + directory name here to find it back. + */ + dirlen= dirname_length(pfs->m_filename); + filename= pfs->m_filename + dirlen; + filename_length= pfs->m_filename_length - dirlen; + if ((len == filename_length) && + (strncmp(name, filename, filename_length) == 0)) + return pfs; + + pfs= it.scan_next(); + } + + return NULL; +} + +/* tests */ + +void test_bootstrap() +{ + void *psi; + void *psi_2; + PSI_bootstrap *boot; + PFS_global_param param; + + diag("test_bootstrap"); + + memset(& param, 0xFF, sizeof(param)); + param.m_enabled= true; + param.m_mutex_class_sizing= 0; + param.m_rwlock_class_sizing= 0; + param.m_cond_class_sizing= 0; + param.m_thread_class_sizing= 0; + param.m_table_share_sizing= 0; + param.m_file_class_sizing= 0; + param.m_socket_class_sizing= 0; + param.m_mutex_sizing= 0; + param.m_rwlock_sizing= 0; + param.m_cond_sizing= 0; + param.m_thread_sizing= 0; + param.m_table_sizing= 0; + param.m_file_sizing= 0; + param.m_file_handle_sizing= 0; + param.m_socket_sizing= 0; + param.m_events_waits_history_sizing= 0; + param.m_events_waits_history_long_sizing= 0; + param.m_setup_actor_sizing= 0; + param.m_setup_object_sizing= 0; + param.m_user_sizing= 0; + param.m_account_sizing= 0; + param.m_host_sizing= 0; + param.m_stage_class_sizing= 0; + param.m_events_stages_history_sizing= 0; + param.m_events_stages_history_long_sizing= 0; + param.m_statement_class_sizing= 0; + param.m_events_statements_history_sizing= 0; + param.m_events_statements_history_long_sizing= 0; + param.m_events_transactions_history_sizing= 0; + param.m_events_transactions_history_long_sizing= 0; + param.m_digest_sizing= 0; + param.m_session_connect_attrs_sizing= 0; + param.m_program_sizing= 0; + param.m_statement_stack_sizing= 0; + param.m_memory_class_sizing= 0; + param.m_metadata_lock_sizing= 0; + param.m_max_digest_length= 0; + param.m_max_sql_text_length= 0; + + param.m_hints.m_table_definition_cache = 100; + param.m_hints.m_table_open_cache = 100; + param.m_hints.m_max_connections = 100; + param.m_hints.m_open_files_limit = 100; + param.m_hints.m_max_prepared_stmt_count= 100; + + pre_initialize_performance_schema(); + boot= initialize_performance_schema(& param); + ok(boot != NULL, "boot"); + ok(boot->get_interface != NULL, "boot->get_interface"); + + psi= boot->get_interface(0); + ok(psi == NULL, "no version 0"); + + psi= boot->get_interface(PSI_VERSION_1); + ok(psi != NULL, "version 1"); + + psi_2= boot->get_interface(PSI_VERSION_2); + ok(psi_2 == NULL, "version 2"); + + unload_performance_schema(); +} + +/* + Not a test, helper for testing pfs.cc +*/ +PSI * load_perfschema() +{ + PSI *psi; + PSI_bootstrap *boot; + PFS_global_param param; + + memset(& param, 0xFF, sizeof(param)); + param.m_enabled= true; + param.m_mutex_class_sizing= 10; + param.m_rwlock_class_sizing= 10; + param.m_cond_class_sizing= 10; + param.m_thread_class_sizing= 10; + param.m_table_share_sizing= 10; + param.m_file_class_sizing= 10; + param.m_socket_class_sizing= 10; + param.m_mutex_sizing= 10; + param.m_rwlock_sizing= 10; + param.m_cond_sizing= 10; + param.m_thread_sizing= 10; + param.m_table_sizing= 10; + param.m_file_sizing= 10; + param.m_file_handle_sizing= 50; + param.m_socket_sizing= 10; + param.m_events_waits_history_sizing= 10; + param.m_events_waits_history_long_sizing= 10; + param.m_setup_actor_sizing= 0; + param.m_setup_object_sizing= 0; + param.m_user_sizing= 0; + param.m_account_sizing= 0; + param.m_host_sizing= 0; + param.m_stage_class_sizing= 0; + param.m_events_stages_history_sizing= 0; + param.m_events_stages_history_long_sizing= 0; + param.m_statement_class_sizing= 0; + param.m_events_statements_history_sizing= 0; + param.m_events_statements_history_long_sizing= 0; + param.m_events_transactions_history_sizing= 0; + param.m_events_transactions_history_long_sizing= 0; + param.m_digest_sizing= 0; + param.m_session_connect_attrs_sizing= 0; + param.m_program_sizing= 0; + param.m_statement_stack_sizing= 10; + param.m_memory_class_sizing= 10; + param.m_metadata_lock_sizing= 10; + param.m_max_digest_length= 0; + param.m_max_sql_text_length= 1000; + + param.m_hints.m_table_definition_cache = 100; + param.m_hints.m_table_open_cache = 100; + param.m_hints.m_max_connections = 100; + param.m_hints.m_open_files_limit = 100; + param.m_hints.m_max_prepared_stmt_count= 100; + + pre_initialize_performance_schema(); + /* test_bootstrap() covered this, assuming it just works */ + boot= initialize_performance_schema(& param); + psi= (PSI *)boot->get_interface(PSI_VERSION_1); + + /* Reset every consumer to a known state */ + flag_global_instrumentation= true; + flag_thread_instrumentation= true; + + return (PSI*) psi; +} + +void unload_performance_schema() +{ + cleanup_table_share(); + cleanup_instruments(); + cleanup_sync_class(); + cleanup_thread_class(); + cleanup_table_share(); + cleanup_file_class(); + cleanup_stage_class(); + cleanup_statement_class(); + cleanup_socket_class(); + cleanup_events_waits_history_long(); + cleanup_events_stages_history_long(); + cleanup_events_statements_history_long(); + cleanup_table_share_hash(); + cleanup_file_hash(); + cleanup_digest(); + + shutdown_performance_schema(); +} + +void test_bad_registration() +{ + PSI *psi; + + diag("test_bad_registration"); + + psi= load_perfschema(); + + /* + Test that length('wait/synch/mutex/' (17) + category + '/' (1)) < 32 + --> category can be up to 13 chars for a mutex. + */ + + PSI_mutex_key dummy_mutex_key= 9999; + PSI_mutex_info bad_mutex_1[]= + { + { & dummy_mutex_key, "X", 0} + }; + + psi->register_mutex("/", bad_mutex_1, 1); + ok(dummy_mutex_key == 0, "zero key"); + dummy_mutex_key= 9999; + psi->register_mutex("a/", bad_mutex_1, 1); + ok(dummy_mutex_key == 0, "zero key"); + dummy_mutex_key= 9999; + psi->register_mutex("/b", bad_mutex_1, 1); + ok(dummy_mutex_key == 0, "zero key"); + dummy_mutex_key= 9999; + psi->register_mutex("a/b", bad_mutex_1, 1); + ok(dummy_mutex_key == 0, "zero key"); + dummy_mutex_key= 9999; + psi->register_mutex("12345678901234", bad_mutex_1, 1); + ok(dummy_mutex_key == 0, "zero key"); + dummy_mutex_key= 9999; + psi->register_mutex("1234567890123", bad_mutex_1, 1); + ok(dummy_mutex_key == 1, "assigned key"); + + /* + Test that length('wait/synch/mutex/' (17) + category + '/' (1) + name) <= 128 + --> category + name can be up to 110 chars for a mutex. + */ + + dummy_mutex_key= 9999; + PSI_mutex_info bad_mutex_2[]= + { + { & dummy_mutex_key, + /* 110 chars name */ + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "1234567890", + 0} + }; + + psi->register_mutex("X", bad_mutex_2, 1); + ok(dummy_mutex_key == 0, "zero key"); + + dummy_mutex_key= 9999; + PSI_mutex_info bad_mutex_3[]= + { + { & dummy_mutex_key, + /* 109 chars name */ + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "123456789", + 0} + }; + + psi->register_mutex("XX", bad_mutex_3, 1); + ok(dummy_mutex_key == 0, "zero key"); + + psi->register_mutex("X", bad_mutex_3, 1); + ok(dummy_mutex_key == 2, "assigned key"); + + /* + Test that length('wait/synch/rwlock/' (18) + category + '/' (1)) < 32 + --> category can be up to 12 chars for a rwlock. + */ + + PSI_rwlock_key dummy_rwlock_key= 9999; + PSI_rwlock_info bad_rwlock_1[]= + { + { & dummy_rwlock_key, "X", 0} + }; + + psi->register_rwlock("/", bad_rwlock_1, 1); + ok(dummy_rwlock_key == 0, "zero key"); + dummy_rwlock_key= 9999; + psi->register_rwlock("a/", bad_rwlock_1, 1); + ok(dummy_rwlock_key == 0, "zero key"); + dummy_rwlock_key= 9999; + psi->register_rwlock("/b", bad_rwlock_1, 1); + ok(dummy_rwlock_key == 0, "zero key"); + dummy_rwlock_key= 9999; + psi->register_rwlock("a/b", bad_rwlock_1, 1); + ok(dummy_rwlock_key == 0, "zero key"); + dummy_rwlock_key= 9999; + psi->register_rwlock("1234567890123", bad_rwlock_1, 1); + ok(dummy_rwlock_key == 0, "zero key"); + dummy_rwlock_key= 9999; + psi->register_rwlock("123456789012", bad_rwlock_1, 1); + ok(dummy_rwlock_key == 1, "assigned key"); + + /* + Test that length('wait/synch/rwlock/' (18) + category + '/' (1) + name) <= 128 + --> category + name can be up to 109 chars for a rwlock. + */ + + dummy_rwlock_key= 9999; + PSI_rwlock_info bad_rwlock_2[]= + { + { & dummy_rwlock_key, + /* 109 chars name */ + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "123456789", + 0} + }; + + psi->register_rwlock("X", bad_rwlock_2, 1); + ok(dummy_rwlock_key == 0, "zero key"); + + dummy_rwlock_key= 9999; + PSI_rwlock_info bad_rwlock_2_sx[]= + { + { & dummy_rwlock_key, + /* 109 chars name */ + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "123456789", + PSI_RWLOCK_FLAG_SX} + }; + + psi->register_rwlock("Y", bad_rwlock_2_sx, 1); + ok(dummy_rwlock_key == 0, "zero key SX"); + + dummy_rwlock_key= 9999; + PSI_rwlock_info bad_rwlock_3[]= + { + { & dummy_rwlock_key, + /* 108 chars name */ + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678", + 0} + }; + + psi->register_rwlock("XX", bad_rwlock_3, 1); + ok(dummy_rwlock_key == 0, "zero key"); + + psi->register_rwlock("X", bad_rwlock_3, 1); + ok(dummy_rwlock_key == 2, "assigned key"); + + dummy_rwlock_key= 9999; + PSI_rwlock_info bad_rwlock_3_sx[]= + { + { & dummy_rwlock_key, + /* 108 chars name */ + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678", + PSI_RWLOCK_FLAG_SX} + }; + + psi->register_rwlock("YY", bad_rwlock_3_sx, 1); + ok(dummy_rwlock_key == 0, "zero key SX"); + + psi->register_rwlock("Y", bad_rwlock_3_sx, 1); + ok(dummy_rwlock_key == 3, "assigned key SX"); + + /* + Test that length('wait/synch/cond/' (16) + category + '/' (1)) < 32 + --> category can be up to 14 chars for a cond. + */ + + PSI_cond_key dummy_cond_key= 9999; + PSI_cond_info bad_cond_1[]= + { + { & dummy_cond_key, "X", 0} + }; + + psi->register_cond("/", bad_cond_1, 1); + ok(dummy_cond_key == 0, "zero key"); + dummy_cond_key= 9999; + psi->register_cond("a/", bad_cond_1, 1); + ok(dummy_cond_key == 0, "zero key"); + dummy_cond_key= 9999; + psi->register_cond("/b", bad_cond_1, 1); + ok(dummy_cond_key == 0, "zero key"); + dummy_cond_key= 9999; + psi->register_cond("a/b", bad_cond_1, 1); + ok(dummy_cond_key == 0, "zero key"); + dummy_cond_key= 9999; + psi->register_cond("123456789012345", bad_cond_1, 1); + ok(dummy_cond_key == 0, "zero key"); + dummy_cond_key= 9999; + psi->register_cond("12345678901234", bad_cond_1, 1); + ok(dummy_cond_key == 1, "assigned key"); + + /* + Test that length('wait/synch/cond/' (16) + category + '/' (1) + name) <= 128 + --> category + name can be up to 111 chars for a cond. + */ + + dummy_cond_key= 9999; + PSI_cond_info bad_cond_2[]= + { + { & dummy_cond_key, + /* 111 chars name */ + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678901", + 0} + }; + + psi->register_cond("X", bad_cond_2, 1); + ok(dummy_cond_key == 0, "zero key"); + + dummy_cond_key= 9999; + PSI_cond_info bad_cond_3[]= + { + { & dummy_cond_key, + /* 110 chars name */ + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "1234567890", + 0} + }; + + psi->register_cond("XX", bad_cond_3, 1); + ok(dummy_cond_key == 0, "zero key"); + + psi->register_cond("X", bad_cond_3, 1); + ok(dummy_cond_key == 2, "assigned key"); + + /* + Test that length('thread/' (7) + category + '/' (1)) < 32 + --> category can be up to 23 chars for a thread. + */ + + PSI_thread_key dummy_thread_key= 9999; + PSI_thread_info bad_thread_1[]= + { + { & dummy_thread_key, "X", 0} + }; + + psi->register_thread("/", bad_thread_1, 1); + ok(dummy_thread_key == 0, "zero key"); + dummy_thread_key= 9999; + psi->register_thread("a/", bad_thread_1, 1); + ok(dummy_thread_key == 0, "zero key"); + dummy_thread_key= 9999; + psi->register_thread("/b", bad_thread_1, 1); + ok(dummy_thread_key == 0, "zero key"); + dummy_thread_key= 9999; + psi->register_thread("a/b", bad_thread_1, 1); + ok(dummy_thread_key == 0, "zero key"); + dummy_thread_key= 9999; + psi->register_thread("123456789012345678901234", bad_thread_1, 1); + ok(dummy_thread_key == 0, "zero key"); + dummy_thread_key= 9999; + psi->register_thread("12345678901234567890123", bad_thread_1, 1); + ok(dummy_thread_key == 1, "assigned key"); + + /* + Test that length('thread/' (7) + category + '/' (1) + name) <= 128 + --> category + name can be up to 120 chars for a thread. + */ + + dummy_thread_key= 9999; + PSI_thread_info bad_thread_2[]= + { + { & dummy_thread_key, + /* 120 chars name */ + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678901234567890", + 0} + }; + + psi->register_thread("X", bad_thread_2, 1); + ok(dummy_thread_key == 0, "zero key"); + + dummy_thread_key= 9999; + PSI_thread_info bad_thread_3[]= + { + { & dummy_thread_key, + /* 119 chars name */ + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "1234567890123456789", + 0} + }; + + psi->register_thread("XX", bad_thread_3, 1); + ok(dummy_thread_key == 0, "zero key"); + + psi->register_thread("X", bad_thread_3, 1); + ok(dummy_thread_key == 2, "assigned key"); + + /* + Test that length('wait/io/file/' (13) + category + '/' (1)) < 32 + --> category can be up to 17 chars for a file. + */ + + PSI_file_key dummy_file_key= 9999; + PSI_file_info bad_file_1[]= + { + { & dummy_file_key, "X", 0} + }; + + psi->register_file("/", bad_file_1, 1); + ok(dummy_file_key == 0, "zero key"); + dummy_file_key= 9999; + psi->register_file("a/", bad_file_1, 1); + ok(dummy_file_key == 0, "zero key"); + dummy_file_key= 9999; + psi->register_file("/b", bad_file_1, 1); + ok(dummy_file_key == 0, "zero key"); + dummy_file_key= 9999; + psi->register_file("a/b", bad_file_1, 1); + ok(dummy_file_key == 0, "zero key"); + dummy_file_key= 9999; + psi->register_file("123456789012345678", bad_file_1, 1); + ok(dummy_file_key == 0, "zero key"); + dummy_file_key= 9999; + psi->register_file("12345678901234567", bad_file_1, 1); + ok(dummy_file_key == 1, "assigned key"); + + /* + Test that length('wait/io/file/' (13) + category + '/' (1) + name) <= 128 + --> category + name can be up to 114 chars for a file. + */ + + dummy_file_key= 9999; + PSI_file_info bad_file_2[]= + { + { & dummy_file_key, + /* 114 chars name */ + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678901234", + 0} + }; + + psi->register_file("X", bad_file_2, 1); + ok(dummy_file_key == 0, "zero key"); + + dummy_file_key= 9999; + PSI_file_info bad_file_3[]= + { + { & dummy_file_key, + /* 113 chars name */ + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "1234567890123", + 0} + }; + + psi->register_file("XX", bad_file_3, 1); + ok(dummy_file_key == 0, "zero key"); + + psi->register_file("X", bad_file_3, 1); + ok(dummy_file_key == 2, "assigned key"); + + /* + Test that length('wait/io/socket/' (15) + category + '/' (1)) < 32 + --> category can be up to 15 chars for a socket. + */ + + PSI_socket_key dummy_socket_key= 9999; + PSI_socket_info bad_socket_1[]= + { + { & dummy_socket_key, "X", 0} + }; + + psi->register_socket("/", bad_socket_1, 1); + ok(dummy_socket_key == 0, "zero key"); + dummy_socket_key= 9999; + psi->register_socket("a/", bad_socket_1, 1); + ok(dummy_socket_key == 0, "zero key"); + dummy_socket_key= 9999; + psi->register_socket("/b", bad_socket_1, 1); + ok(dummy_socket_key == 0, "zero key"); + dummy_socket_key= 9999; + psi->register_socket("a/b", bad_socket_1, 1); + ok(dummy_socket_key == 0, "zero key"); + dummy_socket_key= 9999; + psi->register_socket("1234567890123456", bad_socket_1, 1); + ok(dummy_socket_key == 0, "zero key"); + dummy_socket_key= 9999; + psi->register_socket("123456789012345", bad_socket_1, 1); + ok(dummy_socket_key == 1, "assigned key"); + + /* + Test that length('wait/io/socket/' (15) + category + '/' (1) + name) <= 128 + --> category + name can be up to 112 chars for a socket. + */ + + dummy_socket_key= 9999; + PSI_socket_info bad_socket_2[]= + { + { & dummy_socket_key, + /* 112 chars name */ + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "123456789012", + 0} + }; + + psi->register_socket("X", bad_socket_2, 1); + ok(dummy_socket_key == 0, "zero key"); + + dummy_socket_key= 9999; + PSI_socket_info bad_socket_3[]= + { + { & dummy_socket_key, + /* 111 chars name */ + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678901", + 0} + }; + + psi->register_socket("XX", bad_socket_3, 1); + ok(dummy_socket_key == 0, "zero key"); + + psi->register_socket("X", bad_socket_3, 1); + ok(dummy_socket_key == 2, "assigned key"); + + unload_performance_schema(); +} + +void test_init_disabled() +{ + PSI *psi; + + diag("test_init_disabled"); + + psi= load_perfschema(); + + PSI_mutex_key mutex_key_A; + PSI_mutex_info all_mutex[]= + { + { & mutex_key_A, "M-A", 0} + }; + + PSI_rwlock_key rwlock_key_A; + PSI_rwlock_info all_rwlock[]= + { + { & rwlock_key_A, "RW-A", 0} + }; + + PSI_cond_key cond_key_A; + PSI_cond_info all_cond[]= + { + { & cond_key_A, "C-A", 0} + }; + + PSI_file_key file_key_A; + PSI_file_info all_file[]= + { + { & file_key_A, "F-A", 0} + }; + + PSI_socket_key socket_key_A; + PSI_socket_info all_socket[]= + { + { & socket_key_A, "S-A", 0} + }; + + PSI_thread_key thread_key_1; + PSI_thread_info all_thread[]= + { + { & thread_key_1, "T-1", 0} + }; + + psi->register_mutex("test", all_mutex, 1); + psi->register_rwlock("test", all_rwlock, 1); + psi->register_cond("test", all_cond, 1); + psi->register_file("test", all_file, 1); + psi->register_socket("test", all_socket, 1); + psi->register_thread("test", all_thread, 1); + + PFS_mutex_class *mutex_class_A; + PFS_rwlock_class *rwlock_class_A; + PFS_cond_class *cond_class_A; + PFS_file_class *file_class_A; + PFS_socket_class *socket_class_A; + PSI_mutex *mutex_A1; + PSI_rwlock *rwlock_A1; + PSI_cond *cond_A1; + PFS_file *file_A1; + PSI_socket *socket_A1; + PSI_thread *thread_1; + + /* Preparation */ + + thread_1= psi->new_thread(thread_key_1, NULL, 0); + ok(thread_1 != NULL, "T-1"); + psi->set_thread_id(thread_1, 1); + + mutex_class_A= find_mutex_class(mutex_key_A); + ok(mutex_class_A != NULL, "mutex class A"); + + rwlock_class_A= find_rwlock_class(rwlock_key_A); + ok(rwlock_class_A != NULL, "rwlock class A"); + + cond_class_A= find_cond_class(cond_key_A); + ok(cond_class_A != NULL, "cond class A"); + + file_class_A= find_file_class(file_key_A); + ok(file_class_A != NULL, "file class A"); + + socket_class_A= find_socket_class(socket_key_A); + ok(socket_class_A != NULL, "socket class A"); + + /* + Pretend thread T-1 is running, and disabled, with thread_instrumentation. + Disabled instruments are still created so they can be enabled later. + */ + + /* ------------------------------------------------------------------------ */ + + psi->set_thread(thread_1); + setup_thread(thread_1, false); + + /* disabled M-A + disabled T-1: instrumentation */ + + mutex_class_A->m_enabled= false; + mutex_A1= psi->init_mutex(mutex_key_A, NULL); + ok(mutex_A1 != NULL, "mutex_A1 disabled, instrumented"); + + /* enabled M-A + disabled T-1: instrumentation (for later) */ + + mutex_class_A->m_enabled= true; + mutex_A1= psi->init_mutex(mutex_key_A, NULL); + ok(mutex_A1 != NULL, "mutex_A1 enabled, instrumented"); + + /* broken key + disabled T-1: no instrumentation */ + + mutex_class_A->m_enabled= true; + mutex_A1= psi->init_mutex(0, NULL); + ok(mutex_A1 == NULL, "mutex key 0 not instrumented"); + mutex_A1= psi->init_mutex(99, NULL); + ok(mutex_A1 == NULL, "broken mutex key not instrumented"); + + /* disabled RW-A + disabled T-1: no instrumentation */ + + rwlock_class_A->m_enabled= false; + rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL); + ok(rwlock_A1 != NULL, "rwlock_A1 disabled, instrumented"); + + /* enabled RW-A + disabled T-1: instrumentation (for later) */ + + rwlock_class_A->m_enabled= true; + rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL); + ok(rwlock_A1 != NULL, "rwlock_A1 enabled, instrumented"); + + /* broken key + disabled T-1: no instrumentation */ + + rwlock_class_A->m_enabled= true; + rwlock_A1= psi->init_rwlock(0, NULL); + ok(rwlock_A1 == NULL, "rwlock key 0 not instrumented"); + rwlock_A1= psi->init_rwlock(99, NULL); + ok(rwlock_A1 == NULL, "broken rwlock key not instrumented"); + + /* disabled C-A + disabled T-1: no instrumentation */ + + cond_class_A->m_enabled= false; + cond_A1= psi->init_cond(cond_key_A, NULL); + ok(cond_A1 != NULL, "cond_A1 disabled, instrumented"); + + /* enabled C-A + disabled T-1: instrumentation (for later) */ + + cond_class_A->m_enabled= true; + cond_A1= psi->init_cond(cond_key_A, NULL); + ok(cond_A1 != NULL, "cond_A1 enabled, instrumented"); + + /* broken key + disabled T-1: no instrumentation */ + + cond_class_A->m_enabled= true; + cond_A1= psi->init_cond(0, NULL); + ok(cond_A1 == NULL, "cond key 0 not instrumented"); + cond_A1= psi->init_cond(99, NULL); + ok(cond_A1 == NULL, "broken cond key not instrumented"); + + /* disabled F-A + disabled T-1: no instrumentation */ + + file_class_A->m_enabled= false; + psi->create_file(file_key_A, "foo", (File) 12); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "not instrumented"); + + /* enabled F-A + disabled T-1: no instrumentation */ + + file_class_A->m_enabled= true; + psi->create_file(file_key_A, "foo", (File) 12); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "not instrumented"); + + /* broken key + disabled T-1: no instrumentation */ + + file_class_A->m_enabled= true; + psi->create_file(0, "foo", (File) 12); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "file_A1 not instrumented"); + psi->create_file(99, "foo", (File) 12); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "file_A1 not instrumented"); + + /* disabled S-A + disabled T-1: no instrumentation */ + + socket_class_A->m_enabled= false; + socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0); + ok(socket_A1 != NULL, "socket_A1 disabled, instrumented"); + + /* enabled S-A + disabled T-1: instrumentation (for later) */ + + socket_class_A->m_enabled= true; + socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0); + ok(socket_A1 != NULL, "socket_A1 enabled, instrumented"); + + /* broken key + disabled T-1: no instrumentation */ + + socket_class_A->m_enabled= true; + socket_A1= psi->init_socket(0, NULL, NULL, 0); + ok(socket_A1 == NULL, "socket key 0 not instrumented"); + socket_A1= psi->init_socket(99, NULL, NULL, 0); + ok(socket_A1 == NULL, "broken socket key not instrumented"); + + /* Pretend thread T-1 is enabled */ + /* ----------------------------- */ + + setup_thread(thread_1, true); + + /* disabled M-A + enabled T-1: no instrumentation */ + + mutex_class_A->m_enabled= false; + mutex_A1= psi->init_mutex(mutex_key_A, NULL); + ok(mutex_A1 != NULL, "mutex_A1 disabled, instrumented"); + + /* enabled M-A + enabled T-1: instrumentation */ + + mutex_class_A->m_enabled= true; + mutex_A1= psi->init_mutex(mutex_key_A, NULL); + ok(mutex_A1 != NULL, "mutex_A1 enabled, instrumented"); + psi->destroy_mutex(mutex_A1); + + /* broken key + enabled T-1: no instrumentation */ + + mutex_class_A->m_enabled= true; + mutex_A1= psi->init_mutex(0, NULL); + ok(mutex_A1 == NULL, "mutex_A1 not instrumented"); + mutex_A1= psi->init_mutex(99, NULL); + ok(mutex_A1 == NULL, "mutex_A1 not instrumented"); + + /* disabled RW-A + enabled T-1: no instrumentation */ + + rwlock_class_A->m_enabled= false; + rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL); + ok(rwlock_A1 != NULL, "rwlock_A1 disabled, instrumented"); + + /* enabled RW-A + enabled T-1: instrumentation */ + + rwlock_class_A->m_enabled= true; + rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL); + ok(rwlock_A1 != NULL, "rwlock_A1 enabled, instrumented"); + psi->destroy_rwlock(rwlock_A1); + + /* broken key + enabled T-1: no instrumentation */ + + rwlock_class_A->m_enabled= true; + rwlock_A1= psi->init_rwlock(0, NULL); + ok(rwlock_A1 == NULL, "rwlock_A1 not instrumented"); + rwlock_A1= psi->init_rwlock(99, NULL); + ok(rwlock_A1 == NULL, "rwlock_A1 not instrumented"); + + /* disabled C-A + enabled T-1: no instrumentation */ + + cond_class_A->m_enabled= false; + cond_A1= psi->init_cond(cond_key_A, NULL); + ok(cond_A1 != NULL, "cond_A1 disabled, instrumented"); + + /* enabled C-A + enabled T-1: instrumentation */ + + cond_class_A->m_enabled= true; + cond_A1= psi->init_cond(cond_key_A, NULL); + ok(cond_A1 != NULL, "cond_A1 enabled, instrumented"); + psi->destroy_cond(cond_A1); + + /* broken key + enabled T-1: no instrumentation */ + + cond_class_A->m_enabled= true; + cond_A1= psi->init_cond(0, NULL); + ok(cond_A1 == NULL, "cond_A1 not instrumented"); + cond_A1= psi->init_cond(99, NULL); + ok(cond_A1 == NULL, "cond_A1 not instrumented"); + + /* disabled F-A + enabled T-1: no instrumentation */ + + file_class_A->m_enabled= false; + psi->create_file(file_key_A, "foo", (File) 12); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "file_A1 not instrumented"); + + /* enabled F-A + open failed + enabled T-1: no instrumentation */ + + file_class_A->m_enabled= true; + psi->create_file(file_key_A, "foo", (File) -1); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "file_A1 not instrumented"); + + /* enabled F-A + out-of-descriptors + enabled T-1: no instrumentation */ + + file_class_A->m_enabled= true; + psi->create_file(file_key_A, "foo", (File) 65000); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "file_A1 not instrumented"); + ok(file_handle_lost == 1, "lost a file handle"); + file_handle_lost= 0; + + /* enabled F-A + enabled T-1: instrumentation */ + + file_class_A->m_enabled= true; + psi->create_file(file_key_A, "foo-instrumented", (File) 12); + file_A1= lookup_file_by_name("foo-instrumented"); + ok(file_A1 != NULL, "file_A1 instrumented"); + destroy_file(reinterpret_cast<PFS_thread*>(psi->get_thread()), file_A1); + + /* broken key + enabled T-1: no instrumentation */ + + file_class_A->m_enabled= true; + psi->create_file(0, "foo", (File) 12); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "file key 0 not instrumented"); + psi->create_file(99, "foo", (File) 12); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "broken file key not instrumented"); + + /* disabled S-A + enabled T-1: no instrumentation */ + + socket_class_A->m_enabled= false; + ok(socket_A1 == NULL, "socket_A1 not instrumented"); + + /* enabled S-A + enabled T-1: instrumentation */ + + socket_class_A->m_enabled= true; + socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0); + ok(socket_A1 != NULL, "socket_A1 instrumented"); + psi->destroy_socket(socket_A1); + + /* broken key + enabled T-1: no instrumentation */ + + socket_class_A->m_enabled= true; + socket_A1= psi->init_socket(0, NULL, NULL, 0); + ok(socket_A1 == NULL, "socket_A1 not instrumented"); + socket_A1= psi->init_socket(99, NULL, NULL, 0); + ok(socket_A1 == NULL, "socket_A1 not instrumented"); + + /* Pretend the running thread is not instrumented */ + /* ---------------------------------------------- */ + + psi->delete_current_thread(); + + /* disabled M-A + unknown thread: no instrumentation */ + + mutex_class_A->m_enabled= false; + mutex_A1= psi->init_mutex(mutex_key_A, NULL); + ok(mutex_A1 != NULL, "mutex_A1 disabled, instrumented"); + + /* enabled M-A + unknown thread: instrumentation (for later) */ + + mutex_class_A->m_enabled= true; + mutex_A1= psi->init_mutex(mutex_key_A, NULL); + ok(mutex_A1 != NULL, "mutex_A1 enabled, instrumented"); + + /* broken key + unknown thread: no instrumentation */ + + mutex_class_A->m_enabled= true; + mutex_A1= psi->init_mutex(0, NULL); + ok(mutex_A1 == NULL, "mutex key 0 not instrumented"); + mutex_A1= psi->init_mutex(99, NULL); + ok(mutex_A1 == NULL, "broken mutex key not instrumented"); + + /* disabled RW-A + unknown thread: no instrumentation */ + + rwlock_class_A->m_enabled= false; + rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL); + ok(rwlock_A1 != NULL, "rwlock_A1 disabled, instrumented"); + + /* enabled RW-A + unknown thread: instrumentation (for later) */ + + rwlock_class_A->m_enabled= true; + rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL); + ok(rwlock_A1 != NULL, "rwlock_A1 enabled, instrumented"); + + /* broken key + unknown thread: no instrumentation */ + + rwlock_class_A->m_enabled= true; + rwlock_A1= psi->init_rwlock(0, NULL); + ok(rwlock_A1 == NULL, "rwlock key 0 not instrumented"); + rwlock_A1= psi->init_rwlock(99, NULL); + ok(rwlock_A1 == NULL, "broken rwlock key not instrumented"); + + /* disabled C-A + unknown thread: no instrumentation */ + + cond_class_A->m_enabled= false; + cond_A1= psi->init_cond(cond_key_A, NULL); + ok(cond_A1 != NULL, "cond_A1 disabled, instrumented"); + + /* enabled C-A + unknown thread: instrumentation (for later) */ + + cond_class_A->m_enabled= true; + cond_A1= psi->init_cond(cond_key_A, NULL); + ok(cond_A1 != NULL, "cond_A1 enabled, instrumented"); + + /* broken key + unknown thread: no instrumentation */ + + cond_class_A->m_enabled= true; + cond_A1= psi->init_cond(0, NULL); + ok(cond_A1 == NULL, "cond key 0 not instrumented"); + cond_A1= psi->init_cond(99, NULL); + ok(cond_A1 == NULL, "broken cond key not instrumented"); + + /* disabled F-A + unknown thread: no instrumentation */ + + file_class_A->m_enabled= false; + psi->create_file(file_key_A, "foo", (File) 12); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "file_A1 not instrumented"); + + /* enabled F-A + unknown thread: no instrumentation */ + + file_class_A->m_enabled= true; + psi->create_file(file_key_A, "foo", (File) 12); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "file_A1 not instrumented"); + + /* broken key + unknown thread: no instrumentation */ + + file_class_A->m_enabled= true; + psi->create_file(0, "foo", (File) 12); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "not instrumented"); + psi->create_file(99, "foo", (File) 12); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "not instrumented"); + + /* disabled S-A + unknown thread: no instrumentation */ + + socket_class_A->m_enabled= false; + socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0); + ok(socket_A1 != NULL, "socket_A1 disabled, instrumented"); + + /* enabled S-A + unknown thread: instrumentation (for later) */ + + socket_class_A->m_enabled= true; + socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0); + ok(socket_A1 != NULL, "socket_A1 enabled, instrumented"); + + /* broken key + unknown thread: no instrumentation */ + + socket_class_A->m_enabled= true; + socket_A1= psi->init_socket(0, NULL, NULL, 0); + ok(socket_A1 == NULL, "socket key 0 not instrumented"); + socket_A1= psi->init_socket(99, NULL, NULL, 0); + ok(socket_A1 == NULL, "broken socket key not instrumented"); + + unload_performance_schema(); +} + +void test_locker_disabled() +{ + PSI *psi; + + diag("test_locker_disabled"); + + psi= load_perfschema(); + + PSI_mutex_key mutex_key_A; + PSI_mutex_info all_mutex[]= + { + { & mutex_key_A, "M-A", 0} + }; + + PSI_rwlock_key rwlock_key_A; + PSI_rwlock_info all_rwlock[]= + { + { & rwlock_key_A, "RW-A", 0} + }; + + PSI_cond_key cond_key_A; + PSI_cond_info all_cond[]= + { + { & cond_key_A, "C-A", 0} + }; + + PSI_file_key file_key_A; + PSI_file_info all_file[]= + { + { & file_key_A, "F-A", 0} + }; + + PSI_socket_key socket_key_A; + PSI_socket_info all_socket[]= + { + { & socket_key_A, "S-A", 0} + }; + + PSI_thread_key thread_key_1; + PSI_thread_info all_thread[]= + { + { & thread_key_1, "T-1", 0} + }; + + psi->register_mutex("test", all_mutex, 1); + psi->register_rwlock("test", all_rwlock, 1); + psi->register_cond("test", all_cond, 1); + psi->register_file("test", all_file, 1); + psi->register_socket("test", all_socket, 1); + psi->register_thread("test", all_thread, 1); + + PFS_mutex_class *mutex_class_A; + PFS_rwlock_class *rwlock_class_A; + PFS_cond_class *cond_class_A; + PFS_file_class *file_class_A; + PFS_socket_class *socket_class_A; + PSI_mutex *mutex_A1; + PSI_rwlock *rwlock_A1; + PSI_cond *cond_A1; + PSI_file *file_A1; + PSI_socket *socket_A1; + PSI_thread *thread_1; + + /* Preparation */ + + thread_1= psi->new_thread(thread_key_1, NULL, 0); + ok(thread_1 != NULL, "T-1"); + psi->set_thread_id(thread_1, 1); + + mutex_class_A= find_mutex_class(mutex_key_A); + ok(mutex_class_A != NULL, "mutex info A"); + + rwlock_class_A= find_rwlock_class(rwlock_key_A); + ok(rwlock_class_A != NULL, "rwlock info A"); + + cond_class_A= find_cond_class(cond_key_A); + ok(cond_class_A != NULL, "cond info A"); + + file_class_A= find_file_class(file_key_A); + ok(file_class_A != NULL, "file info A"); + + socket_class_A= find_socket_class(socket_key_A); + ok(socket_class_A != NULL, "socket info A"); + + /* Pretend thread T-1 is running, and enabled */ + /* ------------------------------------------ */ + + psi->set_thread(thread_1); + setup_thread(thread_1, true); + + /* Enable all instruments, instantiate objects */ + + mutex_class_A->m_enabled= true; + mutex_A1= psi->init_mutex(mutex_key_A, NULL); + ok(mutex_A1 != NULL, "instrumented"); + + rwlock_class_A->m_enabled= true; + rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL); + ok(rwlock_A1 != NULL, "instrumented"); + + cond_class_A->m_enabled= true; + cond_A1= psi->init_cond(cond_key_A, NULL); + ok(cond_A1 != NULL, "instrumented"); + + file_class_A->m_enabled= true; + psi->create_file(file_key_A, "foo", (File) 12); + file_A1= (PSI_file*) lookup_file_by_name("foo"); + ok(file_A1 != NULL, "instrumented"); + destroy_file(reinterpret_cast<PFS_thread*>(psi->get_thread()), + reinterpret_cast<PFS_file*>(file_A1)); + + socket_class_A->m_enabled= true; + socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0); + ok(socket_A1 != NULL, "instrumented"); + + /* Socket lockers require a thread owner */ + psi->set_socket_thread_owner(socket_A1); + + PSI_mutex_locker *mutex_locker; + PSI_mutex_locker_state mutex_state; + PSI_rwlock_locker *rwlock_locker; + PSI_rwlock_locker_state rwlock_state; + PSI_cond_locker *cond_locker; + PSI_cond_locker_state cond_state; + PSI_file_locker *file_locker; + PSI_file_locker_state file_state; + PSI_socket_locker *socket_locker; + PSI_socket_locker_state socket_state; + + /* Pretend thread T-1 is disabled */ + /* ------------------------------ */ + + setup_thread(thread_1, false); + flag_events_waits_current= true; + mutex_class_A->m_enabled= true; + rwlock_class_A->m_enabled= true; + cond_class_A->m_enabled= true; + file_class_A->m_enabled= true; + socket_class_A->m_enabled= true; + + mutex_locker= psi->start_mutex_wait(&mutex_state, mutex_A1, PSI_MUTEX_LOCK, "foo.cc", 12); + ok(mutex_locker == NULL, "no locker (T-1 disabled)"); + rwlock_locker= psi->start_rwlock_rdwait(&rwlock_state, rwlock_A1, PSI_RWLOCK_READLOCK, "foo.cc", 12); + ok(rwlock_locker == NULL, "no locker (T-1 disabled)"); + cond_locker= psi->start_cond_wait(&cond_state, cond_A1, mutex_A1, PSI_COND_WAIT, "foo.cc", 12); + ok(cond_locker == NULL, "no locker (T-1 disabled)"); + file_locker= psi->get_thread_file_name_locker(&file_state, file_key_A, PSI_FILE_OPEN, "xxx", NULL); + ok(file_locker == NULL, "no locker (T-1 disabled)"); + file_locker= psi->get_thread_file_stream_locker(&file_state, file_A1, PSI_FILE_READ); + ok(file_locker == NULL, "no locker (T-1 disabled)"); + file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_READ); + ok(file_locker == NULL, "no locker (T-1 disabled)"); + socket_locker= psi->start_socket_wait(&socket_state, socket_A1, PSI_SOCKET_SEND, 12, "foo.cc", 12); + ok(socket_locker == NULL, "no locker (T-1 disabled)"); + + /* Pretend the global consumer is disabled */ + /* --------------------------------------- */ + + setup_thread(thread_1, true); + flag_global_instrumentation= false; + mutex_class_A->m_enabled= true; + rwlock_class_A->m_enabled= true; + cond_class_A->m_enabled= true; + file_class_A->m_enabled= true; + socket_class_A->m_enabled= true; + update_instruments_derived_flags(); + + mutex_locker= psi->start_mutex_wait(&mutex_state, mutex_A1, PSI_MUTEX_LOCK, "foo.cc", 12); + ok(mutex_locker == NULL, "no locker (global disabled)"); + rwlock_locker= psi->start_rwlock_rdwait(&rwlock_state, rwlock_A1, PSI_RWLOCK_READLOCK, "foo.cc", 12); + ok(rwlock_locker == NULL, "no locker (global disabled)"); + cond_locker= psi->start_cond_wait(&cond_state, cond_A1, mutex_A1, PSI_COND_WAIT, "foo.cc", 12); + ok(cond_locker == NULL, "no locker (global disabled)"); + file_locker= psi->get_thread_file_name_locker(&file_state, file_key_A, PSI_FILE_OPEN, "xxx", NULL); + ok(file_locker == NULL, "no locker (global disabled)"); + file_locker= psi->get_thread_file_stream_locker(&file_state, file_A1, PSI_FILE_READ); + ok(file_locker == NULL, "no locker (global disabled)"); + file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_READ); + ok(file_locker == NULL, "no locker (global disabled)"); + socket_locker= psi->start_socket_wait(&socket_state, socket_A1, PSI_SOCKET_SEND, 12, "foo.cc", 12); + ok(socket_locker == NULL, "no locker (global disabled)"); + + /* Pretend the mode is global, counted only */ + /* ---------------------------------------- */ + + setup_thread(thread_1, true); + flag_global_instrumentation= true; + flag_thread_instrumentation= false; + mutex_class_A->m_enabled= true; + mutex_class_A->m_timed= false; + rwlock_class_A->m_enabled= true; + rwlock_class_A->m_timed= false; + cond_class_A->m_enabled= true; + cond_class_A->m_timed= false; + file_class_A->m_enabled= true; + file_class_A->m_timed= false; + socket_class_A->m_enabled= true; + socket_class_A->m_timed= false; + update_instruments_derived_flags(); + + mutex_locker= psi->start_mutex_wait(&mutex_state, mutex_A1, PSI_MUTEX_LOCK, "foo.cc", 12); + ok(mutex_locker == NULL, "no locker (global counted)"); + rwlock_locker= psi->start_rwlock_rdwait(&rwlock_state, rwlock_A1, PSI_RWLOCK_READLOCK, "foo.cc", 12); + ok(rwlock_locker == NULL, "no locker (global counted)"); + cond_locker= psi->start_cond_wait(&cond_state, cond_A1, mutex_A1, PSI_COND_WAIT, "foo.cc", 12); + ok(cond_locker == NULL, "no locker (global counted)"); + file_locker= psi->get_thread_file_name_locker(&file_state, file_key_A, PSI_FILE_OPEN, "xxx", NULL); + ok(file_locker != NULL, "locker (global counted)"); + psi->start_file_wait(file_locker, 10, __FILE__, __LINE__); + psi->end_file_wait(file_locker, 10); + file_locker= psi->get_thread_file_stream_locker(&file_state, file_A1, PSI_FILE_READ); + ok(file_locker != NULL, "locker (global counted)"); + psi->start_file_wait(file_locker, 10, __FILE__, __LINE__); + psi->end_file_wait(file_locker, 10); + file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_READ); + ok(file_locker != NULL, "locker (global counted)"); + psi->start_file_wait(file_locker, 10, __FILE__, __LINE__); + psi->end_file_wait(file_locker, 10); + /* The null locker shortcut applies only to socket ops with no byte count */ + socket_locker= psi->start_socket_wait(&socket_state, socket_A1, PSI_SOCKET_BIND, 0, "foo.cc", 12); + ok(socket_locker == NULL, "no locker (global counted)"); + + /* TODO */ + + /* Pretend the instrument is disabled */ + /* ---------------------------------- */ + + setup_thread(thread_1, true); + flag_global_instrumentation= true; + flag_events_waits_current= true; + mutex_class_A->m_enabled= false; + rwlock_class_A->m_enabled= false; + cond_class_A->m_enabled= false; + file_class_A->m_enabled= false; + socket_class_A->m_enabled= false; + update_instruments_derived_flags(); + + mutex_locker= psi->start_mutex_wait(&mutex_state, mutex_A1, PSI_MUTEX_LOCK, "foo.cc", 12); + ok(mutex_locker == NULL, "no locker"); + rwlock_locker= psi->start_rwlock_rdwait(&rwlock_state, rwlock_A1, PSI_RWLOCK_READLOCK, "foo.cc", 12); + ok(rwlock_locker == NULL, "no locker"); + cond_locker= psi->start_cond_wait(&cond_state, cond_A1, mutex_A1, PSI_COND_WAIT, "foo.cc", 12); + ok(cond_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_name_locker(&file_state, file_key_A, PSI_FILE_OPEN, "xxx", NULL); + ok(file_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_stream_locker(&file_state, file_A1, PSI_FILE_READ); + ok(file_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_READ); + ok(file_locker == NULL, "no locker"); + socket_locker= psi->start_socket_wait(&socket_state, socket_A1, PSI_SOCKET_SEND, 12, "foo.cc", 12); + ok(socket_locker == NULL, "no locker"); + + /* Pretend everything is enabled and timed */ + /* --------------------------------------- */ + + setup_thread(thread_1, true); + flag_global_instrumentation= true; + flag_thread_instrumentation= true; + flag_events_waits_current= true; + mutex_class_A->m_enabled= true; + mutex_class_A->m_timed= true; + rwlock_class_A->m_enabled= true; + rwlock_class_A->m_timed= true; + cond_class_A->m_enabled= true; + cond_class_A->m_timed= true; + file_class_A->m_enabled= true; + file_class_A->m_timed= true; + socket_class_A->m_enabled= true; + socket_class_A->m_timed= true; + update_instruments_derived_flags(); + + mutex_locker= psi->start_mutex_wait(&mutex_state, mutex_A1, PSI_MUTEX_LOCK, __FILE__, __LINE__); + ok(mutex_locker != NULL, "locker"); + psi->end_mutex_wait(mutex_locker, 0); + rwlock_locker= psi->start_rwlock_rdwait(&rwlock_state, rwlock_A1, PSI_RWLOCK_READLOCK, __FILE__, __LINE__); + ok(rwlock_locker != NULL, "locker"); + psi->end_rwlock_rdwait(rwlock_locker, 0); + cond_locker= psi->start_cond_wait(&cond_state, cond_A1, mutex_A1, PSI_COND_WAIT, __FILE__, __LINE__); + ok(cond_locker != NULL, "locker"); + psi->end_cond_wait(cond_locker, 0); + file_locker= psi->get_thread_file_name_locker(&file_state, file_key_A, PSI_FILE_STREAM_OPEN, "xxx", NULL); + ok(file_locker != NULL, "locker"); + psi->start_file_open_wait(file_locker, __FILE__, __LINE__); + psi->end_file_open_wait(file_locker, NULL); + file_locker= psi->get_thread_file_stream_locker(&file_state, file_A1, PSI_FILE_READ); + ok(file_locker != NULL, "locker"); + psi->start_file_wait(file_locker, 10, __FILE__, __LINE__); + psi->end_file_wait(file_locker, 10); + file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_READ); + ok(file_locker != NULL, "locker"); + psi->start_file_wait(file_locker, 10, __FILE__, __LINE__); + psi->end_file_wait(file_locker, 10); + socket_locker= psi->start_socket_wait(&socket_state, socket_A1, PSI_SOCKET_SEND, 12, "foo.cc", 12); + ok(socket_locker != NULL, "locker"); + psi->end_socket_wait(socket_locker, 10); + + /* Pretend the socket does not have a thread owner */ + /* ---------------------------------------------- */ + + socket_class_A->m_enabled= true; + socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0); + ok(socket_A1 != NULL, "instrumented"); + /* Socket thread owner has not been set */ + socket_locker= psi->start_socket_wait(&socket_state, socket_A1, PSI_SOCKET_SEND, 12, "foo.cc", 12); + ok(socket_locker != NULL, "locker (owner not used)"); + psi->end_socket_wait(socket_locker, 10); + + /* Pretend the running thread is not instrumented */ + /* ---------------------------------------------- */ + + psi->delete_current_thread(); + flag_events_waits_current= true; + mutex_class_A->m_enabled= true; + rwlock_class_A->m_enabled= true; + cond_class_A->m_enabled= true; + file_class_A->m_enabled= true; + socket_class_A->m_enabled= true; + update_instruments_derived_flags(); + + mutex_locker= psi->start_mutex_wait(&mutex_state, mutex_A1, PSI_MUTEX_LOCK, "foo.cc", 12); + ok(mutex_locker == NULL, "no locker"); + rwlock_locker= psi->start_rwlock_rdwait(&rwlock_state, rwlock_A1, PSI_RWLOCK_READLOCK, "foo.cc", 12); + ok(rwlock_locker == NULL, "no locker"); + cond_locker= psi->start_cond_wait(&cond_state, cond_A1, mutex_A1, PSI_COND_WAIT, "foo.cc", 12); + ok(cond_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_name_locker(&file_state, file_key_A, PSI_FILE_OPEN, "xxx", NULL); + ok(file_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_stream_locker(&file_state, file_A1, PSI_FILE_READ); + ok(file_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_READ); + ok(file_locker == NULL, "no locker"); + socket_locker= psi->start_socket_wait(&socket_state, socket_A1, PSI_SOCKET_SEND, 12, "foo.cc", 12); + ok(socket_locker == NULL, "no locker"); + + unload_performance_schema(); +} + +void test_file_instrumentation_leak() +{ + PSI *psi; + + diag("test_file_instrumentation_leak"); + + psi= load_perfschema(); + + PSI_file_key file_key_A; + PSI_file_key file_key_B; + PSI_file_info all_file[]= + { + { & file_key_A, "F-A", 0}, + { & file_key_B, "F-B", 0} + }; + + PSI_thread_key thread_key_1; + PSI_thread_info all_thread[]= + { + { & thread_key_1, "T-1", 0} + }; + + psi->register_file("test", all_file, 2); + psi->register_thread("test", all_thread, 1); + + PFS_file_class *file_class_A; + PFS_file_class *file_class_B; + PSI_file_locker_state file_state; + PSI_thread *thread_1; + + /* Preparation */ + + thread_1= psi->new_thread(thread_key_1, NULL, 0); + ok(thread_1 != NULL, "T-1"); + psi->set_thread_id(thread_1, 1); + + file_class_A= find_file_class(file_key_A); + ok(file_class_A != NULL, "file info A"); + + file_class_B= find_file_class(file_key_B); + ok(file_class_B != NULL, "file info B"); + + psi->set_thread(thread_1); + + /* Pretend everything is enabled */ + /* ----------------------------- */ + + setup_thread(thread_1, true); + flag_events_waits_current= true; + file_class_A->m_enabled= true; + file_class_B->m_enabled= true; + + PSI_file_locker *file_locker; + + /* Simulate OPEN + READ of 100 bytes + CLOSE on descriptor 12 */ + + file_locker= psi->get_thread_file_name_locker(&file_state, file_key_A, PSI_FILE_OPEN, "AAA", NULL); + ok(file_locker != NULL, "locker"); + psi->start_file_open_wait(file_locker, __FILE__, __LINE__); + psi->end_file_open_wait_and_bind_to_descriptor(file_locker, 12); + + file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_READ); + ok(file_locker != NULL, "locker"); + psi->start_file_wait(file_locker, 100, __FILE__, __LINE__); + psi->end_file_wait(file_locker, 100); + + file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_CLOSE); + ok(file_locker != NULL, "locker"); + psi->start_file_wait(file_locker, 0, __FILE__, __LINE__); + psi->end_file_wait(file_locker, 0); + + /* Simulate uninstrumented-OPEN + WRITE on descriptor 24 */ + + file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 24, PSI_FILE_WRITE); + ok(file_locker == NULL, "no locker, since the open was not instrumented"); + + /* + Simulate uninstrumented-OPEN + WRITE on descriptor 12 : + the instrumentation should not leak (don't charge the file io on unknown B to "AAA") + */ + + file_locker= psi->get_thread_file_descriptor_locker(&file_state, (File) 12, PSI_FILE_WRITE); + ok(file_locker == NULL, "no locker, no leak"); + + unload_performance_schema(); +} + +void test_enabled() +{ +#ifdef LATER + PSI *psi; + + diag("test_enabled"); + + psi= load_perfschema(); + + PSI_mutex_key mutex_key_A; + PSI_mutex_key mutex_key_B; + PSI_mutex_info all_mutex[]= + { + { & mutex_key_A, "M-A", 0}, + { & mutex_key_B, "M-B", 0} + }; + + PSI_rwlock_key rwlock_key_A; + PSI_rwlock_key rwlock_key_B; + PSI_rwlock_info all_rwlock[]= + { + { & rwlock_key_A, "RW-A", 0}, + { & rwlock_key_B, "RW-B", 0} + }; + + PSI_cond_key cond_key_A; + PSI_cond_key cond_key_B; + PSI_cond_info all_cond[]= + { + { & cond_key_A, "C-A", 0}, + { & cond_key_B, "C-B", 0} + }; + + unload_performance_schema(); +#endif +} + +void test_event_name_index() +{ + PSI *psi; + PSI_bootstrap *boot; + PFS_global_param param; + + diag("test_event_name_index"); + + memset(& param, 0xFF, sizeof(param)); + param.m_enabled= true; + + /* NOTE: Need to add 4 to each index: table io, table lock, idle, metadata lock */ + + /* Per mutex info waits should be at [0..9] */ + param.m_mutex_class_sizing= 10; + /* Per rwlock info waits should be at [10..29] */ + param.m_rwlock_class_sizing= 20; + /* Per cond info waits should be at [30..69] */ + param.m_cond_class_sizing= 40; + /* Per file info waits should be at [70..149] */ + param.m_file_class_sizing= 80; + /* Per socket info waits should be at [150..309] */ + param.m_socket_class_sizing= 160; + /* Per table info waits should be at [310] */ + param.m_table_share_sizing= 320; + + param.m_thread_class_sizing= 0; + param.m_user_sizing= 0; + param.m_account_sizing= 0; + param.m_host_sizing= 0; + param.m_stage_class_sizing= 0; + param.m_events_stages_history_sizing= 0; + param.m_events_stages_history_long_sizing= 0; + param.m_statement_class_sizing= 0; + param.m_events_statements_history_sizing= 0; + param.m_events_statements_history_long_sizing= 0; + param.m_events_transactions_history_sizing= 0; + param.m_events_transactions_history_long_sizing= 0; + param.m_digest_sizing= 0; + param.m_session_connect_attrs_sizing= 0; + param.m_program_sizing= 0; + param.m_statement_stack_sizing= 10; + param.m_memory_class_sizing= 12; + param.m_metadata_lock_sizing= 10; + param.m_max_digest_length= 0; + param.m_max_sql_text_length= 1000; + + param.m_mutex_sizing= 0; + param.m_rwlock_sizing= 0; + param.m_cond_sizing= 0; + param.m_thread_sizing= 0; + param.m_table_sizing= 0; + param.m_file_sizing= 0; + param.m_file_handle_sizing= 0; + param.m_socket_sizing= 0; + param.m_events_waits_history_sizing= 0; + param.m_events_waits_history_long_sizing= 0; + param.m_setup_actor_sizing= 0; + param.m_setup_object_sizing= 0; + + param.m_hints.m_table_definition_cache = 100; + param.m_hints.m_table_open_cache = 100; + param.m_hints.m_max_connections = 100; + param.m_hints.m_open_files_limit = 100; + param.m_hints.m_max_prepared_stmt_count= 100; + + pre_initialize_performance_schema(); + boot= initialize_performance_schema(& param); + ok(boot != NULL, "bootstrap"); + psi= (PSI*) boot->get_interface(PSI_VERSION_1); + ok(psi != NULL, "psi"); + + PFS_mutex_class *mutex_class; + PSI_mutex_key dummy_mutex_key_1; + PSI_mutex_key dummy_mutex_key_2; + PSI_mutex_info dummy_mutexes[]= + { + { & dummy_mutex_key_1, "M-1", 0}, + { & dummy_mutex_key_2, "M-2", 0} + }; + + psi->register_mutex("X", dummy_mutexes, 2); + mutex_class= find_mutex_class(dummy_mutex_key_1); + ok(mutex_class != NULL, "mutex class 1"); + ok(mutex_class->m_event_name_index == 4, "index 4"); + mutex_class= find_mutex_class(dummy_mutex_key_2); + ok(mutex_class != NULL, "mutex class 2"); + ok(mutex_class->m_event_name_index == 5, "index 5"); + + PFS_rwlock_class *rwlock_class; + PSI_rwlock_key dummy_rwlock_key_1; + PSI_rwlock_key dummy_rwlock_key_2; + PSI_rwlock_info dummy_rwlocks[]= + { + { & dummy_rwlock_key_1, "RW-1", 0}, + { & dummy_rwlock_key_2, "RW-2", 0} + }; + + psi->register_rwlock("X", dummy_rwlocks, 2); + rwlock_class= find_rwlock_class(dummy_rwlock_key_1); + ok(rwlock_class != NULL, "rwlock class 1"); + ok(rwlock_class->m_event_name_index == 14, "index 14"); + rwlock_class= find_rwlock_class(dummy_rwlock_key_2); + ok(rwlock_class != NULL, "rwlock class 2"); + ok(rwlock_class->m_event_name_index == 15, "index 15"); + + PFS_cond_class *cond_class; + PSI_cond_key dummy_cond_key_1; + PSI_cond_key dummy_cond_key_2; + PSI_cond_info dummy_conds[]= + { + { & dummy_cond_key_1, "C-1", 0}, + { & dummy_cond_key_2, "C-2", 0} + }; + + psi->register_cond("X", dummy_conds, 2); + cond_class= find_cond_class(dummy_cond_key_1); + ok(cond_class != NULL, "cond class 1"); + ok(cond_class->m_event_name_index == 34, "index 34"); + cond_class= find_cond_class(dummy_cond_key_2); + ok(cond_class != NULL, "cond class 2"); + ok(cond_class->m_event_name_index == 35, "index 35"); + + PFS_file_class *file_class; + PSI_file_key dummy_file_key_1; + PSI_file_key dummy_file_key_2; + PSI_file_info dummy_files[]= + { + { & dummy_file_key_1, "F-1", 0}, + { & dummy_file_key_2, "F-2", 0} + }; + + psi->register_file("X", dummy_files, 2); + file_class= find_file_class(dummy_file_key_1); + ok(file_class != NULL, "file class 1"); + ok(file_class->m_event_name_index == 74, "index 74"); + file_class= find_file_class(dummy_file_key_2); + ok(file_class != NULL, "file class 2"); + ok(file_class->m_event_name_index == 75, "index 75"); + + PFS_socket_class *socket_class; + PSI_socket_key dummy_socket_key_1; + PSI_socket_key dummy_socket_key_2; + PSI_socket_info dummy_sockets[]= + { + { & dummy_socket_key_1, "S-1", 0}, + { & dummy_socket_key_2, "S-2", 0} + }; + + psi->register_socket("X", dummy_sockets, 2); + socket_class= find_socket_class(dummy_socket_key_1); + ok(socket_class != NULL, "socket class 1"); + ok(socket_class->m_event_name_index == 154, "index 154"); + socket_class= find_socket_class(dummy_socket_key_2); + ok(socket_class != NULL, "socket class 2"); + ok(socket_class->m_event_name_index == 155, "index 155"); + + ok(global_table_io_class.m_event_name_index == 0, "index 0"); + ok(global_table_lock_class.m_event_name_index == 1, "index 1"); + ok(wait_class_max= 314, "314 event names"); // 4 global classes + + shutdown_performance_schema(); +} + +void test_memory_instruments() +{ + PSI *psi; + PSI_thread *owner; + + diag("test_memory_instruments"); + + psi= load_perfschema(); + + PSI_memory_key memory_key_A; + PSI_memory_info all_memory[]= + { + { & memory_key_A, "M-A", 0} + }; + + PSI_thread_key thread_key_1; + PSI_thread_info all_thread[]= + { + { & thread_key_1, "T-1", 0} + }; + + psi->register_memory("test", all_memory, 1); + psi->register_thread("test", all_thread, 1); + + PFS_memory_class *memory_class_A; + PSI_thread *thread_1; + PSI_memory_key key; + + /* Preparation */ + + thread_1= psi->new_thread(thread_key_1, NULL, 0); + ok(thread_1 != NULL, "T-1"); + psi->set_thread_id(thread_1, 1); + + memory_class_A= find_memory_class(memory_key_A); + ok(memory_class_A != NULL, "memory info A"); + + /* Pretend thread T-1 is running, and enabled */ + /* ------------------------------------------ */ + + psi->set_thread(thread_1); + setup_thread(thread_1, true); + + /* Enable all instruments */ + + memory_class_A->m_enabled= true; + + /* for coverage, need to print stats collected. */ + + key= psi->memory_alloc(memory_key_A, 100, & owner); + ok(key == memory_key_A, "alloc memory info A"); + key= psi->memory_realloc(memory_key_A, 100, 200, & owner); + ok(key == memory_key_A, "realloc memory info A"); + key= psi->memory_realloc(memory_key_A, 200, 300, & owner); + ok(key == memory_key_A, "realloc up memory info A"); + key= psi->memory_realloc(memory_key_A, 300, 50, & owner); + ok(key == memory_key_A, "realloc down memory info A"); + psi->memory_free(memory_key_A, 50, owner); + + /* Use global instrumentation only */ + /* ------------------------------- */ + + flag_thread_instrumentation= false; + + key= psi->memory_alloc(memory_key_A, 100, & owner); + ok(key == memory_key_A, "alloc memory info A"); + key= psi->memory_realloc(memory_key_A, 100, 200, & owner); + ok(key == memory_key_A, "realloc memory info A"); + key= psi->memory_realloc(memory_key_A, 200, 300, & owner); + ok(key == memory_key_A, "realloc up memory info A"); + key= psi->memory_realloc(memory_key_A, 300, 50, & owner); + ok(key == memory_key_A, "realloc down memory info A"); + psi->memory_free(memory_key_A, 50, owner); + + /* Garbage, for robustness */ + /* ----------------------- */ + + key= psi->memory_alloc(9999, 100, & owner); + ok(key == PSI_NOT_INSTRUMENTED, "alloc with unknown key"); + key= psi->memory_realloc(PSI_NOT_INSTRUMENTED, 100, 200, & owner); + ok(key == PSI_NOT_INSTRUMENTED, "realloc with unknown key"); + psi->memory_free(PSI_NOT_INSTRUMENTED, 200, owner); + + shutdown_performance_schema(); +} + +void test_leaks() +{ + PSI_bootstrap *boot; + PFS_global_param param; + + /* Allocate everything, to make sure cleanup does not forget anything. */ + + memset(& param, 0xFF, sizeof(param)); + param.m_enabled= true; + param.m_mutex_class_sizing= 10; + param.m_rwlock_class_sizing= 10; + param.m_cond_class_sizing= 10; + param.m_thread_class_sizing= 10; + param.m_table_share_sizing= 10; + param.m_file_class_sizing= 10; + param.m_socket_class_sizing= 10; + param.m_mutex_sizing= 1000; + param.m_rwlock_sizing= 1000; + param.m_cond_sizing= 1000; + param.m_thread_sizing= 1000; + param.m_table_sizing= 1000; + param.m_file_sizing= 1000; + param.m_file_handle_sizing= 1000; + param.m_socket_sizing= 1000; + param.m_events_waits_history_sizing= 10; + param.m_events_waits_history_long_sizing= 1000; + param.m_setup_actor_sizing= 1000; + param.m_setup_object_sizing= 1000; + param.m_host_sizing= 1000; + param.m_user_sizing= 1000; + param.m_account_sizing= 1000; + param.m_stage_class_sizing= 10; + param.m_events_stages_history_sizing= 10; + param.m_events_stages_history_long_sizing= 1000; + param.m_statement_class_sizing= 10; + param.m_events_statements_history_sizing= 10; + param.m_events_statements_history_long_sizing= 1000; + param.m_session_connect_attrs_sizing= 1000; + param.m_memory_class_sizing= 10; + param.m_metadata_lock_sizing= 1000; + param.m_digest_sizing= 1000; + param.m_program_sizing= 1000; + param.m_statement_stack_sizing= 10; + param.m_max_digest_length= 1000; + param.m_max_sql_text_length= 1000; + + param.m_hints.m_table_definition_cache = 100; + param.m_hints.m_table_open_cache = 100; + param.m_hints.m_max_connections = 100; + param.m_hints.m_open_files_limit = 100; + param.m_hints.m_max_prepared_stmt_count= 100; + + pre_initialize_performance_schema(); + boot= initialize_performance_schema(& param); + ok(boot != NULL, "bootstrap"); + shutdown_performance_schema(); + + /* Leaks will be reported with valgrind */ +} + +void do_all_tests() +{ + /* Using initialize_performance_schema(), no partial init needed. */ + + test_bootstrap(); + test_bad_registration(); + test_init_disabled(); + test_locker_disabled(); + test_file_instrumentation_leak(); + test_event_name_index(); + test_memory_instruments(); + test_leaks(); +} + +int main(int argc, char **argv) +{ + plan(232); + MY_INIT(argv[0]); + do_all_tests(); + my_end(0); + return (exit_status()); +} diff --git a/storage/perfschema/unittest/pfs_account-oom-t.cc b/storage/perfschema/unittest/pfs_account-oom-t.cc new file mode 100644 index 00000000..97aea4c5 --- /dev/null +++ b/storage/perfschema/unittest/pfs_account-oom-t.cc @@ -0,0 +1,174 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_thread.h> +#include <pfs_instr.h> +#include <pfs_stat.h> +#include <pfs_global.h> +#include <pfs_defaults.h> +#include <pfs_user.h> +#include <pfs_host.h> +#include <pfs_account.h> +#include <pfs_buffer_container.h> +#include <tap.h> + +#include "stub_pfs_global.h" +#include "stub_global_status_var.h" + +#include <string.h> /* memset */ + +PFS_thread pfs_thread; + +void initialize_performance_schema_helper(PFS_global_param *param) +{ + stub_alloc_always_fails= false; + stub_alloc_fails_after_count= 1000; + + param->m_enabled= true; + param->m_thread_class_sizing= 10; + param->m_thread_sizing= 1000; + param->m_account_sizing= 1000; + transaction_class_max= 0; + + pfs_thread.m_account_hash_pins= NULL; + + init_event_name_sizing(param); + init_sync_class(param->m_mutex_class_sizing, param->m_rwlock_class_sizing, param->m_cond_class_sizing); + init_thread_class(param->m_thread_class_sizing); + init_table_share(param->m_table_share_sizing); + init_table_share_lock_stat(param->m_table_lock_stat_sizing); + init_table_share_index_stat(param->m_index_stat_sizing); + init_file_class(param->m_file_class_sizing); + init_stage_class(param->m_stage_class_sizing); + init_statement_class(param->m_statement_class_sizing); + init_socket_class(param->m_socket_class_sizing); + init_memory_class(param->m_memory_class_sizing); + init_instruments(param); + init_events_waits_history_long(param->m_events_waits_history_long_sizing); + init_events_stages_history_long(param->m_events_stages_history_long_sizing); + init_events_statements_history_long(param->m_events_statements_history_long_sizing); + init_events_transactions_history_long(param->m_events_transactions_history_long_sizing); + init_file_hash(param); + init_table_share_hash(param); + init_setup_actor(param); + init_setup_actor_hash(param); + init_setup_object(param); + init_setup_object_hash(param); + init_host(param); + init_host_hash(param); + init_user(param); + init_user_hash(param); + init_account(param); + init_account_hash(param); + init_digest(param); + init_digest_hash(param); + init_program(param); + init_program_hash(param); + init_prepared_stmt(param); + pfs_initialized= true; +} + +void test_oom() +{ + PFS_global_param param; + PFS_account *pfs_account; + const char *username= "username"; + const char *hostname= "hostname"; + + uint user_len= (uint)strlen(username); + uint host_len= (uint)strlen(hostname); + + /* Account. */ + memset(¶m, 0, sizeof(param)); + initialize_performance_schema_helper(¶m); + stub_alloc_fails_after_count= 1; + pfs_account= find_or_create_account(&pfs_thread, username, user_len, hostname, host_len); + ok(pfs_account == NULL, "oom (account)"); + ok(global_account_container.m_lost == 1, "lost (account)"); + shutdown_performance_schema(); + + /* Account waits. */ + memset(¶m, 0, sizeof(param)); + param.m_mutex_class_sizing= 10; + initialize_performance_schema_helper(¶m); + stub_alloc_fails_after_count= 2; + pfs_account= find_or_create_account(&pfs_thread, username, user_len, hostname, host_len); + ok(pfs_account == NULL, "oom (account waits)"); + ok(global_account_container.m_lost == 1, "lost (account waits)"); + shutdown_performance_schema(); + + + /* Account stages. */ + memset(¶m, 0, sizeof(param)); + param.m_stage_class_sizing= 10; + initialize_performance_schema_helper(¶m); + stub_alloc_fails_after_count= 3; + pfs_account= find_or_create_account(&pfs_thread, username, user_len, hostname, host_len); + ok(pfs_account == NULL, "oom (account stages)"); + ok(global_account_container.m_lost == 1, "lost (account stages)"); + shutdown_performance_schema(); + + /* Account statements. */ + memset(¶m, 0, sizeof(param)); + param.m_statement_class_sizing= 10; + initialize_performance_schema_helper(¶m); + stub_alloc_fails_after_count= 3; + pfs_account= find_or_create_account(&pfs_thread, username, user_len, hostname, host_len); + ok(pfs_account == NULL, "oom (account statements)"); + ok(global_account_container.m_lost == 1, "lost (account statements)"); + shutdown_performance_schema(); + + /* Account transactions. */ + memset(¶m, 0, sizeof(param)); + initialize_performance_schema_helper(¶m); + transaction_class_max= 1; + stub_alloc_fails_after_count= 3; + pfs_account= find_or_create_account(&pfs_thread, username, user_len, hostname, host_len); + ok(pfs_account == NULL, "oom (account transactions)"); + ok(global_account_container.m_lost == 1, "lost (account transactions)"); + shutdown_performance_schema(); + + /* Account memory. */ + memset(¶m, 0, sizeof(param)); + param.m_memory_class_sizing= 10; + initialize_performance_schema_helper(¶m); + stub_alloc_fails_after_count= 3; + pfs_account= find_or_create_account(&pfs_thread, username, user_len, hostname, host_len); + ok(pfs_account == NULL, "oom (account memory)"); + ok(global_account_container.m_lost == 1, "lost (account memory)"); + shutdown_performance_schema(); +} + +void do_all_tests() +{ + test_oom(); +} + +int main(int, char **) +{ + plan(12); + MY_INIT("pfs_account-oom-t"); + do_all_tests(); + my_end(0); + return (exit_status()); +} diff --git a/storage/perfschema/unittest/pfs_connect_attr-t.cc b/storage/perfschema/unittest/pfs_connect_attr-t.cc new file mode 100644 index 00000000..dee56ebc --- /dev/null +++ b/storage/perfschema/unittest/pfs_connect_attr-t.cc @@ -0,0 +1,352 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_thread.h> +#include <pfs_server.h> +#include <pfs_instr_class.h> +#include <pfs_instr.h> +#include <pfs_global.h> +#include <tap.h> + + +#include <string.h> +#include <memory.h> + +/* test helpers, to inspect data */ +bool read_nth_attr(const char *connect_attrs, uint connect_attrs_length, + const CHARSET_INFO *connect_attrs_cs, + uint ordinal, + char *attr_name, uint max_attr_name, + uint *attr_name_length, + char *attr_value, uint max_attr_value, + uint *attr_value_length); + +void test_blob_parser() +{ + char name[100], value[4096]; + unsigned char packet[10000], *ptr; + uint name_len, value_len, idx, packet_length; + bool result; + const CHARSET_INFO *cs= &my_charset_utf8mb3_bin; + + diag("test_blob_parser"); + + result= read_nth_attr("", 0, cs, 0, + name, 32, &name_len, value, 1024, &value_len); + ok(result == false, "zero length blob"); + + + result= read_nth_attr("\x1", 1, cs, 0, + name, 32, &name_len, value, 1024, &value_len); + ok(result == false, "invalid key length"); + + + result= read_nth_attr("\x2k1\x1", 4, cs, 0, + name, 32, &name_len, value, 1024, &value_len); + ok(result == false, "invalid value length"); + + + result= read_nth_attr("\x2k1\x2v1", 6, cs, 0, + name, 32, &name_len, value, 1024, &value_len); + ok(result == true, "one pair return"); + ok(name_len == 2, "one pair attr name length"); + ok(!strncmp(name, "k1", name_len), "one pair attr name"); + ok(value_len == 2, "one pair value length"); + ok(!strncmp(value, "v1", value_len), "one pair value"); + + result= read_nth_attr("\x2k1\x2v1", 6, cs, 1, + name, 32, &name_len, value, 1024, &value_len); + ok(result == false, "no second arg"); + + result= read_nth_attr("\x2k1\x2v1\x2k2\x2v2", 12, cs, 1, + name, 32, &name_len, value, 1024, &value_len); + ok(result == true, "two pairs return"); + ok(name_len == 2, "two pairs attr name length"); + ok(!strncmp(name, "k2", name_len), "two pairs attr name"); + ok(value_len == 2, "two pairs value length"); + ok(!strncmp(value, "v2", value_len), "two pairs value"); + + result= read_nth_attr("\x2k1\xff\x2k2\x2v2", 12, cs, 1, + name, 32, &name_len, value, 1024, &value_len); + ok(result == false, "two pairs first value bad return"); + + result= read_nth_attr("\x2k1\x2v1\x2k2\x2v2", 10, cs, 1, + name, 32, &name_len, value, 1024, &value_len); + ok(result == false, "two pairs wrong global length"); + + result= read_nth_attr("\x21z123456789z123456789z123456789z12\x2v1", 37, cs, 0, + name, 32, &name_len, value, 1024, &value_len); + ok(result == true, "attr name overflow"); + ok(name_len == 32, "attr name overflow length"); + ok(!strncmp(name, "z123456789z123456789z123456789z1", name_len), + "attr name overflow name"); + ok(value_len == 2, "attr name overflow value length"); + ok(!strncmp(value, "v1", value_len), "attr name overflow value"); + + packet[0]= 2; + packet[1]= 'k'; + packet[2]= '1'; + ptr= net_store_length(packet + 3, 1025); + for (idx= 0; idx < 1025; idx++) + *ptr++= '0' + (idx % 10); + packet_length= (uint) (ptr - packet); + result= read_nth_attr((char *) packet, packet_length, cs, 0, + name, 32, &name_len, value, 1024, &value_len); + ok(result == true, "attr value overflow"); + ok(name_len == 2, "attr value overflow length"); + ok(!strncmp(name, "k1", name_len), "attr value overflow name"); + ok(value_len == 1024, "attr value overflow value length"); + for (idx= 0; idx < 1024; idx++) + { + if (value[idx] != (char) ('0' + (idx % 10))) + break; + } + ok (idx == 1024, "attr value overflow value"); + + result= read_nth_attr("\x21z123456789z123456789z123456789z12\x2v1\x2k2\x2v2", + 43, cs, 1, + name, 32, &name_len, value, 1024, &value_len); + ok(result == true, "prev attr name overflow"); + ok(name_len == 2, "prev attr name overflow length"); + ok(!strncmp(name, "k2", name_len), + "prev attr name overflow name"); + ok(value_len == 2, "prev attr name overflow value length"); + ok(!strncmp(value, "v2", value_len), "prev attr name overflow value"); + + + packet[1]= 'k'; + packet[2]= '1'; + packet[3]= 2; + packet[4]= 'v'; + packet[5]= '1'; + + for(idx= 251; idx < 256; idx++) + { + packet[0]= idx; + result= read_nth_attr((char *) packet, 6, cs, 0, + name, 32, &name_len, value, 1024, &value_len); + ok(result == false, "invalid string length %d", idx); + } + + memset(packet, 0, sizeof(packet)); + for (idx=0; idx < 1660 /* *6 = 9960 */; idx++) + memcpy(packet + idx * 6, "\x2k1\x2v1", 6); + result= read_nth_attr((char *) packet, 8192, cs, 1364, + name, 32, &name_len, value, 1024, &value_len); + ok(result == true, "last valid attribute %d", 1364); + result= read_nth_attr((char *) packet, 8192, cs, 1365, + name, 32, &name_len, value, 1024, &value_len); + ok(result == false, "first attribute that's cut %d", 1365); +} + +void test_multibyte_lengths() +{ + char name[100], value[4096]; + uint name_len, value_len; + bool result; + const CHARSET_INFO *cs= &my_charset_utf8mb3_bin; + + unsigned char var_len_packet[] = { + 252, 2, 0, 'k', '1', + 253, 2, 0, 0, 'v', '1', + 254, 2, 0, 0, 0, 0, 0, 0, 0, 'k', '2', + 254, 2, 0, 0, 0, 0, 0, 0, 0, 'v', '2' + }; + + result= read_nth_attr((char *) var_len_packet, sizeof(var_len_packet), cs, 0, + name, 32, &name_len, value, 1024, &value_len); + ok(result == true, "multibyte lengths return"); + ok(name_len == 2, "multibyte lengths name length"); + ok(!strncmp(name, "k1", name_len), "multibyte lengths attr name"); + ok(value_len == 2, "multibyte lengths value length"); + ok(!strncmp(value, "v1", value_len), "multibyte lengths value"); + + result= read_nth_attr((char *) var_len_packet, sizeof(var_len_packet), cs, 1, + name, 32, &name_len, value, 1024, &value_len); + ok(result == true, "multibyte lengths second attr return"); + ok(name_len == 2, "multibyte lengths second attr name length"); + ok(!strncmp(name, "k2", name_len), "multibyte lengths second attr attr name"); + ok(value_len == 2, "multibyte lengths value length"); + ok(!strncmp(value, "v2", value_len), "multibyte lengths second attr value"); +} + + +void test_utf8_parser() +{ + /* utf8 max byte length per character is 6 */ + char name[33 * 6], value[1024 * 6], packet[1500 * 6], *ptr; + uint name_len, value_len; + bool result; + const CHARSET_INFO *cs= &my_charset_utf8mb3_bin; + + /* note : this is encoded in utf-8 */ + const char *attr1= "Георги"; + const char *val1= "Кодинов"; + const char *attr2= "Пловдив"; + const char *val2= "България"; + + ptr= packet; + *ptr++= strlen(attr1); + memcpy(ptr, attr1, strlen(attr1)); + ptr+= strlen(attr1); + *ptr++= strlen(val1); + memcpy(ptr, val1, strlen(val1)); + ptr+= strlen(val1); + + *ptr++= strlen(attr2); + memcpy(ptr, attr2, strlen(attr2)); + ptr+= strlen(attr2); + *ptr++= strlen(val2); + memcpy(ptr, val2, strlen(val2)); + ptr+= strlen(val2); + + diag("test_utf8_parser attr pair #1"); + + result= read_nth_attr((char *) packet, ptr - packet, cs, 0, + name, sizeof(name), &name_len, + value, sizeof(value), &value_len); + ok(result == true, "return"); + ok(name_len == strlen(attr1), "name length"); + ok(!strncmp(name, attr1, name_len), "attr name"); + ok(value_len == strlen(val1), "value length"); + ok(!strncmp(value, val1, value_len), "value"); + + diag("test_utf8_parser attr pair #2"); + result= read_nth_attr((char *) packet, ptr - packet, cs, 1, + name, sizeof(name), &name_len, + value, sizeof(value), &value_len); + ok(result == true, "return"); + ok(name_len == strlen(attr2), "name length"); + ok(!strncmp(name, attr2, name_len), "attr name"); + ok(value_len == strlen(val2), "value length"); + ok(!strncmp(value, val2, value_len), "value"); +} + + +void test_utf8_parser_bad_encoding() +{ + /* utf8 max byte length per character is 3*/ + char name[33 * 3], value[1024 * 3], packet[1500 * 3], *ptr; + uint name_len, value_len; + bool result; + const CHARSET_INFO *cs= &my_charset_utf8mb3_bin; + + /* note : this is encoded in utf-8 */ + const char *attr= "Георги"; + const char *val= "Кодинов"; + + ptr= packet; + *ptr++= strlen(attr); + memcpy(ptr, attr, strlen(attr)); + ptr[0]= (char)0xFA; // invalid UTF-8 char + ptr+= strlen(attr); + *ptr++= strlen(val); + memcpy(ptr, val, strlen(val)); + ptr+= strlen(val); + + diag("test_utf8_parser_bad_encoding"); + + result= read_nth_attr((char *) packet, ptr - packet, cs, 0, + name, sizeof(name), &name_len, + value, sizeof(value), &value_len); + ok(result == false, "return"); +} + +const CHARSET_INFO *cs_cp1251; + +void test_cp1251_parser() +{ + /* utf8 max byte length per character is 3*/ + char name[33 * 3], value[1024 * 3], packet[1500 * 3], *ptr; + uint name_len, value_len; + bool result; + + /* note : this is Георги in windows-1251 */ + const char *attr1= "\xc3\xe5\xee\xf0\xe3\xe8"; + /* note : this is Кодинов in windows-1251 */ + const char *val1= "\xca\xee\xe4\xe8\xed\xee\xe2"; + /* note : this is Пловдив in windows-1251 */ + const char *attr2= "\xcf\xeb\xee\xe2\xe4\xe8\xe2"; + /* note : this is България in windows-1251 */ + const char *val2= "\xc1\xfa\xeb\xe3\xe0\xf0\xe8\xff"; + + ptr= packet; + *ptr++= strlen(attr1); + memcpy(ptr, attr1, strlen(attr1)); + ptr+= strlen(attr1); + *ptr++= strlen(val1); + memcpy(ptr, val1, strlen(val1)); + ptr+= strlen(val1); + + *ptr++= strlen(attr2); + memcpy(ptr, attr2, strlen(attr2)); + ptr+= strlen(attr2); + *ptr++= strlen(val2); + memcpy(ptr, val2, strlen(val2)); + ptr+= strlen(val2); + + diag("test_cp1251_parser attr pair #1"); + + result= read_nth_attr((char *) packet, ptr - packet, cs_cp1251, 0, + name, sizeof(name), &name_len, + value, sizeof(value), &value_len); + ok(result == true, "return"); + /* need to compare to the UTF-8 equivalents */ + ok(name_len == strlen("Георги"), "name length"); + ok(!strncmp(name, "Георги", name_len), "attr name"); + ok(value_len == strlen("Кодинов"), "value length"); + ok(!strncmp(value, "Кодинов", value_len), "value"); + + diag("test_cp1251_parser attr pair #2"); + result= read_nth_attr((char *) packet, ptr - packet, cs_cp1251, 1, + name, sizeof(name), &name_len, + value, sizeof(value), &value_len); + ok(result == true, "return"); + /* need to compare to the UTF-8 equivalents */ + ok(name_len == strlen("Пловдив"), "name length"); + ok(!strncmp(name, "Пловдив", name_len), "attr name"); + ok(value_len == strlen("България"), "value length"); + ok(!strncmp(value, "България", value_len), "value"); +} + + +void do_all_tests() +{ + test_blob_parser(); + test_multibyte_lengths(); + test_utf8_parser(); + test_utf8_parser_bad_encoding(); + test_cp1251_parser(); +} + +int main(int, char **) +{ + MY_INIT("pfs_connect_attr-t"); + + cs_cp1251= get_charset_by_csname("cp1251", MY_CS_PRIMARY, MYF(0)); + if (!cs_cp1251) + diag("skipping the cp1251 tests : missing character set"); + plan(59 + (cs_cp1251 ? 10 : 0)); + do_all_tests(); + return (exit_status()); +} diff --git a/storage/perfschema/unittest/pfs_host-oom-t.cc b/storage/perfschema/unittest/pfs_host-oom-t.cc new file mode 100644 index 00000000..f903561d --- /dev/null +++ b/storage/perfschema/unittest/pfs_host-oom-t.cc @@ -0,0 +1,148 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_thread.h> +#include <pfs_instr.h> +#include <pfs_stat.h> +#include <pfs_global.h> +#include <pfs_host.h> +#include <pfs_buffer_container.h> +#include <tap.h> + +#include "stub_pfs_global.h" +#include "stub_global_status_var.h" + +#include <string.h> /* memset */ + +extern struct PSI_bootstrap PFS_bootstrap; + +void test_oom() +{ + PSI *psi; + PFS_global_param param; + PSI_bootstrap *boot; + + memset(& param, 0xFF, sizeof(param)); + param.m_enabled= true; + param.m_mutex_class_sizing= 0; + param.m_rwlock_class_sizing= 0; + param.m_cond_class_sizing= 0; + param.m_thread_class_sizing= 10; + param.m_table_share_sizing= 0; + param.m_file_class_sizing= 0; + param.m_socket_class_sizing= 0; + param.m_mutex_sizing= 0; + param.m_rwlock_sizing= 0; + param.m_cond_sizing= 0; + param.m_thread_sizing= 1000; + param.m_table_sizing= 0; + param.m_file_sizing= 0; + param.m_file_handle_sizing= 0; + param.m_socket_sizing= 0; + param.m_events_waits_history_sizing= 10; + param.m_events_waits_history_long_sizing= 0; + param.m_setup_actor_sizing= 0; + param.m_setup_object_sizing= 0; + param.m_user_sizing= 0; + param.m_host_sizing= 1000; + param.m_account_sizing= 0; + param.m_stage_class_sizing= 50; + param.m_events_stages_history_sizing= 0; + param.m_events_stages_history_long_sizing= 0; + param.m_statement_class_sizing= 50; + param.m_events_statements_history_sizing= 0; + param.m_events_statements_history_long_sizing= 0; + param.m_events_transactions_history_sizing= 0; + param.m_events_transactions_history_long_sizing= 0; + param.m_digest_sizing= 0; + param.m_session_connect_attrs_sizing= 0; + param.m_program_sizing= 0; + param.m_statement_stack_sizing= 0; + param.m_memory_class_sizing= 10; + param.m_metadata_lock_sizing= 0; + param.m_max_digest_length= 0; + param.m_max_sql_text_length= 0; + + /* Setup */ + + stub_alloc_always_fails= false; + stub_alloc_fails_after_count= 1000; + + pre_initialize_performance_schema(); + boot= initialize_performance_schema(¶m); + psi= (PSI *)boot->get_interface(PSI_VERSION_1); + + PSI_thread_key thread_key_1; + PSI_thread_info all_thread[]= + { + {&thread_key_1, "T-1", 0} + }; + psi->register_thread("test", all_thread, 1); + + PSI_thread *thread_1= psi->new_thread(thread_key_1, NULL, 0); + psi->set_thread(thread_1); + + /* Tests */ + + int first_fail= 1; + stub_alloc_fails_after_count= first_fail; + psi->set_thread_account("", 0, "host1", 5); + ok(global_host_container.m_lost == 1, "oom (host)"); + + stub_alloc_fails_after_count= first_fail + 1; + psi->set_thread_account("", 0, "host2", 5); + ok(global_host_container.m_lost == 2, "oom (host waits)"); + + stub_alloc_fails_after_count= first_fail + 2; + psi->set_thread_account("", 0, "host3", 5); + ok(global_host_container.m_lost == 3, "oom (host stages)"); + + stub_alloc_fails_after_count= first_fail + 3; + psi->set_thread_account("", 0, "host4", 5); + ok(global_host_container.m_lost == 4, "oom (host statements)"); + + stub_alloc_fails_after_count= first_fail + 4; + psi->set_thread_account("", 0, "host5", 5); + ok(global_host_container.m_lost == 5, "oom (host transactions)"); + + stub_alloc_fails_after_count= first_fail + 5; + psi->set_thread_account("", 0, "host6", 5); + ok(global_host_container.m_lost == 6, "oom (host memory)"); + + shutdown_performance_schema(); +} + +void do_all_tests() +{ + test_oom(); +} + +int main(int, char **) +{ + plan(6); + MY_INIT("pfs_host-oom-t"); + do_all_tests(); + my_end(0); + return (exit_status()); +} + diff --git a/storage/perfschema/unittest/pfs_instr-oom-t.cc b/storage/perfschema/unittest/pfs_instr-oom-t.cc new file mode 100644 index 00000000..579aec6c --- /dev/null +++ b/storage/perfschema/unittest/pfs_instr-oom-t.cc @@ -0,0 +1,459 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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 */ + +#include <my_global.h> +#include <my_thread.h> +#include <pfs_instr.h> +#include <pfs_stat.h> +#include <pfs_global.h> +#include <pfs_user.h> +#include <pfs_host.h> +#include <pfs_account.h> +#include <pfs_instr_class.h> +#include <pfs_buffer_container.h> +#include <tap.h> + +#include "stub_pfs_global.h" +#include "stub_global_status_var.h" + +#include <string.h> /* memset */ + +extern struct PSI_bootstrap PFS_bootstrap; + +PSI_thread_key thread_key_1; +PSI_thread_info all_thread[]= +{ + {&thread_key_1, "T-1", 0} +}; + +/** Simulate initialize_performance_schema(). */ + +PSI * initialize_performance_schema_helper(PFS_global_param *param) +{ + PSI *psi; + + stub_alloc_always_fails= false; + stub_alloc_fails_after_count= 1000; + + param->m_enabled= true; + param->m_thread_class_sizing= 10; + param->m_thread_sizing= 1000; + + pre_initialize_performance_schema(); + + init_event_name_sizing(param); + init_sync_class(param->m_mutex_class_sizing, param->m_rwlock_class_sizing, param->m_cond_class_sizing); + init_thread_class(param->m_thread_class_sizing); + init_table_share(param->m_table_share_sizing); + init_table_share_lock_stat(param->m_table_lock_stat_sizing); + init_table_share_index_stat(param->m_index_stat_sizing); + init_file_class(param->m_file_class_sizing); + init_stage_class(param->m_stage_class_sizing); + init_statement_class(param->m_statement_class_sizing); + init_socket_class(param->m_socket_class_sizing); + init_memory_class(param->m_memory_class_sizing); + init_instruments(param); + init_events_waits_history_long(param->m_events_waits_history_long_sizing); + init_events_stages_history_long(param->m_events_stages_history_long_sizing); + init_events_statements_history_long(param->m_events_statements_history_long_sizing); + init_events_transactions_history_long(param->m_events_transactions_history_long_sizing); + init_file_hash(param); + init_table_share_hash(param); + init_setup_actor(param); + init_setup_actor_hash(param); + init_setup_object(param); + init_setup_object_hash(param); + init_host(param); + init_host_hash(param); + init_user(param); + init_user_hash(param); + init_account(param); + init_account_hash(param); + init_digest(param); + init_digest_hash(param); + init_program(param); + init_program_hash(param); + init_prepared_stmt(param); + pfs_initialized= true; + + PSI_bootstrap *boot= &PFS_bootstrap; + psi= (PSI *)boot->get_interface(PSI_VERSION_1); + psi->register_thread("test", all_thread, 1); + return (psi); +} + +void test_oom() +{ + int rc; + PSI *psi; + PFS_global_param param; + + stub_alloc_always_fails= false; + stub_alloc_fails_after_count= 1000; + + PFS_mutex_class dummy_mutex_class; + PFS_rwlock_class dummy_rwlock_class; + PFS_cond_class dummy_cond_class; + PFS_thread_class dummy_thread_class; + PFS_file_class dummy_file_class; + PFS_socket_class dummy_socket_class; + PFS_table_share dummy_table_share; + PFS_mutex *mutex_1; + PFS_mutex *mutex_2; + PFS_rwlock *rwlock_1; + PFS_rwlock *rwlock_2; + PFS_cond *cond_1; + PFS_cond *cond_2; + PFS_thread *thread_1; + PFS_thread *thread_2; + PFS_file *file_1; + PFS_file *file_2; + PFS_socket *socket_1; + PFS_socket *socket_2; + PFS_table *table_1; + PFS_table *table_2; + + memset(& param, 0xFF, sizeof(param)); + param.m_enabled= true; + param.m_mutex_class_sizing= 1; + param.m_rwlock_class_sizing= 1; + param.m_cond_class_sizing= 1; + param.m_thread_class_sizing= 1; + param.m_table_share_sizing= 1; + param.m_file_class_sizing= 1; + param.m_socket_class_sizing= 1; + param.m_mutex_sizing= 1; + param.m_rwlock_sizing= 1; + param.m_cond_sizing= 1; + param.m_thread_sizing= 1; + param.m_table_sizing= 1; + param.m_file_sizing= 1; + param.m_file_handle_sizing= 100; + param.m_socket_sizing= 2; + param.m_events_waits_history_sizing= 10; + param.m_events_waits_history_long_sizing= 10000; + param.m_setup_actor_sizing= 0; + param.m_setup_object_sizing= 0; + param.m_host_sizing= 0; + param.m_user_sizing= 0; + param.m_account_sizing= 0; + param.m_stage_class_sizing= 0; + param.m_events_stages_history_sizing= 0; + param.m_events_stages_history_long_sizing= 0; + param.m_statement_class_sizing= 0; + param.m_events_statements_history_sizing= 0; + param.m_events_statements_history_long_sizing= 0; + param.m_events_transactions_history_sizing= 0; + param.m_events_transactions_history_long_sizing= 0; + param.m_digest_sizing= 0; + param.m_session_connect_attrs_sizing= 0; + param.m_program_sizing= 0; + param.m_prepared_stmt_sizing= 0; + param.m_statement_stack_sizing= 0; + param.m_memory_class_sizing= 1; + param.m_metadata_lock_sizing= 0; + param.m_max_digest_length= 0; + param.m_max_sql_text_length= 0; + + init_event_name_sizing(¶m); + rc= init_instruments(¶m); + ok(rc == 0, "instances init"); + + dummy_mutex_class.m_event_name_index= 0; + dummy_mutex_class.m_flags= 0; + dummy_mutex_class.m_enabled= true; + dummy_mutex_class.m_volatility= PSI_VOLATILITY_UNKNOWN; + dummy_rwlock_class.m_event_name_index= 1; + dummy_rwlock_class.m_flags= 0; + dummy_rwlock_class.m_enabled= true; + dummy_rwlock_class.m_volatility= PSI_VOLATILITY_UNKNOWN; + dummy_cond_class.m_event_name_index= 2; + dummy_cond_class.m_flags= 0; + dummy_cond_class.m_enabled= true; + dummy_cond_class.m_volatility = PSI_VOLATILITY_UNKNOWN; + dummy_file_class.m_event_name_index= 3; + dummy_file_class.m_flags= 0; + dummy_file_class.m_enabled= true; + dummy_file_class.m_volatility = PSI_VOLATILITY_UNKNOWN; + dummy_socket_class.m_event_name_index= 4; + dummy_socket_class.m_flags= 0; + dummy_socket_class.m_enabled= true; + dummy_socket_class.m_volatility = PSI_VOLATILITY_UNKNOWN; + dummy_table_share.m_enabled= true; + dummy_table_share.m_timed= true; + + /* Create mutex. */ + stub_alloc_always_fails= false; + mutex_1= create_mutex(&dummy_mutex_class, NULL); + ok(mutex_1 != NULL, "create mutex"); + destroy_mutex(mutex_1); + cleanup_instruments(); + + stub_alloc_always_fails= true; + mutex_2= create_mutex(&dummy_mutex_class, NULL); + ok(mutex_2 == NULL, "oom (create mutex)"); + + /* Create rwlock. */ + stub_alloc_always_fails = false; + rc = init_instruments(¶m); + ok(rc == 0, "instances init"); + rwlock_1= create_rwlock(&dummy_rwlock_class, NULL); + ok(rwlock_1 != NULL, "create rwlock"); + destroy_rwlock(rwlock_1); + cleanup_instruments(); + + stub_alloc_always_fails= true; + rwlock_2= create_rwlock(&dummy_rwlock_class, NULL); + ok(rwlock_2 == NULL, "oom (create rwlock)"); + + /* Create cond. */ + stub_alloc_always_fails = false; + rc = init_instruments(¶m); + ok(rc == 0, "instances init"); + cond_1= create_cond(&dummy_cond_class, NULL); + ok(cond_1 != NULL, "create cond"); + destroy_cond(cond_1); + cleanup_instruments(); + + stub_alloc_always_fails= true; + cond_2= create_cond(&dummy_cond_class, NULL); + ok(cond_2 == NULL, "oom (create cond)"); + + /* Create file. */ + PFS_thread fake_thread; + rc = init_instruments(¶m); + fake_thread.m_filename_hash_pins= NULL; + init_file_hash(¶m); + + stub_alloc_always_fails = true; + file_2 = find_or_create_file(&fake_thread, &dummy_file_class, "dummy", 5, true); + ok(file_2 == NULL, "oom (create file)"); + + stub_alloc_always_fails= false; + file_1= find_or_create_file(&fake_thread, &dummy_file_class, "dummy", 5, true); + ok(file_1 != NULL, "create file"); + release_file(file_1); + cleanup_instruments(); + + /* Create socket. */ + stub_alloc_always_fails = false; + rc = init_instruments(¶m); + ok(rc == 0, "instances init"); + socket_1= create_socket(&dummy_socket_class, NULL, NULL, 0); + ok(socket_1 != NULL, "create socket"); + destroy_socket(socket_1); + cleanup_instruments(); + + stub_alloc_always_fails= true; + socket_2= create_socket(&dummy_socket_class, NULL, NULL, 0); + ok(socket_2 == NULL, "oom (create socket)"); + + /* Create table. */ + stub_alloc_always_fails= false; + rc = init_instruments(¶m); + table_1= create_table(&dummy_table_share, &fake_thread, NULL); + ok(table_1 != NULL, "create table"); + destroy_table(table_1); + cleanup_instruments(); + + stub_alloc_always_fails= true; + table_2= create_table(&dummy_table_share, &fake_thread, NULL); + ok(table_2 == NULL, "oom (create table)"); + + /* Create thread. */ + stub_alloc_always_fails= false; + rc = init_instruments(¶m); + thread_1= create_thread(&dummy_thread_class, NULL, 0); + ok(thread_1 != NULL, "create thread"); + destroy_thread(thread_1); + cleanup_instruments(); + + stub_alloc_always_fails= true; + thread_2= create_thread(&dummy_thread_class, NULL, 0); + ok(thread_2 == NULL, "oom (create thread)"); + + PSI_thread *thread; + + /* Per thread wait. */ + memset(¶m, 0, sizeof(param)); + param.m_mutex_class_sizing= 50; + param.m_rwlock_class_sizing= 50; + param.m_cond_class_sizing= 50; + param.m_file_class_sizing= 50; + param.m_socket_class_sizing= 0; + psi= initialize_performance_schema_helper(¶m); + stub_alloc_fails_after_count= 2; + thread= psi->new_thread(thread_key_1, NULL, 0); + ok(thread == NULL, "oom (per thread wait)"); + + cleanup_sync_class(); + cleanup_thread_class(); + cleanup_file_class(); + cleanup_instruments(); + + /* Thread waits history sizing. */ + memset(¶m, 0, sizeof(param)); + param.m_enabled= true; + param.m_events_waits_history_sizing= 10; + psi= initialize_performance_schema_helper(¶m); + stub_alloc_fails_after_count= 3; + thread= psi->new_thread(thread_key_1, NULL, 0); + ok(thread == NULL, "oom (thread waits history sizing)"); + + cleanup_thread_class(); + cleanup_instruments(); + + /* Per thread stages. */ + memset(¶m, 0, sizeof(param)); + param.m_stage_class_sizing= 50; + psi= initialize_performance_schema_helper(¶m); + stub_alloc_fails_after_count= 3; + thread= psi->new_thread(thread_key_1, NULL, 0); + ok(thread == NULL, "oom (per thread stages)"); + + cleanup_stage_class(); + cleanup_thread_class(); + cleanup_instruments(); + cleanup_stage_class(); + + /* Thread stages history sizing. */ + memset(¶m, 0, sizeof(param)); + param.m_events_stages_history_sizing= 10; + psi= initialize_performance_schema_helper(¶m); + stub_alloc_fails_after_count= 3; + thread= psi->new_thread(thread_key_1, NULL, 0); + ok(thread == NULL, "oom (thread stages history sizing)"); + + cleanup_instruments(); + cleanup_thread_class(); + + /* Per thread statements. */ + memset(¶m, 0, sizeof(param)); + param.m_stage_class_sizing= 50; + psi= initialize_performance_schema_helper(¶m); + init_statement_class(param.m_statement_class_sizing); + stub_alloc_fails_after_count= 3; + thread= psi->new_thread(thread_key_1, NULL, 0); + ok(thread == NULL, "oom (per thread statements)"); + + cleanup_stage_class(); + cleanup_statement_class(); + cleanup_thread_class(); + cleanup_instruments(); + + /* Thread statements history sizing. */ + memset(¶m, 0, sizeof(param)); + param.m_events_statements_history_sizing= 10; + psi= initialize_performance_schema_helper(¶m); + stub_alloc_fails_after_count= 3; + thread= psi->new_thread(thread_key_1, NULL, 0); + ok(thread == NULL, "oom (thread statements history sizing)"); + + cleanup_thread_class(); + cleanup_instruments(); + + /* Per thread transactions. */ + memset(¶m, 0, sizeof(param)); + psi= initialize_performance_schema_helper(¶m); + transaction_class_max= 1; // set by register_global_classes(); + stub_alloc_fails_after_count= 3; + thread= psi->new_thread(thread_key_1, NULL, 0); + ok(thread == NULL, "oom (per thread transactions)"); + transaction_class_max= 0; + + cleanup_thread_class(); + cleanup_instruments(); + + /* Thread transactions history sizing. */ + memset(¶m, 0, sizeof(param)); + param.m_events_transactions_history_sizing= 10; + psi= initialize_performance_schema_helper(¶m); + stub_alloc_fails_after_count= 3; + thread= psi->new_thread(thread_key_1, NULL, 0); + ok(thread == NULL, "oom (thread transactions history sizing)"); + + cleanup_thread_class(); + cleanup_instruments(); + + /* Global stages. */ + memset(¶m, 0, sizeof(param)); + param.m_enabled= true; + param.m_mutex_class_sizing= 10; + param.m_stage_class_sizing= 20; + + stub_alloc_fails_after_count= 2; + init_event_name_sizing(¶m); + rc= init_stage_class(param.m_stage_class_sizing); + ok(rc == 0, "init stage class"); + rc= init_instruments(& param); + ok(rc == 1, "oom (global stages)"); + + cleanup_stage_class(); + cleanup_instruments(); + + /* Global statements. */ + memset(¶m, 0, sizeof(param)); + param.m_enabled= true; + param.m_mutex_class_sizing= 10; + param.m_statement_class_sizing= 20; + + stub_alloc_fails_after_count= 2; + init_event_name_sizing(¶m); + rc= init_statement_class(param.m_statement_class_sizing); + ok(rc == 0, "init statement class"); + rc= init_instruments(¶m); + ok(rc == 1, "oom (global statements)"); + + cleanup_statement_class(); + cleanup_instruments(); + + /* Global memory. */ + memset(¶m, 0, sizeof(param)); + param.m_enabled= true; + param.m_mutex_class_sizing= 10; + param.m_memory_class_sizing= 20; + + stub_alloc_fails_after_count= 2; + init_event_name_sizing(¶m); + rc= init_memory_class(param.m_memory_class_sizing); + ok(rc == 0, "init memory class"); + rc= init_instruments(& param); + ok(rc == 1, "oom (global memory)"); + + cleanup_memory_class(); + cleanup_instruments(); +} + +void do_all_tests() +{ + test_oom(); +} + +int main(int argc, char **argv) +{ + plan(32); + MY_INIT(argv[0]); + do_all_tests(); + my_end(0); + return exit_status(); +} + diff --git a/storage/perfschema/unittest/pfs_instr-t.cc b/storage/perfschema/unittest/pfs_instr-t.cc new file mode 100644 index 00000000..9667d7ff --- /dev/null +++ b/storage/perfschema/unittest/pfs_instr-t.cc @@ -0,0 +1,488 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_thread.h> +#include <pfs_instr.h> +#include <pfs_stat.h> +#include <pfs_global.h> +#include <pfs_instr_class.h> +#include <pfs_buffer_container.h> +#include <tap.h> + +#include "stub_global_status_var.h" + +#include <memory.h> + +PFS_global_param param; + +void test_no_instruments() +{ + int rc; + + memset(& param, 0xFF, sizeof(param)); + param.m_enabled= true; + param.m_mutex_class_sizing= 0; + param.m_rwlock_class_sizing= 0; + param.m_cond_class_sizing= 0; + param.m_thread_class_sizing= 0; + param.m_table_share_sizing= 0; + param.m_file_class_sizing= 0; + param.m_socket_class_sizing= 0; + param.m_mutex_sizing= 0; + param.m_rwlock_sizing= 0; + param.m_cond_sizing= 0; + param.m_thread_sizing= 0; + param.m_table_sizing= 0; + param.m_file_sizing= 0; + param.m_file_handle_sizing= 0; + param.m_socket_sizing= 0; + param.m_events_waits_history_sizing= 0; + param.m_events_waits_history_long_sizing= 0; + param.m_setup_actor_sizing= 0; + param.m_setup_object_sizing= 0; + param.m_host_sizing= 0; + param.m_user_sizing= 0; + param.m_account_sizing= 0; + param.m_stage_class_sizing= 0; + param.m_events_stages_history_sizing= 0; + param.m_events_stages_history_long_sizing= 0; + param.m_statement_class_sizing= 0; + param.m_events_statements_history_sizing= 0; + param.m_events_statements_history_long_sizing= 0; + param.m_events_transactions_history_sizing= 0; + param.m_events_transactions_history_long_sizing= 0; + param.m_digest_sizing= 0; + param.m_session_connect_attrs_sizing= 0; + param.m_program_sizing= 0; + param.m_prepared_stmt_sizing= 0; + param.m_statement_stack_sizing= 0; + param.m_memory_class_sizing= 0; + param.m_metadata_lock_sizing= 0; + + init_event_name_sizing(& param); + rc= init_instruments(& param); + ok(rc == 0, "zero init"); + + cleanup_instruments(); +} + +void test_no_instances() +{ + int rc; + PFS_mutex_class dummy_mutex_class; + PFS_rwlock_class dummy_rwlock_class; + PFS_cond_class dummy_cond_class; + PFS_thread_class dummy_thread_class; + PFS_file_class dummy_file_class; + PFS_table_share dummy_table_share; + PFS_socket_class dummy_socket_class; + PFS_mutex *mutex; + PFS_rwlock *rwlock; + PFS_cond *cond; + PFS_thread *thread; + PFS_file *file; + PFS_socket *socket; + PFS_table *table; + + dummy_mutex_class.m_event_name_index = 0; + dummy_mutex_class.m_flags = 0; + dummy_mutex_class.m_enabled = true; + dummy_mutex_class.m_volatility = PSI_VOLATILITY_UNKNOWN; + dummy_rwlock_class.m_event_name_index = 1; + dummy_rwlock_class.m_flags = 0; + dummy_rwlock_class.m_enabled = true; + dummy_rwlock_class.m_volatility = PSI_VOLATILITY_UNKNOWN; + dummy_cond_class.m_event_name_index = 2; + dummy_cond_class.m_flags = 0; + dummy_cond_class.m_enabled = true; + dummy_cond_class.m_volatility = PSI_VOLATILITY_UNKNOWN; + dummy_file_class.m_event_name_index = 3; + dummy_file_class.m_flags = 0; + dummy_file_class.m_enabled = true; + dummy_file_class.m_volatility = PSI_VOLATILITY_UNKNOWN; + dummy_socket_class.m_event_name_index = 4; + dummy_socket_class.m_flags = 0; + dummy_socket_class.m_enabled = true; + dummy_socket_class.m_volatility = PSI_VOLATILITY_UNKNOWN; + + memset(& param, 0xFF, sizeof(param)); + param.m_enabled= true; + param.m_mutex_class_sizing= 1; + param.m_rwlock_class_sizing= 1; + param.m_cond_class_sizing= 1; + param.m_thread_class_sizing= 1; + param.m_table_share_sizing= 1; + param.m_file_class_sizing= 1; + param.m_socket_class_sizing= 0; + param.m_mutex_sizing= 0; + param.m_rwlock_sizing= 0; + param.m_cond_sizing= 0; + param.m_thread_sizing= 0; + param.m_table_sizing= 0; + param.m_file_sizing= 0; + param.m_file_handle_sizing= 0; + param.m_socket_sizing= 0; + param.m_events_waits_history_sizing= 0; + param.m_events_waits_history_long_sizing= 0; + param.m_setup_actor_sizing= 0; + param.m_setup_object_sizing= 0; + param.m_host_sizing= 0; + param.m_user_sizing= 0; + param.m_account_sizing= 0; + param.m_stage_class_sizing= 0; + param.m_events_stages_history_sizing= 0; + param.m_events_stages_history_long_sizing= 0; + param.m_statement_class_sizing= 0; + param.m_events_statements_history_sizing= 0; + param.m_events_statements_history_long_sizing= 0; + param.m_events_transactions_history_sizing= 0; + param.m_events_transactions_history_long_sizing= 0; + param.m_digest_sizing= 0; + param.m_session_connect_attrs_sizing= 0; + param.m_program_sizing= 0; + param.m_prepared_stmt_sizing= 0; + param.m_statement_stack_sizing= 0; + param.m_memory_class_sizing= 1; + param.m_metadata_lock_sizing= 0; + + init_event_name_sizing(& param); + rc= init_instruments(& param); + ok(rc == 0, "no instances init"); + + mutex= create_mutex(& dummy_mutex_class, NULL); + ok(mutex == NULL, "no mutex"); + ok(global_mutex_container.get_lost_counter() == 1, "lost 1"); + mutex= create_mutex(& dummy_mutex_class, NULL); + ok(mutex == NULL, "no mutex"); + ok(global_mutex_container.get_lost_counter() == 2, "lost 2"); + + rwlock= create_rwlock(& dummy_rwlock_class, NULL); + ok(rwlock == NULL, "no rwlock"); + ok(global_rwlock_container.m_lost == 1, "lost 1"); + rwlock= create_rwlock(& dummy_rwlock_class, NULL); + ok(rwlock == NULL, "no rwlock"); + ok(global_rwlock_container.m_lost == 2, "lost 2"); + + cond= create_cond(& dummy_cond_class, NULL); + ok(cond == NULL, "no cond"); + ok(global_cond_container.m_lost == 1, "lost 1"); + cond= create_cond(& dummy_cond_class, NULL); + ok(cond == NULL, "no cond"); + ok(global_cond_container.m_lost == 2, "lost 2"); + + thread= create_thread(& dummy_thread_class, NULL, 0); + ok(thread == NULL, "no thread"); + ok(global_thread_container.m_lost == 1, "lost 1"); + thread= create_thread(& dummy_thread_class, NULL, 0); + ok(thread == NULL, "no thread"); + ok(global_thread_container.m_lost == 2, "lost 2"); + + PFS_thread fake_thread; + fake_thread.m_filename_hash_pins= NULL; + + file= find_or_create_file(& fake_thread, & dummy_file_class, "dummy", 5, true); + ok(file == NULL, "no file"); + ok(global_file_container.m_lost == 1, "lost 1"); + file= find_or_create_file(& fake_thread, & dummy_file_class, "dummy", 5, true); + ok(file == NULL, "no file"); + ok(global_file_container.m_lost == 2, "lost 2"); + + init_file_hash(& param); + + file= find_or_create_file(& fake_thread, & dummy_file_class, "dummy", 5, true); + ok(file == NULL, "no file"); + ok(global_file_container.m_lost == 3, "lost 3"); + file= find_or_create_file(& fake_thread, & dummy_file_class, "dummy", 5, true); + ok(file == NULL, "no file"); + ok(global_file_container.m_lost == 4, "lost 4"); + + char long_file_name[10000]; + int size= sizeof(long_file_name); + memset(long_file_name, 'X', size); + + file= find_or_create_file(& fake_thread, & dummy_file_class, long_file_name, size, true); + ok(file == NULL, "no file"); + ok(global_file_container.m_lost == 5, "lost 5"); + + table= create_table(& dummy_table_share, & fake_thread, NULL); + ok(table == NULL, "no table"); + ok(global_table_container.m_lost == 1, "lost 1"); + table= create_table(& dummy_table_share, & fake_thread, NULL); + ok(table == NULL, "no table"); + ok(global_table_container.m_lost == 2, "lost 2"); + + socket= create_socket(& dummy_socket_class, NULL, NULL, 0); + ok(socket == NULL, "no socket"); + ok(global_socket_container.m_lost == 1, "lost 1"); + socket= create_socket(& dummy_socket_class, NULL, NULL, 0); + ok(socket == NULL, "no socket"); + ok(global_socket_container.m_lost == 2, "lost 2"); + + /* No result to test, just make sure it does not crash */ + reset_events_waits_by_instance(); + reset_events_waits_by_thread(); + + cleanup_file_hash(); + cleanup_instruments(); +} + +void test_with_instances() +{ + int rc; + PFS_mutex_class dummy_mutex_class; + PFS_rwlock_class dummy_rwlock_class; + PFS_cond_class dummy_cond_class; + PFS_thread_class dummy_thread_class; + PFS_file_class dummy_file_class; + PFS_socket_class dummy_socket_class; + PFS_table_share dummy_table_share; + PFS_mutex *mutex_1; + PFS_mutex *mutex_2; + PFS_rwlock *rwlock_1; + PFS_rwlock *rwlock_2; + PFS_cond *cond_1; + PFS_cond *cond_2; + PFS_thread *thread_1; + PFS_thread *thread_2; + PFS_file *file_1; + PFS_file *file_2; + PFS_socket *socket_1; + PFS_socket *socket_2; + PFS_table *table_1; + PFS_table *table_2; + + memset(& param, 0xFF, sizeof(param)); + param.m_enabled= true; + param.m_mutex_class_sizing= 1; + param.m_rwlock_class_sizing= 1; + param.m_cond_class_sizing= 1; + param.m_thread_class_sizing= 1; + param.m_table_share_sizing= 1; + param.m_file_class_sizing= 1; + param.m_socket_class_sizing= 1; + param.m_mutex_sizing= 2; + param.m_rwlock_sizing= 2; + param.m_cond_sizing= 2; + param.m_thread_sizing= 2; + param.m_table_sizing= 2; + param.m_file_sizing= 2; + param.m_file_handle_sizing= 100; + param.m_socket_sizing= 2; + param.m_events_waits_history_sizing= 10; + param.m_events_waits_history_long_sizing= 10000; + param.m_setup_actor_sizing= 0; + param.m_setup_object_sizing= 0; + param.m_host_sizing= 0; + param.m_user_sizing= 0; + param.m_account_sizing= 0; + param.m_stage_class_sizing= 0; + param.m_events_stages_history_sizing= 0; + param.m_events_stages_history_long_sizing= 0; + param.m_statement_class_sizing= 0; + param.m_events_statements_history_sizing= 0; + param.m_events_statements_history_long_sizing= 0; + param.m_events_transactions_history_sizing= 0; + param.m_events_transactions_history_long_sizing= 0; + param.m_digest_sizing= 0; + param.m_session_connect_attrs_sizing= 0; + param.m_program_sizing= 0; + param.m_prepared_stmt_sizing= 0; + param.m_statement_stack_sizing= 0; + param.m_memory_class_sizing= 1; + param.m_metadata_lock_sizing= 0; + + init_event_name_sizing(& param); + rc= init_instruments(& param); + ok(rc == 0, "instances init"); + + dummy_mutex_class.m_event_name_index= 0; + dummy_mutex_class.m_flags= 0; + dummy_mutex_class.m_enabled= true; + dummy_mutex_class.m_volatility = PSI_VOLATILITY_UNKNOWN; + dummy_rwlock_class.m_event_name_index= 1; + dummy_rwlock_class.m_flags= 0; + dummy_rwlock_class.m_enabled= true; + dummy_rwlock_class.m_volatility = PSI_VOLATILITY_UNKNOWN; + dummy_cond_class.m_event_name_index= 2; + dummy_cond_class.m_flags= 0; + dummy_cond_class.m_enabled= true; + dummy_cond_class.m_volatility = PSI_VOLATILITY_UNKNOWN; + dummy_file_class.m_event_name_index= 3; + dummy_file_class.m_flags= 0; + dummy_file_class.m_enabled= true; + dummy_file_class.m_volatility = PSI_VOLATILITY_UNKNOWN; + dummy_socket_class.m_event_name_index= 4; + dummy_socket_class.m_flags= 0; + dummy_socket_class.m_enabled= true; + dummy_socket_class.m_volatility = PSI_VOLATILITY_UNKNOWN; + + dummy_table_share.m_enabled= true; + dummy_table_share.m_timed= true; + + mutex_1= create_mutex(& dummy_mutex_class, NULL); + ok(mutex_1 != NULL, "mutex"); + ok(global_mutex_container.get_lost_counter() == 0, "not lost"); + mutex_2= create_mutex(& dummy_mutex_class, NULL); + ok(mutex_2 != NULL, "mutex"); + ok(global_mutex_container.get_lost_counter() == 0, "not lost"); + mutex_2= create_mutex(& dummy_mutex_class, NULL); + ok(mutex_2 == NULL, "no mutex"); + ok(global_mutex_container.get_lost_counter() == 1, "lost 1"); + destroy_mutex(mutex_1); + mutex_2= create_mutex(& dummy_mutex_class, NULL); + ok(mutex_2 != NULL, "mutex"); + ok(global_mutex_container.get_lost_counter() == 1, "no new loss"); + + rwlock_1= create_rwlock(& dummy_rwlock_class, NULL); + ok(rwlock_1 != NULL, "rwlock"); + ok(global_rwlock_container.m_lost == 0, "not lost"); + rwlock_2= create_rwlock(& dummy_rwlock_class, NULL); + ok(rwlock_2 != NULL, "rwlock"); + ok(global_rwlock_container.m_lost == 0, "not lost"); + rwlock_2= create_rwlock(& dummy_rwlock_class, NULL); + ok(rwlock_2 == NULL, "no rwlock"); + ok(global_rwlock_container.m_lost == 1, "lost 1"); + destroy_rwlock(rwlock_1); + rwlock_2= create_rwlock(& dummy_rwlock_class, NULL); + ok(rwlock_2 != NULL, "rwlock"); + ok(global_rwlock_container.m_lost == 1, "no new loss"); + + cond_1= create_cond(& dummy_cond_class, NULL); + ok(cond_1 != NULL, "cond"); + ok(global_cond_container.m_lost == 0, "not lost"); + cond_2= create_cond(& dummy_cond_class, NULL); + ok(cond_2 != NULL, "cond"); + ok(global_cond_container.m_lost == 0, "not lost"); + cond_2= create_cond(& dummy_cond_class, NULL); + ok(cond_2 == NULL, "no cond"); + ok(global_cond_container.m_lost == 1, "lost 1"); + destroy_cond(cond_1); + cond_2= create_cond(& dummy_cond_class, NULL); + ok(cond_2 != NULL, "cond"); + ok(global_cond_container.m_lost == 1, "no new loss"); + + thread_1= create_thread(& dummy_thread_class, NULL, 0); + ok(thread_1 != NULL, "thread"); + ok(global_thread_container.m_lost == 0, "not lost"); + thread_2= create_thread(& dummy_thread_class, NULL, 0); + ok(thread_2 != NULL, "thread"); + ok(global_thread_container.m_lost == 0, "not lost"); + thread_2= create_thread(& dummy_thread_class, NULL, 0); + ok(thread_2 == NULL, "no thread"); + ok(global_thread_container.m_lost == 1, "lost 1"); + destroy_thread(thread_1); + thread_2= create_thread(& dummy_thread_class, NULL, 0); + ok(thread_2 != NULL, "thread"); + ok(global_thread_container.m_lost == 1, "no new loss"); + + PFS_thread fake_thread; + fake_thread.m_filename_hash_pins= NULL; + + file_1= find_or_create_file(& fake_thread, & dummy_file_class, "dummy", 5, true); + ok(file_1 == NULL, "no file"); + ok(global_file_container.m_lost == 1, "lost 1"); + file_1= find_or_create_file(& fake_thread, & dummy_file_class, "dummy", 5, true); + ok(file_1 == NULL, "no file"); + ok(global_file_container.m_lost == 2, "lost 2"); + + init_file_hash(& param); + global_file_container.m_lost= 0; + + file_1= find_or_create_file(& fake_thread, & dummy_file_class, "dummy_A", 7, true); + ok(file_1 != NULL, "file"); + ok(file_1->m_file_stat.m_open_count == 1, "open count 1"); + ok(global_file_container.m_lost == 0, "not lost"); + file_2= find_or_create_file(& fake_thread, & dummy_file_class, "dummy_A", 7, true); + ok(file_1 == file_2, "same file"); + ok(file_1->m_file_stat.m_open_count == 2, "open count 2"); + ok(global_file_container.m_lost == 0, "not lost"); + release_file(file_2); + ok(file_1->m_file_stat.m_open_count == 1, "open count 1"); + file_2= find_or_create_file(& fake_thread, & dummy_file_class, "dummy_B", 7, true); + ok(file_2 != NULL, "file"); + ok(global_file_container.m_lost == 0, "not lost"); + file_2= find_or_create_file(& fake_thread, & dummy_file_class, "dummy_C", 7, true); + ok(file_2 == NULL, "no file"); + ok(global_file_container.m_lost == 1, "lost"); + release_file(file_1); + /* the file still exists, not destroyed */ + ok(file_1->m_file_stat.m_open_count == 0, "open count 0"); + file_2= find_or_create_file(& fake_thread, & dummy_file_class, "dummy_D", 7, true); + ok(file_2 == NULL, "no file"); + ok(global_file_container.m_lost == 2, "lost"); + + socket_1= create_socket(& dummy_socket_class, NULL, NULL, 0); + ok(socket_1 != NULL, "socket"); + ok(global_socket_container.m_lost == 0, "not lost"); + socket_2= create_socket(& dummy_socket_class, NULL, NULL, 0); + ok(socket_2 != NULL, "socket"); + ok(global_socket_container.m_lost == 0, "not lost"); + socket_2= create_socket(& dummy_socket_class, NULL, NULL, 0); + ok(socket_2 == NULL, "no socket"); + ok(global_socket_container.m_lost == 1, "lost 1"); + destroy_socket(socket_1); + socket_2= create_socket(& dummy_socket_class, NULL, NULL, 0); + ok(socket_2 != NULL, "socket"); + ok(global_socket_container.m_lost == 1, "no new loss"); + + table_1= create_table(& dummy_table_share, & fake_thread, NULL); + ok(table_1 != NULL, "table"); + ok(global_table_container.m_lost == 0, "not lost"); + table_2= create_table(& dummy_table_share, & fake_thread, NULL); + ok(table_2 != NULL, "table"); + ok(global_table_container.m_lost == 0, "not lost"); + table_2= create_table(& dummy_table_share, & fake_thread, NULL); + ok(table_2 == NULL, "no table"); + ok(global_table_container.m_lost == 1, "lost 1"); + destroy_table(table_1); + table_2= create_table(& dummy_table_share, & fake_thread, NULL); + ok(table_2 != NULL, "table"); + ok(global_table_container.m_lost == 1, "no new loss"); + + //TODO: test that cleanup works + reset_events_waits_by_instance(); + reset_events_waits_by_thread(); + + cleanup_file_hash(); + cleanup_instruments(); +} + +void do_all_tests() +{ + flag_global_instrumentation= true; + flag_thread_instrumentation= true; + + test_no_instruments(); + test_no_instances(); + test_with_instances(); +} + +int main(int argc, char **argv) +{ + plan(103); + MY_INIT(argv[0]); + do_all_tests(); + my_end(0); + return (exit_status()); +} + diff --git a/storage/perfschema/unittest/pfs_instr_class-oom-t.cc b/storage/perfschema/unittest/pfs_instr_class-oom-t.cc new file mode 100644 index 00000000..b7c17d06 --- /dev/null +++ b/storage/perfschema/unittest/pfs_instr_class-oom-t.cc @@ -0,0 +1,114 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_thread.h> +#include <pfs_instr_class.h> +#include <pfs_instr.h> +#include <pfs_global.h> +#include <tap.h> +#include <sql_class.h> +#include <pfs_buffer_container.h> + +#include "stub_pfs_global.h" +#include "stub_global_status_var.h" + +void test_oom() +{ + int rc; + PFS_global_param param; + TABLE_SHARE table_share; + PFS_thread pfs_thread; + PFS_table_share *pfs_table_share; + + rc= init_sync_class(1000, 0, 0); + ok(rc == 1, "oom (mutex)"); + rc= init_sync_class(0, 1000, 0); + ok(rc == 1, "oom (rwlock)"); + rc= init_sync_class(0, 0, 1000); + ok(rc == 1, "oom (cond)"); + rc= init_thread_class(1000); + ok(rc == 1, "oom (thread)"); + rc= init_file_class(1000); + ok(rc == 1, "oom (file)"); + rc= init_socket_class(1000); + ok(rc == 1, "oom (socket)"); + rc= init_stage_class(1000); + ok(rc == 1, "oom (stage)"); + rc= init_statement_class(1000); + ok(rc == 1, "oom (statement)"); + rc= init_memory_class(1000); + ok(rc == 1, "oom (memory)"); + + cleanup_sync_class(); + cleanup_thread_class(); + cleanup_file_class(); + cleanup_table_share(); + cleanup_socket_class(); + cleanup_stage_class(); + cleanup_statement_class(); + cleanup_memory_class(); + + /* Table share classes. */ + memset(¶m, 0, sizeof(param)); + param.m_enabled= true; + param.m_table_share_sizing= 100; + param.m_setup_object_sizing= 100; + + pfs_thread.m_table_share_hash_pins= NULL; + pfs_thread.m_setup_object_hash_pins= NULL; + + char db_name[]= "schema 1"; + char table_name[]= "table 1"; + table_share.db.str= db_name; + table_share.db.length= strlen(db_name); + table_share.table_name.str= table_name; + table_share.table_name.length= strlen(table_name); + + init_table_share(param.m_table_share_sizing); + init_table_share_hash(¶m); + init_setup_object_hash(¶m); + + stub_alloc_always_fails= false; + pfs_table_share= find_or_create_table_share(&pfs_thread, false, &table_share); + ok(pfs_table_share == NULL, "oom (pfs table share)"); + ok(global_table_share_container.m_lost == 1, "oom (table share)"); + + cleanup_table_share(); + cleanup_table_share_hash(); + cleanup_setup_object_hash(); +} + +void do_all_tests() +{ + test_oom(); +} + +int main(int argc, char **argv) +{ + plan(11); + MY_INIT(argv[0]); + do_all_tests(); + my_end(0); + return (exit_status()); +} + diff --git a/storage/perfschema/unittest/pfs_instr_class-t.cc b/storage/perfschema/unittest/pfs_instr_class-t.cc new file mode 100644 index 00000000..7651898d --- /dev/null +++ b/storage/perfschema/unittest/pfs_instr_class-t.cc @@ -0,0 +1,750 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_thread.h> +#include <string.h> // strncpy +#include <pfs_instr_class.h> +#include <pfs_instr.h> +#include <pfs_global.h> +#include <tap.h> + +#include "stub_global_status_var.h" + +void test_no_registration() +{ + int rc; + PFS_sync_key key; + PFS_thread_key thread_key; + PFS_file_key file_key; + PFS_socket_key socket_key; + PFS_memory_key memory_key; + PFS_mutex_class *mutex; + PFS_rwlock_class *rwlock; + PFS_cond_class *cond; + PFS_thread_class *thread; + PFS_file_class *file; + PFS_socket_class *socket; + PFS_memory_class *memory; + /* PFS_table_share *table; */ + + rc= init_sync_class(0, 0, 0); + ok(rc == 0, "zero init (sync)"); + rc= init_thread_class(0); + ok(rc == 0, "zero init (thread)"); + rc= init_file_class(0); + ok(rc == 0, "zero init (file)"); + rc= init_socket_class(0); + ok(rc == 0, "zero init (socket)"); + rc= init_table_share(0); + ok(rc == 0, "zero init (table)"); + rc= init_memory_class(0); + ok(rc == 0, "zero init (memory)"); + + key= register_mutex_class("FOO", 3, 0); + ok(key == 0, "no mutex registered"); + key= register_mutex_class("BAR", 3, 0); + ok(key == 0, "no mutex registered"); + key= register_mutex_class("FOO", 3, 0); + ok(key == 0, "no mutex registered"); + + key= register_rwlock_class("FOO", 3, 0); + ok(key == 0, "no rwlock registered"); + key= register_rwlock_class("BAR", 3, 0); + ok(key == 0, "no rwlock registered"); + key= register_rwlock_class("FOO", 3, 0); + ok(key == 0, "no rwlock registered"); + + key= register_cond_class("FOO", 3, 0); + ok(key == 0, "no cond registered"); + key= register_cond_class("BAR", 3, 0); + ok(key == 0, "no cond registered"); + key= register_cond_class("FOO", 3, 0); + ok(key == 0, "no cond registered"); + + thread_key= register_thread_class("FOO", 3, 0); + ok(thread_key == 0, "no thread registered"); + thread_key= register_thread_class("BAR", 3, 0); + ok(thread_key == 0, "no thread registered"); + thread_key= register_thread_class("FOO", 3, 0); + ok(thread_key == 0, "no thread registered"); + + file_key= register_file_class("FOO", 3, 0); + ok(file_key == 0, "no file registered"); + file_key= register_file_class("BAR", 3, 0); + ok(file_key == 0, "no file registered"); + file_key= register_file_class("FOO", 3, 0); + ok(file_key == 0, "no file registered"); + + socket_key= register_socket_class("FOO", 3, 0); + ok(socket_key == 0, "no socket registered"); + socket_key= register_socket_class("BAR", 3, 0); + ok(socket_key == 0, "no socket registered"); + socket_key= register_socket_class("FOO", 3, 0); + ok(socket_key == 0, "no socket registered"); + + memory_key= register_memory_class("FOO", 3, 0); + ok(memory_key == 0, "no memory registered"); + memory_key= register_memory_class("BAR", 3, 0); + ok(memory_key == 0, "no memory registered"); + memory_key= register_memory_class("FOO", 3, 0); + ok(memory_key == 0, "no memory registered"); + +#ifdef LATER + PFS_thread fake_thread; + fake_thread.m_table_share_hash_pins= NULL; + + table= find_or_create_table_share(& fake_thread, false, "foo_db", 6, "foo_table", 9); + ok(table == NULL, "not created"); + table= find_or_create_table_share(& fake_thread, false, "bar_db", 6, "bar_table", 9); + ok(table == NULL, "not created"); + table= find_or_create_table_share(& fake_thread, false, "foo_db", 6, "foo_table", 9); + ok(table == NULL, "not created"); +#endif + + mutex= find_mutex_class(0); + ok(mutex == NULL, "no mutex key 0"); + mutex= find_mutex_class(1); + ok(mutex == NULL, "no mutex key 1"); + mutex= find_mutex_class(9999); + ok(mutex == NULL, "no mutex key 9999"); + + rwlock= find_rwlock_class(0); + ok(rwlock == NULL, "no rwlock key 0"); + rwlock= find_rwlock_class(1); + ok(rwlock == NULL, "no rwlock key 1"); + rwlock= find_rwlock_class(9999); + ok(rwlock == NULL, "no rwlock key 9999"); + + cond= find_cond_class(0); + ok(cond == NULL, "no cond key 0"); + cond= find_cond_class(1); + ok(cond == NULL, "no cond key 1"); + cond= find_cond_class(9999); + ok(cond == NULL, "no cond key 9999"); + + thread= find_thread_class(0); + ok(thread == NULL, "no thread key 0"); + thread= find_thread_class(1); + ok(thread == NULL, "no thread key 1"); + thread= find_thread_class(9999); + ok(thread == NULL, "no thread key 9999"); + + file= find_file_class(0); + ok(file == NULL, "no file key 0"); + file= find_file_class(1); + ok(file == NULL, "no file key 1"); + file= find_file_class(9999); + ok(file == NULL, "no file key 9999"); + + socket= find_socket_class(0); + ok(socket == NULL, "no socket key 0"); + socket= find_socket_class(1); + ok(socket == NULL, "no socket key 1"); + socket= find_socket_class(9999); + ok(socket == NULL, "no socket key 9999"); + + memory= find_memory_class(0); + ok(memory == NULL, "no memory key 0"); + memory= find_memory_class(1); + ok(memory == NULL, "no memory key 1"); + memory= find_memory_class(9999); + ok(memory == NULL, "no memory key 9999"); + + cleanup_sync_class(); + cleanup_thread_class(); + cleanup_file_class(); + cleanup_socket_class(); + cleanup_table_share(); + cleanup_memory_class(); +} + +void test_mutex_registration() +{ + int rc; + PFS_sync_key key; + PFS_mutex_class *mutex; + + rc= init_sync_class(5, 0, 0); + ok(rc == 0, "room for 5 mutex"); + + key= register_mutex_class("FOO", 3, 0); + ok(key == 1, "foo registered"); + key= register_mutex_class("BAR", 3, 0); + ok(key == 2, "bar registered"); + key= register_mutex_class("FOO", 3, 0); + ok(key == 1, "foo re registered"); + key= register_mutex_class("M-3", 3, 0); + ok(key == 3, "M-3 registered"); + key= register_mutex_class("M-4", 3, 0); + ok(key == 4, "M-4 registered"); + key= register_mutex_class("M-5", 3, 0); + ok(key == 5, "M-5 registered"); + ok(mutex_class_lost == 0, "lost nothing"); + key= register_mutex_class("M-6", 3, 0); + ok(key == 0, "M-6 not registered"); + ok(mutex_class_lost == 1, "lost 1 mutex"); + key= register_mutex_class("M-7", 3, 0); + ok(key == 0, "M-7 not registered"); + ok(mutex_class_lost == 2, "lost 2 mutex"); + key= register_mutex_class("M-3", 3, 0); + ok(key == 3, "M-3 re registered"); + ok(mutex_class_lost == 2, "lost 2 mutex"); + key= register_mutex_class("M-5", 3, 0); + ok(key == 5, "M-5 re registered"); + ok(mutex_class_lost == 2, "lost 2 mutex"); + + mutex= find_mutex_class(0); + ok(mutex == NULL, "no key 0"); + mutex= find_mutex_class(3); + ok(mutex != NULL, "found key 3"); + ok(strncmp(mutex->m_name, "M-3", 3) == 0, "key 3 is M-3"); + ok(mutex->m_name_length == 3, "name length 3"); + mutex= find_mutex_class(9999); + ok(mutex == NULL, "no key 9999"); + + cleanup_sync_class(); +} + +void test_rwlock_registration() +{ + int rc; + PFS_sync_key key; + PFS_rwlock_class *rwlock; + + rc= init_sync_class(0, 5, 0); + ok(rc == 0, "room for 5 rwlock"); + + key= register_rwlock_class("FOO", 3, 0); + ok(key == 1, "foo registered"); + key= register_rwlock_class("BAR", 3, 0); + ok(key == 2, "bar registered"); + key= register_rwlock_class("FOO", 3, 0); + ok(key == 1, "foo re registered"); + key= register_rwlock_class("RW-3", 4, 0); + ok(key == 3, "RW-3 registered"); + key= register_rwlock_class("RW-4", 4, 0); + ok(key == 4, "RW-4 registered"); + key= register_rwlock_class("RW-5", 4, 0); + ok(key == 5, "RW-5 registered"); + key= register_rwlock_class("RW-6", 4, 0); + ok(key == 0, "RW-6 not registered"); + key= register_rwlock_class("RW-7", 4, 0); + ok(key == 0, "RW-7 not registered"); + key= register_rwlock_class("RW-3", 4, 0); + ok(key == 3, "RW-3 re registered"); + key= register_rwlock_class("RW-5", 4, 0); + ok(key == 5, "RW-5 re registered"); + + rwlock= find_rwlock_class(0); + ok(rwlock == NULL, "no key 0"); + rwlock= find_rwlock_class(3); + ok(rwlock != NULL, "found key 3"); + ok(strncmp(rwlock->m_name, "RW-3", 4) == 0, "key 3 is RW-3"); + ok(rwlock->m_name_length == 4, "name length 4"); + rwlock= find_rwlock_class(9999); + ok(rwlock == NULL, "no key 9999"); + + cleanup_sync_class(); +} + +void test_cond_registration() +{ + int rc; + PFS_sync_key key; + PFS_cond_class *cond; + + rc= init_sync_class(0, 0, 5); + ok(rc == 0, "room for 5 cond"); + + key= register_cond_class("FOO", 3, 0); + ok(key == 1, "foo registered"); + key= register_cond_class("BAR", 3, 0); + ok(key == 2, "bar registered"); + key= register_cond_class("FOO", 3, 0); + ok(key == 1, "foo re registered"); + key= register_cond_class("C-3", 3, 0); + ok(key == 3, "C-3 registered"); + key= register_cond_class("C-4", 3, 0); + ok(key == 4, "C-4 registered"); + key= register_cond_class("C-5", 3, 0); + ok(key == 5, "C-5 registered"); + key= register_cond_class("C-6", 3, 0); + ok(key == 0, "C-6 not registered"); + key= register_cond_class("C-7", 3, 0); + ok(key == 0, "C-7 not registered"); + key= register_cond_class("C-3", 3, 0); + ok(key == 3, "C-3 re registered"); + key= register_cond_class("C-5", 3, 0); + ok(key == 5, "C-5 re registered"); + + cond= find_cond_class(0); + ok(cond == NULL, "no key 0"); + cond= find_cond_class(3); + ok(cond != NULL, "found key 3"); + ok(strncmp(cond->m_name, "C-3", 3) == 0, "key 3 is C-3"); + ok(cond->m_name_length == 3, "name length 3"); + cond= find_cond_class(9999); + ok(cond == NULL, "no key 9999"); + + cleanup_sync_class(); +} + +void test_thread_registration() +{ + int rc; + PFS_thread_key key; + PFS_thread_class *thread; + + rc= init_thread_class(5); + ok(rc == 0, "room for 5 thread"); + + key= register_thread_class("FOO", 3, 0); + ok(key == 1, "foo registered"); + key= register_thread_class("BAR", 3, 0); + ok(key == 2, "bar registered"); + key= register_thread_class("FOO", 3, 0); + ok(key == 1, "foo re registered"); + key= register_thread_class("Thread-3", 8, 0); + ok(key == 3, "Thread-3 registered"); + key= register_thread_class("Thread-4", 8, 0); + ok(key == 4, "Thread-4 registered"); + key= register_thread_class("Thread-5", 8, 0); + ok(key == 5, "Thread-5 registered"); + key= register_thread_class("Thread-6", 8, 0); + ok(key == 0, "Thread-6 not registered"); + key= register_thread_class("Thread-7", 8, 0); + ok(key == 0, "Thread-7 not registered"); + key= register_thread_class("Thread-3", 8, 0); + ok(key == 3, "Thread-3 re registered"); + key= register_thread_class("Thread-5", 8, 0); + ok(key == 5, "Thread-5 re registered"); + + thread= find_thread_class(0); + ok(thread == NULL, "no key 0"); + thread= find_thread_class(3); + ok(thread != NULL, "found key 3"); + ok(strncmp(thread->m_name, "Thread-3", 8) == 0, "key 3 is Thread-3"); + ok(thread->m_name_length == 8, "name length 8"); + thread= find_thread_class(9999); + ok(thread == NULL, "no key 9999"); + + cleanup_thread_class(); +} + +void test_file_registration() +{ + int rc; + PFS_file_key key; + PFS_file_class *file; + + rc= init_file_class(5); + ok(rc == 0, "room for 5 file"); + + key= register_file_class("FOO", 3, 0); + ok(key == 1, "foo registered"); + key= register_file_class("BAR", 3, 0); + ok(key == 2, "bar registered"); + key= register_file_class("FOO", 3, 0); + ok(key == 1, "foo re registered"); + key= register_file_class("File-3", 6, 0); + ok(key == 3, "File-3 registered"); + key= register_file_class("File-4", 6, 0); + ok(key == 4, "File-4 registered"); + key= register_file_class("File-5", 6, 0); + ok(key == 5, "File-5 registered"); + key= register_file_class("File-6", 6, 0); + ok(key == 0, "File-6 not registered"); + key= register_file_class("File-7", 6, 0); + ok(key == 0, "File-7 not registered"); + key= register_file_class("File-3", 6, 0); + ok(key == 3, "File-3 re registered"); + key= register_file_class("File-5", 6, 0); + ok(key == 5, "File-5 re registered"); + + file= find_file_class(0); + ok(file == NULL, "no key 0"); + file= find_file_class(3); + ok(file != NULL, "found key 3"); + ok(strncmp(file->m_name, "File-3", 6) == 0, "key 3 is File-3"); + ok(file->m_name_length == 6, "name length 6"); + file= find_file_class(9999); + ok(file == NULL, "no key 9999"); + + cleanup_file_class(); +} + +void test_socket_registration() +{ + int rc; + PFS_socket_key key; + PFS_socket_class *socket; + + rc= init_socket_class(5); + ok(rc == 0, "room for 5 socket"); + + key= register_socket_class("FOO", 3, 0); + ok(key == 1, "foo registered"); + key= register_socket_class("BAR", 3, 0); + ok(key == 2, "bar registered"); + key= register_socket_class("FOO", 3, 0); + ok(key == 1, "foo re registered"); + key= register_socket_class("Socket-3", 8, 0); + ok(key == 3, "Socket-3 registered"); + key= register_socket_class("Socket-4", 8, 0); + ok(key == 4, "Socket-4 registered"); + key= register_socket_class("Socket-5", 8, 0); + ok(key == 5, "Socket-5 registered"); + ok(socket_class_lost == 0, "lost nothing"); + key= register_socket_class("Socket-6", 8, 0); + ok(key == 0, "Socket-6 not registered"); + ok(socket_class_lost == 1, "lost 1 socket"); + key= register_socket_class("Socket-7", 8, 0); + ok(key == 0, "Socket-7 not registered"); + ok(socket_class_lost == 2, "lost 2 socket"); + key= register_socket_class("Socket-3", 8, 0); + ok(key == 3, "Socket-3 re registered"); + ok(socket_class_lost == 2, "lost 2 socket"); + key= register_socket_class("Socket-5", 8, 0); + ok(key == 5, "Socket-5 re registered"); + ok(socket_class_lost == 2, "lost 2 socket"); + + socket= find_socket_class(0); + ok(socket == NULL, "no key 0"); + socket= find_socket_class(3); + ok(socket != NULL, "found key 3"); + ok(strncmp(socket->m_name, "Socket-3", 8) == 0, "key 3 is Socket-3"); + ok(socket->m_name_length == 8, "name length 3"); + socket= find_socket_class(9999); + ok(socket == NULL, "no key 9999"); + + cleanup_socket_class(); +} + +void test_table_registration() +{ +#ifdef LATER + PFS_table_share *table_share; + PFS_table_share *table_share_2; + + PFS_thread fake_thread; + fake_thread.m_table_share_hash_pins= NULL; + + table_share_lost= 0; + table_share= find_or_create_table_share(& fake_thread, false, "db1", 3, "t1", 2); + ok(table_share == NULL, "not created"); + ok(table_share_lost == 1, "lost the table"); + + table_share_lost= 0; + init_table_share(5); + init_table_share_hash(); + + table_share= find_or_create_table_share(& fake_thread, false, "db1", 3, "t1", 2); + ok(table_share != NULL, "created db1.t1"); + ok(table_share_lost == 0, "not lost"); + + table_share_2= find_or_create_table_share(& fake_thread, false, "db1", 3, "t1", 2); + ok(table_share_2 != NULL, "found db1.t1"); + ok(table_share_lost == 0, "not lost"); + ok(table_share == table_share_2, "same table"); + + table_share_2= find_or_create_table_share(& fake_thread, false, "db1", 3, "t2", 2); + ok(table_share_2 != NULL, "created db1.t2"); + ok(table_share_lost == 0, "not lost"); + + table_share_2= find_or_create_table_share(& fake_thread, false, "db2", 3, "t1", 2); + ok(table_share_2 != NULL, "created db2.t1"); + ok(table_share_lost == 0, "not lost"); + + table_share_2= find_or_create_table_share(& fake_thread, false, "db2", 3, "t2", 2); + ok(table_share_2 != NULL, "created db2.t2"); + ok(table_share_lost == 0, "not lost"); + + table_share_2= find_or_create_table_share(& fake_thread, false, "db3", 3, "t3", 2); + ok(table_share_2 != NULL, "created db3.t3"); + ok(table_share_lost == 0, "not lost"); + + table_share_2= find_or_create_table_share(& fake_thread, false, "db4", 3, "t4", 2); + ok(table_share_2 == NULL, "lost db4.t4"); + ok(table_share_lost == 1, "lost"); + + table_share_lost= 0; + table_share_2= find_or_create_table_share(& fake_thread, false, "db1", 3, "t2", 2); + ok(table_share_2 != NULL, "found db1.t2"); + ok(table_share_lost == 0, "not lost"); + ok(strncmp(table_share_2->m_schema_name, "db1", 3) == 0 , "schema db1"); + ok(table_share_2->m_schema_name_length == 3, "length 3"); + ok(strncmp(table_share_2->m_table_name, "t2", 2) == 0 , "table t2"); + ok(table_share_2->m_table_name_length == 2, "length 2"); + + cleanup_table_share_hash(); + cleanup_table_share(); +#endif +} + +void test_memory_registration() +{ + int rc; + PFS_memory_key key; + PFS_memory_class *memory; + + rc= init_memory_class(5); + ok(rc == 0, "room for 5 memory"); + + key= register_memory_class("FOO", 3, 0); + ok(key == 1, "foo registered"); + key= register_memory_class("BAR", 3, 0); + ok(key == 2, "bar registered"); + key= register_memory_class("FOO", 3, 0); + ok(key == 1, "foo re registered"); + key= register_memory_class("Memory-3", 8, 0); + ok(key == 3, "Memory-3 registered"); + key= register_memory_class("Memory-4", 8, 0); + ok(key == 4, "Memory-4 registered"); + key= register_memory_class("Memory-5", 8, 0); + ok(key == 5, "Memory-5 registered"); + ok(memory_class_lost == 0, "lost nothing"); + key= register_memory_class("Memory-6", 8, 0); + ok(key == 0, "Memory-6 not registered"); + ok(memory_class_lost == 1, "lost 1 memory"); + key= register_memory_class("Memory-7", 8, 0); + ok(key == 0, "Memory-7 not registered"); + ok(memory_class_lost == 2, "lost 2 memory"); + key= register_memory_class("Memory-3", 8, 0); + ok(key == 3, "Memory-3 re registered"); + ok(memory_class_lost == 2, "lost 2 memory"); + key= register_memory_class("Memory-5", 8, 0); + ok(key == 5, "Memory-5 re registered"); + ok(memory_class_lost == 2, "lost 2 memory"); + + memory= find_memory_class(0); + ok(memory == NULL, "no key 0"); + memory= find_memory_class(3); + ok(memory != NULL, "found key 3"); + ok(strncmp(memory->m_name, "Memory-3", 8) == 0, "key 3 is Memory-3"); + ok(memory->m_name_length == 8, "name length 3"); + memory= find_memory_class(9999); + ok(memory == NULL, "no key 9999"); + + cleanup_memory_class(); +} + +#ifdef LATER +void set_wait_stat(PFS_instr_class *klass) +{ + PFS_single_stat *stat; + stat= & global_instr_class_waits_array[klass->m_event_name_index]; + + stat->m_count= 12; + stat->m_min= 5; + stat->m_max= 120; + stat->m_sum= 999; +} + +bool is_empty_stat(PFS_instr_class *klass) +{ + PFS_single_stat *stat; + stat= & global_instr_class_waits_array[klass->m_event_name_index]; + + if (stat->m_count != 0) + return false; + if (stat->m_min != (ulonglong) -1) + return false; + if (stat->m_max != 0) + return false; + if (stat->m_sum != 0) + return false; + return true; +} +#endif + +void test_instruments_reset() +{ + int rc; + PFS_sync_key key; + PFS_file_key file_key; + PFS_socket_key socket_key; + PFS_mutex_class *mutex_1; + PFS_mutex_class *mutex_2; + PFS_mutex_class *mutex_3; + PFS_rwlock_class *rwlock_1; + PFS_rwlock_class *rwlock_2; + PFS_rwlock_class *rwlock_3; + PFS_cond_class *cond_1; + PFS_cond_class *cond_2; + PFS_cond_class *cond_3; + PFS_file_class *file_1; + PFS_file_class *file_2; + PFS_file_class *file_3; + PFS_socket_class *socket_1; + PFS_socket_class *socket_2; + PFS_socket_class *socket_3; + + rc= init_sync_class(3, 3, 3); + ok(rc == 0, "init (sync)"); + rc= init_thread_class(3); + ok(rc == 0, "init (thread)"); + rc= init_file_class(3); + ok(rc == 0, "init (file)"); + rc= init_socket_class(3); + ok(rc == 0, "init (socket)"); + + key= register_mutex_class("M-1", 3, 0); + ok(key == 1, "mutex registered"); + key= register_mutex_class("M-2", 3, 0); + ok(key == 2, "mutex registered"); + key= register_mutex_class("M-3", 3, 0); + ok(key == 3, "mutex registered"); + + key= register_rwlock_class("RW-1", 4, 0); + ok(key == 1, "rwlock registered"); + key= register_rwlock_class("RW-2", 4, 0); + ok(key == 2, "rwlock registered"); + key= register_rwlock_class("RW-3", 4, 0); + ok(key == 3, "rwlock registered"); + + key= register_cond_class("C-1", 3, 0); + ok(key == 1, "cond registered"); + key= register_cond_class("C-2", 3, 0); + ok(key == 2, "cond registered"); + key= register_cond_class("C-3", 3, 0); + ok(key == 3, "cond registered"); + + file_key= register_file_class("F-1", 3, 0); + ok(file_key == 1, "file registered"); + file_key= register_file_class("F-2", 3, 0); + ok(file_key == 2, "file registered"); + file_key= register_file_class("F-3", 3, 0); + ok(file_key == 3, "file registered"); + + socket_key= register_socket_class("S-1", 3, 0); + ok(socket_key == 1, "socket registered"); + socket_key= register_socket_class("S-2", 3, 0); + ok(socket_key == 2, "socket registered"); + socket_key= register_socket_class("S-3", 3, 0); + ok(socket_key == 3, "socket registered"); + + mutex_1= find_mutex_class(1); + ok(mutex_1 != NULL, "mutex key 1"); + mutex_2= find_mutex_class(2); + ok(mutex_2 != NULL, "mutex key 2"); + mutex_3= find_mutex_class(3); + ok(mutex_3 != NULL, "mutex key 3"); + + rwlock_1= find_rwlock_class(1); + ok(rwlock_1 != NULL, "rwlock key 1"); + rwlock_2= find_rwlock_class(2); + ok(rwlock_2 != NULL, "rwlock key 2"); + rwlock_3= find_rwlock_class(3); + ok(rwlock_3 != NULL, "rwlock key 3"); + + cond_1= find_cond_class(1); + ok(cond_1 != NULL, "cond key 1"); + cond_2= find_cond_class(2); + ok(cond_2 != NULL, "cond key 2"); + cond_3= find_cond_class(3); + ok(cond_3 != NULL, "cond key 3"); + + file_1= find_file_class(1); + ok(file_1 != NULL, "file key 1"); + file_2= find_file_class(2); + ok(file_2 != NULL, "file key 2"); + file_3= find_file_class(3); + ok(file_3 != NULL, "file key 3"); + + socket_1= find_socket_class(1); + ok(socket_1 != NULL, "socket key 1"); + socket_2= find_socket_class(2); + ok(socket_2 != NULL, "socket key 2"); + socket_3= find_socket_class(3); + ok(socket_3 != NULL, "socket key 3"); + +#ifdef LATER + set_wait_stat(mutex_1); + set_wait_stat(mutex_2); + set_wait_stat(mutex_3); + set_wait_stat(rwlock_1); + set_wait_stat(rwlock_2); + set_wait_stat(rwlock_3); + set_wait_stat(cond_1); + set_wait_stat(cond_2); + set_wait_stat(cond_3); + set_wait_stat(file_1); + set_wait_stat(file_2); + set_wait_stat(file_3); + + ok(! is_empty_stat(mutex_1), "mutex_1 stat is populated"); + ok(! is_empty_stat(mutex_2), "mutex_2 stat is populated"); + ok(! is_empty_stat(mutex_3), "mutex_3 stat is populated"); + ok(! is_empty_stat(rwlock_1), "rwlock_1 stat is populated"); + ok(! is_empty_stat(rwlock_2), "rwlock_2 stat is populated"); + ok(! is_empty_stat(rwlock_3), "rwlock_3 stat is populated"); + ok(! is_empty_stat(cond_1), "cond_1 stat is populated"); + ok(! is_empty_stat(cond_2), "cond_2 stat is populated"); + ok(! is_empty_stat(cond_3), "cond_3 stat is populated"); + ok(! is_empty_stat(file_1), "file_1 stat is populated"); + ok(! is_empty_stat(file_2), "file_2 stat is populated"); + ok(! is_empty_stat(file_3), "file_3 stat is populated"); + + reset_global_wait_stat(); + + ok(is_empty_stat(mutex_1), "mutex_1 stat is cleared"); + ok(is_empty_stat(mutex_2), "mutex_2 stat is cleared"); + ok(is_empty_stat(mutex_3), "mutex_3 stat is cleared"); + ok(is_empty_stat(rwlock_1), "rwlock_1 stat is cleared"); + ok(is_empty_stat(rwlock_2), "rwlock_2 stat is cleared"); + ok(is_empty_stat(rwlock_3), "rwlock_3 stat is cleared"); + ok(is_empty_stat(cond_1), "cond_1 stat is cleared"); + ok(is_empty_stat(cond_2), "cond_2 stat is cleared"); + ok(is_empty_stat(cond_3), "cond_3 stat is cleared"); + ok(is_empty_stat(file_1), "file_1 stat is cleared"); + ok(is_empty_stat(file_2), "file_2 stat is cleared"); + ok(is_empty_stat(file_3), "file_3 stat is cleared"); +#endif + + cleanup_sync_class(); + cleanup_file_class(); + cleanup_socket_class(); +} + +void do_all_tests() +{ + test_no_registration(); + test_mutex_registration(); + test_rwlock_registration(); + test_cond_registration(); + test_thread_registration(); + test_file_registration(); + test_socket_registration(); + test_table_registration(); + test_memory_registration(); + test_instruments_reset(); +} + +int main(int argc, char **argv) +{ + plan(209); + MY_INIT(argv[0]); + do_all_tests(); + my_end(0); + return (exit_status()); +} diff --git a/storage/perfschema/unittest/pfs_misc-t.cc b/storage/perfschema/unittest/pfs_misc-t.cc new file mode 100644 index 00000000..f5964348 --- /dev/null +++ b/storage/perfschema/unittest/pfs_misc-t.cc @@ -0,0 +1,95 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_thread.h> +#include <pfs_instr.h> +#include <pfs_stat.h> +#include <pfs_global.h> +#include <pfs_instr_class.h> +#include <pfs_buffer_container.h> +#include <tap.h> + +#include "stub_global_status_var.h" + +#include <memory.h> + +void test_digest_length_overflow() +{ + if (sizeof(size_t) != 4) + { + skip(3, "digest length overflow requires a 32-bit environment"); + return; + } + + PFS_global_param param; + memset(¶m, 0, sizeof(param)); + param.m_enabled= true; + /* + Force 32-bit arithmetic overflow using the digest memory allocation + parameters. The Performance Schema should detect the overflow, free + allocated memory and abort initialization with a warning. + */ + + /* Max digest length, events_statements_history_long. */ + param.m_events_statements_history_long_sizing= 10000; + param.m_digest_sizing= 1000; + param.m_max_digest_length= (1024 * 1024); + param.m_max_sql_text_length= 0; + pfs_max_digest_length= param.m_max_digest_length; + pfs_max_sqltext= param.m_max_sql_text_length; + + int rc = init_events_statements_history_long(param.m_events_statements_history_long_sizing); + ok(rc == 1, "digest length overflow (init_events_statements_history_long"); + + /* Max sql text length, events_statements_history_long. */ + param.m_max_sql_text_length= (1024 * 1024); + param.m_max_digest_length= 0; + pfs_max_digest_length= param.m_max_digest_length; + pfs_max_sqltext= param.m_max_sql_text_length; + + rc = init_events_statements_history_long(param.m_events_statements_history_long_sizing); + ok(rc == 1, "sql text length overflow (init_events_statements_history_long"); + + /* Max digest length, events_statements_summary_by_digest. */ + param.m_max_digest_length= (1024 * 1024); + param.m_digest_sizing= 10000; + pfs_max_digest_length= param.m_max_digest_length; + pfs_max_sqltext= param.m_max_sql_text_length; + + rc = init_digest(¶m); + ok(rc == 1, "digest length overflow (init_digest)"); +} + +void do_all_tests() +{ + test_digest_length_overflow(); +} + +int main(int, char **) +{ + plan(3); + MY_INIT("pfs_misc-t"); + do_all_tests(); + my_end(0); + return (exit_status()); +} diff --git a/storage/perfschema/unittest/pfs_noop-t.cc b/storage/perfschema/unittest/pfs_noop-t.cc new file mode 100644 index 00000000..57e8cfd8 --- /dev/null +++ b/storage/perfschema/unittest/pfs_noop-t.cc @@ -0,0 +1,245 @@ +/* Copyright (c) 2013, 2023, Oracle and/or its affiliates. + Copyright (c) 2022, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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 */ + +#include <my_global.h> +#include <my_thread.h> +#include <pfs_server.h> +#include <pfs_instr_class.h> +#include <pfs_instr.h> +#include <pfs_global.h> +#include <tap.h> + +#include <string.h> +#include <memory.h> + +#include "stub_print_error.h" +#include "stub_pfs_defaults.h" + +void test_noop() +{ + PSI_mutex *mutex; + PSI_rwlock *rwlock; + PSI_cond *cond; + PSI_socket *socket; + PSI_table_share *table_share; + PSI_table *table; + PSI_file *file; + PSI_thread *thread; + PSI_file_locker *file_locker; + PSI_idle_locker *idle_locker; + PSI_mutex_locker *mutex_locker; + PSI_rwlock_locker *rwlock_locker; + PSI_cond_locker *cond_locker; + PSI_table_locker *table_locker; + PSI_statement_locker *statement_locker; + PSI_transaction_locker *transaction_locker; + PSI_socket_locker *socket_locker; + PSI_digest_locker *digest_locker; + PSI_sp_locker *sp_locker; + PSI_sp_share *sp_share; + PSI_memory_key memory_key; + PSI_metadata_lock *metadata_lock; + PSI_metadata_locker *metadata_locker; + PSI_thread *owner; + + diag("test_noop"); + + PSI_server->register_mutex(NULL, NULL, 0); + PSI_server->register_rwlock(NULL, NULL, 0); + PSI_server->register_cond(NULL, NULL, 0); + PSI_server->register_thread(NULL, NULL, 0); + PSI_server->register_file(NULL, NULL, 0); + PSI_server->register_stage(NULL, NULL, 0); + PSI_server->register_statement(NULL, NULL, 0); + PSI_server->register_socket(NULL, NULL, 0); + + ok(true, "register"); + mutex= PSI_server->init_mutex(1, NULL); + ok(mutex == NULL, "no mutex"); + PSI_server->destroy_mutex(NULL); + rwlock= PSI_server->init_rwlock(1, NULL); + ok(rwlock == NULL, "no rwlock"); + PSI_server->destroy_rwlock(NULL); + cond= PSI_server->init_cond(1, NULL); + ok(cond == NULL, "no cond"); + PSI_server->destroy_cond(NULL); + socket= PSI_server->init_socket(1, NULL, NULL, 0); + ok(socket == NULL, "no socket"); + PSI_server->destroy_socket(NULL); + table_share= PSI_server->get_table_share(false, NULL); + ok(table_share == NULL, "no table_share"); + PSI_server->release_table_share(NULL); + PSI_server->drop_table_share(false, NULL, 0, NULL, 0); + table= PSI_server->open_table(NULL, NULL); + ok(table == NULL, "no table"); + PSI_server->unbind_table(NULL); + table= PSI_server->rebind_table(NULL, NULL, NULL); + ok(table == NULL, "no table"); + PSI_server->close_table(NULL, NULL); + PSI_server->create_file(1, NULL, 2); + /* TODO: spawn thread */ + thread= PSI_server->new_thread(1, NULL, 2); + ok(thread == NULL, "no thread"); + PSI_server->set_thread_id(NULL, 1); + thread= PSI_server->get_thread(); + ok(thread == NULL, "no thread"); + PSI_server->set_thread_user(NULL, 0); + PSI_server->set_thread_account(NULL, 0, NULL, 0); + PSI_server->set_thread_db(NULL, 0); + PSI_server->set_thread_command(1); + PSI_server->set_thread_start_time(1); + PSI_server->set_thread_state(NULL); + PSI_server->set_thread_info(NULL, 0); + PSI_server->set_thread(NULL); + PSI_server->delete_current_thread(); + PSI_server->delete_thread(NULL); + file_locker= PSI_server->get_thread_file_name_locker(NULL, 1, PSI_FILE_OPEN, NULL, NULL); + ok(file_locker == NULL, "no file_locker"); + file_locker= PSI_server->get_thread_file_stream_locker(NULL, NULL, PSI_FILE_OPEN); + ok(file_locker == NULL, "no file_locker"); + file_locker= PSI_server->get_thread_file_descriptor_locker(NULL, 0, PSI_FILE_OPEN); + ok(file_locker == NULL, "no file_locker"); + PSI_server->unlock_mutex(NULL); + PSI_server->unlock_rwlock(NULL); + PSI_server->signal_cond(NULL); + PSI_server->broadcast_cond(NULL); + idle_locker= PSI_server->start_idle_wait(NULL, NULL, 0); + ok(idle_locker == NULL, "no idle_locker"); + PSI_server->end_idle_wait(NULL); + mutex_locker= PSI_server->start_mutex_wait(NULL, NULL, PSI_MUTEX_LOCK, NULL, 0); + ok(mutex_locker == NULL, "no mutex_locker"); + PSI_server->end_mutex_wait(NULL, 0); + rwlock_locker= PSI_server->start_rwlock_rdwait(NULL, NULL, PSI_RWLOCK_READLOCK, NULL, 0); + ok(rwlock_locker == NULL, "no rwlock_locker"); + PSI_server->end_rwlock_rdwait(NULL, 0); + rwlock_locker= PSI_server->start_rwlock_wrwait(NULL, NULL, PSI_RWLOCK_WRITELOCK, NULL, 0); + ok(rwlock_locker == NULL, "no rwlock_locker"); + PSI_server->end_rwlock_wrwait(NULL, 0); + cond_locker= PSI_server->start_cond_wait(NULL, NULL, NULL, PSI_COND_WAIT, NULL, 0); + ok(cond_locker == NULL, "no cond_locker"); + PSI_server->end_cond_wait(NULL, 0); + table_locker= PSI_server->start_table_io_wait(NULL, NULL, PSI_TABLE_FETCH_ROW, 0, NULL, 0); + ok(table_locker == NULL, "no table_locker"); + PSI_server->end_table_io_wait(NULL, 0); + table_locker= PSI_server->start_table_lock_wait(NULL, NULL, PSI_TABLE_LOCK, 0, NULL, 0); + ok(table_locker == NULL, "no table_locker"); + PSI_server->end_table_lock_wait(NULL); + PSI_server->start_file_open_wait(NULL, NULL, 0); + file= PSI_server->end_file_open_wait(NULL, NULL); + ok(file == NULL, "no file"); + PSI_server->end_file_open_wait_and_bind_to_descriptor(NULL, 0); + PSI_server->start_file_wait(NULL, 0, NULL, 0); + PSI_server->end_file_wait(NULL, 0); + PSI_server->start_file_close_wait(NULL, NULL, 0); + PSI_server->end_file_close_wait(NULL, 0); + PSI_server->end_file_rename_wait(NULL, NULL, NULL, 0); + PSI_server->start_stage(1, NULL, 0); + + PSI_stage_progress *progress; + progress= PSI_server->get_current_stage_progress(); + ok(progress == NULL, "no progress"); + + PSI_server->end_stage(); + statement_locker= PSI_server->get_thread_statement_locker(NULL, 1, NULL, NULL); + ok(statement_locker == NULL, "no statement_locker"); + statement_locker= PSI_server->refine_statement(NULL, 1); + ok(statement_locker == NULL, "no statement_locker"); + PSI_server->start_statement(NULL, NULL, 0, NULL, 0); + PSI_server->set_statement_text(NULL, NULL, 0); + PSI_server->set_statement_lock_time(NULL, 0); + PSI_server->set_statement_rows_sent(NULL, 0); + PSI_server->set_statement_rows_examined(NULL, 0); + PSI_server->inc_statement_created_tmp_disk_tables(NULL, 0); + PSI_server->inc_statement_created_tmp_tables(NULL, 0); + PSI_server->inc_statement_select_full_join(NULL, 0); + PSI_server->inc_statement_select_full_range_join(NULL, 0); + PSI_server->inc_statement_select_range(NULL, 0); + PSI_server->inc_statement_select_range_check(NULL, 0); + PSI_server->inc_statement_select_scan(NULL, 0); + PSI_server->inc_statement_sort_merge_passes(NULL, 0); + PSI_server->inc_statement_sort_range(NULL, 0); + PSI_server->inc_statement_sort_rows(NULL, 0); + PSI_server->inc_statement_sort_scan(NULL, 0); + PSI_server->set_statement_no_index_used(NULL); + PSI_server->set_statement_no_good_index_used(NULL); + PSI_server->end_statement(NULL, NULL); + socket_locker= PSI_server->start_socket_wait(NULL, NULL, PSI_SOCKET_SEND, 1, NULL, 0); + ok(socket_locker == NULL, "no socket_locker"); + PSI_server->end_socket_wait(NULL, 0); + PSI_server->set_socket_state(NULL, PSI_SOCKET_STATE_IDLE); + PSI_server->set_socket_info(NULL, NULL, NULL, 0); + PSI_server->set_socket_thread_owner(NULL); + digest_locker= PSI_server->digest_start(NULL); + ok(digest_locker == NULL, "no digest_locker"); + PSI_server->digest_end(NULL, NULL); + sp_locker= PSI_server->start_sp(NULL, NULL); + ok(sp_locker == NULL, "no sp_locker"); + PSI_server->end_sp(NULL); + PSI_server->drop_sp(0, NULL, 0, NULL, 0); + sp_share= PSI_server->get_sp_share(0, NULL, 0, NULL, 0); + ok(sp_share == NULL, "no sp_share"); + PSI_server->release_sp_share(NULL); + PSI_server->register_memory(NULL, NULL, 0); + memory_key= PSI_server->memory_alloc(0, 0, & owner); + ok(memory_key == PSI_NOT_INSTRUMENTED, "no memory_key"); + memory_key= PSI_server->memory_realloc(0, 0, 0, & owner); + ok(memory_key == PSI_NOT_INSTRUMENTED, "no memory_key"); + PSI_server->memory_free(0, 0, NULL); + PSI_server->unlock_table(NULL); + metadata_lock= PSI_server->create_metadata_lock(NULL, NULL, 1, 2, 3, NULL, 0); + ok(metadata_lock == NULL, "no metadata_lock"); + PSI_server->set_metadata_lock_status(NULL, 0); + PSI_server->destroy_metadata_lock(NULL); + metadata_locker= PSI_server->start_metadata_wait(NULL, NULL, NULL, 0); + ok(metadata_locker == NULL, "no metadata_locker"); + PSI_server->end_metadata_wait(NULL, 0); + + transaction_locker= PSI_server->get_thread_transaction_locker(NULL, NULL, 0, 1, false, 1); + ok(transaction_locker == NULL, "no transaction_locker"); + PSI_server->start_transaction(NULL, NULL, 0); + PSI_server->end_transaction(NULL, true); + + PSI_server->set_transaction_gtid(NULL, NULL, NULL); + PSI_server->set_transaction_trxid(NULL, NULL); + PSI_server->set_transaction_xa_state(NULL, 1); + PSI_server->set_transaction_xid(NULL, NULL, 1); + PSI_server->inc_transaction_release_savepoint(NULL, 1); + PSI_server->inc_transaction_rollback_to_savepoint(NULL, 1); + PSI_server->inc_transaction_savepoints(NULL, 1); + + PSI_server->set_thread_THD(NULL, NULL); + + PSI_server->set_thread_peer_port(NULL, 0); + + ok(true, "all noop api called"); +} + +int main(int, char **) +{ + plan(34); + + MY_INIT("pfs_noop-t"); + test_noop(); + return (exit_status()); +} + diff --git a/storage/perfschema/unittest/pfs_server_stubs.cc b/storage/perfschema/unittest/pfs_server_stubs.cc new file mode 100644 index 00000000..1ec84c25 --- /dev/null +++ b/storage/perfschema/unittest/pfs_server_stubs.cc @@ -0,0 +1,63 @@ +/* Copyright (c) 2010, 2023, 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, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/* + Minimal code to be able to link a unit test. +*/ + +#include "my_global.h" +#include "m_ctype.h" +#include "sql_class.h" +#include "sql_show.h" + +struct system_status_var global_status_var; +struct sql_digest_storage; + +uint lower_case_table_names= 0; +CHARSET_INFO *files_charset_info= NULL; +CHARSET_INFO *system_charset_info= NULL; + +void compute_digest_md5(const sql_digest_storage *, unsigned char *) +{ +} + +void reset_status_vars() +{ +} + +void sql_print_warning(const char *format, ...) +{ + /* Do not pollute the unit test output with annoying messages. */ +} + +class sys_var { public: enum where { AUTO }; }; +void set_sys_var_value_origin(void *, enum sys_var::where, const char *) +{ +} + +enum sys_var::where get_sys_var_value_origin(void *ptr) +{ + return sys_var::AUTO; +} + +MY_TIMER_INFO sys_timer_info; diff --git a/storage/perfschema/unittest/pfs_timer-t.cc b/storage/perfschema/unittest/pfs_timer-t.cc new file mode 100644 index 00000000..20a5cb97 --- /dev/null +++ b/storage/perfschema/unittest/pfs_timer-t.cc @@ -0,0 +1,128 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_pthread.h> +#include <pfs_timer.h> +#include "my_sys.h" +#include <tap.h> + +void test_timers() +{ + ulonglong t1_a; + ulonglong t2_a; + ulonglong t3_a; + ulonglong t4_a; + ulonglong t5_a; + ulonglong t1_b; + ulonglong t2_b; + ulonglong t3_b; + ulonglong t4_b; + ulonglong t5_b; + + my_timer_init(&sys_timer_info); + + init_timers(); + + t1_a= get_timer_pico_value(TIMER_NAME_CYCLE); + /* Wait 5 seconds */ + my_sleep(5000000); + t1_b= get_timer_pico_value(TIMER_NAME_CYCLE); + + t2_a= get_timer_pico_value(TIMER_NAME_NANOSEC); + my_sleep(5000000); + t2_b= get_timer_pico_value(TIMER_NAME_NANOSEC); + + t3_a= get_timer_pico_value(TIMER_NAME_MICROSEC); + my_sleep(5000000); + t3_b= get_timer_pico_value(TIMER_NAME_MICROSEC); + + t4_a= get_timer_pico_value(TIMER_NAME_MILLISEC); + my_sleep(5000000); + t4_b= get_timer_pico_value(TIMER_NAME_MILLISEC); + + t5_a= get_timer_pico_value(TIMER_NAME_TICK); + my_sleep(5000000); + t5_b= get_timer_pico_value(TIMER_NAME_TICK); + + /* + Print the timer values, for manual inspection by a human. + Tests involving low level timers can not be automated. + */ + diag("cycle a: %13llu", t1_a); + diag("nano a: %13llu", t2_a); + diag("micro a: %13llu", t3_a); + diag("milli a: %13llu", t4_a); + diag("tick a: %13llu", t5_a); + + diag("cycle b: %13llu", t1_b); + diag("nano b: %13llu", t2_b); + diag("micro b: %13llu", t3_b); + diag("milli b: %13llu", t4_b); + diag("tick b: %13llu", t5_b); + + diag("cycle b-a: %13llu", t1_b-t1_a); + diag("nano b-a: %13llu", t2_b-t2_a); + diag("micro b-a: %13llu", t3_b-t3_a); + diag("milli b-a: %13llu", t4_b-t4_a); + diag("tick b-a: %13llu", t5_b-t5_a); + + if ((t1_a == 0) && (t1_b == 0)) + skip(1, "cycle timer not implemented"); + else + ok(t1_b > t1_a, "cycle timer ascending"); + + if ((t2_a == 0) && (t2_b == 0)) + skip(1, "nano timer not implemented"); + else + ok(t2_b > t2_a, "nano timer ascending"); + + if ((t3_a == 0) && (t3_b == 0)) + skip(1, "micro timer not implemented"); + else + ok(t3_b > t3_a, "micro timer ascending"); + + if ((t4_a == 0) && (t4_b == 0)) + skip(1, "milli timer not implemented"); + else + ok(t4_b > t4_a, "milli timer ascending"); + + if ((t5_a == 0) && (t5_b == 0)) + skip(1, "tick timer not implemented"); + else + ok(t5_b > t5_a, "tick timer ascending"); +} + +void do_all_tests() +{ + test_timers(); +} + +int main(int, char **) +{ + plan(5); + MY_INIT("pfs_timer-t"); + do_all_tests(); + my_end(0); + return (exit_status()); +} + diff --git a/storage/perfschema/unittest/pfs_user-oom-t.cc b/storage/perfschema/unittest/pfs_user-oom-t.cc new file mode 100644 index 00000000..acc3d8c6 --- /dev/null +++ b/storage/perfschema/unittest/pfs_user-oom-t.cc @@ -0,0 +1,146 @@ +/* Copyright (c) 2011, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_thread.h> +#include <pfs_instr.h> +#include <pfs_stat.h> +#include <pfs_global.h> +#include <pfs_user.h> +#include <pfs_buffer_container.h> +#include <tap.h> + +#include "stub_pfs_global.h" +#include "stub_global_status_var.h" + +#include <string.h> /* memset */ + +void test_oom() +{ + PSI *psi; + PFS_global_param param; + PSI_bootstrap *boot; + + memset(& param, 0xFF, sizeof(param)); + param.m_enabled= true; + param.m_mutex_class_sizing= 0; + param.m_rwlock_class_sizing= 0; + param.m_cond_class_sizing= 0; + param.m_thread_class_sizing= 10; + param.m_table_share_sizing= 0; + param.m_file_class_sizing= 0; + param.m_socket_class_sizing= 0; + param.m_mutex_sizing= 0; + param.m_rwlock_sizing= 0; + param.m_cond_sizing= 0; + param.m_thread_sizing= 1000; + param.m_table_sizing= 0; + param.m_file_sizing= 0; + param.m_file_handle_sizing= 0; + param.m_socket_sizing= 0; + param.m_events_waits_history_sizing= 10; + param.m_events_waits_history_long_sizing= 0; + param.m_setup_actor_sizing= 0; + param.m_setup_object_sizing= 0; + param.m_host_sizing= 0; + param.m_user_sizing= 1000; + param.m_account_sizing= 0; + param.m_stage_class_sizing= 50; + param.m_events_stages_history_sizing= 0; + param.m_events_stages_history_long_sizing= 0; + param.m_statement_class_sizing= 50; + param.m_events_statements_history_sizing= 0; + param.m_events_statements_history_long_sizing= 0; + param.m_events_transactions_history_sizing= 0; + param.m_events_transactions_history_long_sizing= 0; + param.m_digest_sizing= 0; + param.m_session_connect_attrs_sizing= 0; + param.m_program_sizing= 0; + param.m_statement_stack_sizing= 0; + param.m_memory_class_sizing= 10; + param.m_metadata_lock_sizing= 0; + param.m_max_digest_length= 0; + param.m_max_sql_text_length= 0; + + /* Setup */ + + stub_alloc_always_fails= false; + stub_alloc_fails_after_count= 1000; + + pre_initialize_performance_schema(); + boot= initialize_performance_schema(¶m); + psi= (PSI *)boot->get_interface(PSI_VERSION_1); + + PSI_thread_key thread_key_1; + PSI_thread_info all_thread[]= + { + {&thread_key_1, "T-1", 0} + }; + psi->register_thread("test", all_thread, 1); + + PSI_thread *thread_1= psi->new_thread(thread_key_1, NULL, 0); + psi->set_thread(thread_1); + + /* Tests */ + + int first_fail= 1; + stub_alloc_fails_after_count= first_fail; + psi->set_thread_account("user1", 5, "", 0); + ok(global_user_container.m_lost == 1, "oom (user)"); + + stub_alloc_fails_after_count= first_fail + 1; + psi->set_thread_account("user2", 5, "", 0); + ok(global_user_container.m_lost == 2, "oom (user waits)"); + + stub_alloc_fails_after_count= first_fail + 2; + psi->set_thread_account("user3", 5, "", 0); + ok(global_user_container.m_lost == 3, "oom (user stages)"); + + stub_alloc_fails_after_count= first_fail + 3; + psi->set_thread_account("user4", 5, "", 0); + ok(global_user_container.m_lost == 4, "oom (user statements)"); + + stub_alloc_fails_after_count= first_fail + 4; + psi->set_thread_account("user5", 5, "", 0); + ok(global_user_container.m_lost == 5, "oom (user transactions)"); + + stub_alloc_fails_after_count= first_fail + 5; + psi->set_thread_account("user6", 5, "", 0); + ok(global_user_container.m_lost == 6, "oom (user memory)"); + + shutdown_performance_schema(); +} + +void do_all_tests() +{ + test_oom(); +} + +int main(int, char **) +{ + plan(6); + MY_INIT("pfs_user-oom-t"); + do_all_tests(); + my_end(0); + return (exit_status()); +} + diff --git a/storage/perfschema/unittest/stub_global_status_var.h b/storage/perfschema/unittest/stub_global_status_var.h new file mode 100644 index 00000000..70644981 --- /dev/null +++ b/storage/perfschema/unittest/stub_global_status_var.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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 */ + +#include <my_global.h> +#include <my_sys.h> +#include <pfs_global.h> +#include <string.h> + + +void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var) +{ +} diff --git a/storage/perfschema/unittest/stub_pfs_defaults.h b/storage/perfschema/unittest/stub_pfs_defaults.h new file mode 100644 index 00000000..4b064b57 --- /dev/null +++ b/storage/perfschema/unittest/stub_pfs_defaults.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2010, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <pfs.h> +#include <pfs_defaults.h> + +void install_default_setup(PSI_bootstrap *) +{ +} + diff --git a/storage/perfschema/unittest/stub_pfs_global.h b/storage/perfschema/unittest/stub_pfs_global.h new file mode 100644 index 00000000..4b792f9b --- /dev/null +++ b/storage/perfschema/unittest/stub_pfs_global.h @@ -0,0 +1,93 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + Copyright (c) 2022, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_sys.h> +#include <pfs_global.h> +#include <string.h> +#include "aligned.h" +#include "assume_aligned.h" + +bool pfs_initialized= false; +size_t pfs_allocated_memory_size= 0; +size_t pfs_allocated_memory_count= 0; + +bool stub_alloc_always_fails= true; +int stub_alloc_fails_after_count= 0; + +void *pfs_malloc(PFS_builtin_memory_class *klass, size_t size, myf) +{ + /* + Catch non initialized sizing parameter in the unit tests. + */ + assert(size <= 100*1024*1024); + + if (stub_alloc_always_fails) + return NULL; + + if (--stub_alloc_fails_after_count <= 0) + return NULL; + + size= MY_ALIGN(size, CPU_LEVEL1_DCACHE_LINESIZE); + void *ptr= aligned_malloc(size, CPU_LEVEL1_DCACHE_LINESIZE); + if (ptr != NULL) + memset_aligned<CPU_LEVEL1_DCACHE_LINESIZE>(ptr, 0, size); + return ptr; +} + +void pfs_free(PFS_builtin_memory_class *, size_t, void *ptr) +{ + if (ptr != NULL) + free(ptr); +} + +void *pfs_malloc_array(PFS_builtin_memory_class *klass, size_t n, size_t size, myf flags) +{ + size_t array_size= n * size; + /* Check for overflow before allocating. */ + if (is_overflow(array_size, n, size)) + return NULL; + return pfs_malloc(klass, array_size, flags); +} + +void pfs_free_array(PFS_builtin_memory_class *klass, size_t n, size_t size, void *ptr) +{ + if (ptr == NULL) + return; + size_t array_size= n * size; + return pfs_free(klass, array_size, ptr); +} + +bool is_overflow(size_t product, size_t n1, size_t n2) +{ + if (n1 != 0 && (product / n1 != n2)) + return true; + else + return false; +} + +void pfs_print_error(const char *format, ...) +{ +} + + diff --git a/storage/perfschema/unittest/stub_print_error.h b/storage/perfschema/unittest/stub_print_error.h new file mode 100644 index 00000000..a02e2478 --- /dev/null +++ b/storage/perfschema/unittest/stub_print_error.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2008, 2023, Oracle and/or its affiliates. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + 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, version 2.0, 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, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include <my_global.h> +#include <my_sys.h> +#include <pfs_global.h> + +bool pfs_initialized= false; + +void *pfs_malloc(PFS_builtin_memory_class *klass, size_t size, myf flags) +{ + void *ptr= malloc(size); + if (ptr && (flags & MY_ZEROFILL)) + memset(ptr, 0, size); + return ptr; +} + +void pfs_free(PFS_builtin_memory_class *, size_t, void *ptr) +{ + if (ptr != NULL) + free(ptr); +} + +void *pfs_malloc_array(PFS_builtin_memory_class *klass, size_t n, size_t size, myf flags) +{ + size_t array_size= n * size; + /* Check for overflow before allocating. */ + if (is_overflow(array_size, n, size)) + return NULL; + return pfs_malloc(klass, array_size, flags); +} + +void pfs_free_array(PFS_builtin_memory_class *klass, size_t n, size_t size, void *ptr) +{ + if (ptr == NULL) + return; + size_t array_size= n * size; + return pfs_free(klass, array_size, ptr); +} + +bool is_overflow(size_t product, size_t n1, size_t n2) +{ + if (n1 != 0 && (product / n1 != n2)) + return true; + else + return false; +} + +void pfs_print_error(const char *format, ...) +{ + /* Do not pollute the unit test output with annoying messages. */ +} + |