diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:07:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:07:14 +0000 |
commit | a175314c3e5827eb193872241446f2f8f5c9d33c (patch) | |
tree | cd3d60ca99ae00829c52a6ca79150a5b6e62528b /storage/perfschema | |
parent | Initial commit. (diff) | |
download | mariadb-10.5-upstream/1%10.5.12.tar.xz mariadb-10.5-upstream/1%10.5.12.zip |
Adding upstream version 1:10.5.12.upstream/1%10.5.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'storage/perfschema')
259 files changed, 75817 insertions, 0 deletions
diff --git a/storage/perfschema/CMakeLists.txt b/storage/perfschema/CMakeLists.txt new file mode 100644 index 00000000..d5625afb --- /dev/null +++ b/storage/perfschema/CMakeLists.txt @@ -0,0 +1,388 @@ +# Copyright (c) 2009, 2019, 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, +# 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_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_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) +IF (TARGET perfschema) + ADD_DEPENDENCIES(perfschema GenServerSource) + 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..1b208e00 --- /dev/null +++ b/storage/perfschema/cursor_by_account.cc @@ -0,0 +1,82 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..d689cf6c --- /dev/null +++ b/storage/perfschema/cursor_by_account.h @@ -0,0 +1,68 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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() + {} + +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..6aa3423c --- /dev/null +++ b/storage/perfschema/cursor_by_host.cc @@ -0,0 +1,82 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..8f256156 --- /dev/null +++ b/storage/perfschema/cursor_by_host.h @@ -0,0 +1,68 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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() + {} + +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..5d56794b --- /dev/null +++ b/storage/perfschema/cursor_by_thread.cc @@ -0,0 +1,83 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..1fd803d9 --- /dev/null +++ b/storage/perfschema/cursor_by_thread.h @@ -0,0 +1,68 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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() + {} + +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..b4462a8d --- /dev/null +++ b/storage/perfschema/cursor_by_thread_connect_attr.cc @@ -0,0 +1,92 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..7aa31115 --- /dev/null +++ b/storage/perfschema/cursor_by_thread_connect_attr.h @@ -0,0 +1,89 @@ +/* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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() + {} + +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..7699bcf1 --- /dev/null +++ b/storage/perfschema/cursor_by_user.cc @@ -0,0 +1,82 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..d31f8861 --- /dev/null +++ b/storage/perfschema/cursor_by_user.h @@ -0,0 +1,68 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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() + {} + +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..da0c2c08 --- /dev/null +++ b/storage/perfschema/ha_perfschema.cc @@ -0,0 +1,497 @@ +/* Copyright (c) 2008, 2016, 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, + 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() +{} + +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"); + + DBUG_ASSERT(m_table_share); + DBUG_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"); + DBUG_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"); + + DBUG_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"); + DBUG_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); + + DBUG_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"); + 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..690bf8d1 --- /dev/null +++ b/storage/perfschema/ha_perfschema.h @@ -0,0 +1,251 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 + { + DBUG_ASSERT(table != NULL); + DBUG_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..5ddea9b1 --- /dev/null +++ b/storage/perfschema/my_thread.h @@ -0,0 +1,133 @@ +#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 + +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..3e9198a6 --- /dev/null +++ b/storage/perfschema/pfs.cc @@ -0,0 +1,7158 @@ +/* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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); + + DBUG_ASSERT(strcmp(new_thread->m_class->m_name, "thread/sql/event_worker") != 0); + DBUG_ASSERT(strcmp(new_thread->m_class->m_name, "thread/sql/event_scheduler") != 0); + DBUG_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() +{ + DBUG_ASSERT(THR_PFS_initialized); + PFS_thread *thread= static_cast<PFS_thread*>(my_get_thread_local(THR_PFS)); + DBUG_ASSERT(thread == NULL || sanitize_thread(thread) != NULL); + return thread; +} + +static inline void +my_thread_set_THR_PFS(PFS_thread *pfs) +{ + DBUG_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; \ + \ + DBUG_ASSERT(category != NULL); \ + DBUG_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++) \ + { \ + DBUG_ASSERT(info->m_key != NULL); \ + DBUG_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; + + DBUG_ASSERT(category != NULL); + DBUG_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++) + { + DBUG_ASSERT(info->m_key != NULL); + DBUG_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; + + DBUG_ASSERT(category != NULL); + DBUG_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; + + DBUG_ASSERT(category != NULL); + DBUG_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, const 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); + + DBUG_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, const 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); + + DBUG_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, const 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); + + DBUG_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)) + { + DBUG_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); + + DBUG_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(); + + DBUG_ASSERT((user != NULL) || (user_len == 0)); + DBUG_ASSERT(user_len >= 0); + DBUG_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(); + + DBUG_ASSERT((user != NULL) || (user_len == 0)); + DBUG_ASSERT(user_len >= 0); + DBUG_ASSERT((uint) user_len <= sizeof(pfs->m_username)); + DBUG_ASSERT((host != NULL) || (host_len == 0)); + DBUG_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(); + + DBUG_ASSERT((db != NULL) || (db_len == 0)); + DBUG_ASSERT(db_len >= 0); + DBUG_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(); + + DBUG_ASSERT(command >= 0); + DBUG_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(); + + DBUG_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. + @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); + DBUG_ASSERT((int) op >= 0); + DBUG_ASSERT((uint) op < array_elements(mutex_operation_map)); + DBUG_ASSERT(state != NULL); + + DBUG_ASSERT(pfs_mutex != NULL); + DBUG_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); + DBUG_ASSERT(static_cast<int> (op) >= 0); + DBUG_ASSERT(static_cast<uint> (op) < array_elements(rwlock_operation_map)); + DBUG_ASSERT(state != NULL); + DBUG_ASSERT(pfs_rwlock != NULL); + DBUG_ASSERT(pfs_rwlock->m_class != NULL); + + /* Operations supported for READ WRITE LOCK */ + + DBUG_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 */ + + DBUG_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) +{ + DBUG_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) +{ + DBUG_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); + DBUG_ASSERT(static_cast<int> (op) >= 0); + DBUG_ASSERT(static_cast<uint> (op) < array_elements(cond_operation_map)); + DBUG_ASSERT(state != NULL); + DBUG_ASSERT(pfs_cond != NULL); + DBUG_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: + DBUG_ASSERT(false); + } + + /* Dead code */ + return PFS_TL_READ; +} + +static inline PFS_TL_LOCK_TYPE external_lock_flags_to_lock_type(uint flags) +{ + DBUG_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) +{ + DBUG_ASSERT(static_cast<int> (op) >= 0); + DBUG_ASSERT(static_cast<uint> (op) < array_elements(table_io_operation_map)); + DBUG_ASSERT(state != NULL); + PFS_table *pfs_table= reinterpret_cast<PFS_table*> (table); + DBUG_ASSERT(pfs_table != NULL); + DBUG_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) +{ + DBUG_ASSERT(state != NULL); + DBUG_ASSERT((op == PSI_TABLE_LOCK) || (op == PSI_TABLE_EXTERNAL_LOCK)); + + PFS_table *pfs_table= reinterpret_cast<PFS_table*> (table); + + DBUG_ASSERT(pfs_table != NULL); + DBUG_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; + DBUG_ASSERT(false); + } + + DBUG_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) +{ + DBUG_ASSERT(static_cast<int> (op) >= 0); + DBUG_ASSERT(static_cast<uint> (op) < array_elements(file_operation_map)); + DBUG_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); + DBUG_ASSERT(static_cast<int> (op) >= 0); + DBUG_ASSERT(static_cast<uint> (op) < array_elements(file_operation_map)); + DBUG_ASSERT(state != NULL); + + if (unlikely(pfs_file == NULL)) + return NULL; + DBUG_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); + DBUG_ASSERT(static_cast<int> (op) >= 0); + DBUG_ASSERT(static_cast<uint> (op) < array_elements(file_operation_map)); + DBUG_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; + + DBUG_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) +{ + DBUG_ASSERT(static_cast<int> (op) >= 0); + DBUG_ASSERT(static_cast<uint> (op) < array_elements(socket_operation_map)); + DBUG_ASSERT(state != NULL); + PFS_socket *pfs_socket= reinterpret_cast<PFS_socket*> (socket); + + DBUG_ASSERT(pfs_socket != NULL); + DBUG_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); + + DBUG_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); + DBUG_ASSERT(pfs_rwlock != NULL); + DBUG_ASSERT(pfs_rwlock == sanitize_rwlock(pfs_rwlock)); + DBUG_ASSERT(pfs_rwlock->m_class != NULL); + DBUG_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); + + DBUG_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); + + DBUG_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) +{ + DBUG_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; + + DBUG_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); + DBUG_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); + DBUG_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--; + + DBUG_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); + DBUG_ASSERT(state != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + + PFS_mutex *mutex= reinterpret_cast<PFS_mutex *> (state->m_mutex); + DBUG_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; + + DBUG_ASSERT(index <= wait_class_max); + DBUG_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); + DBUG_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--; + + DBUG_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); + DBUG_ASSERT(state != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + + PFS_rwlock *rwlock= reinterpret_cast<PFS_rwlock *> (state->m_rwlock); + DBUG_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); + DBUG_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); + DBUG_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--; + + DBUG_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); + DBUG_ASSERT(state != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + + PFS_rwlock *rwlock= reinterpret_cast<PFS_rwlock *> (state->m_rwlock); + DBUG_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); + DBUG_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--; + + DBUG_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); + DBUG_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); + DBUG_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); + DBUG_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--; + + DBUG_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); + DBUG_ASSERT(state != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + + PFS_table *table= reinterpret_cast<PFS_table *> (state->m_table); + DBUG_ASSERT(table != NULL); + + PFS_single_stat *stat; + PFS_table_io_stat *table_io_stat; + + DBUG_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: + DBUG_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); + DBUG_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); + DBUG_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--; + + DBUG_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); + DBUG_ASSERT(state != NULL); + + ulonglong timer_end= 0; + ulonglong wait_time= 0; + + PFS_table *table= reinterpret_cast<PFS_table *> (state->m_table); + DBUG_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); + DBUG_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); + DBUG_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--; + + DBUG_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); + DBUG_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: + DBUG_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); + DBUG_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) +{ + DBUG_ASSERT(filename != NULL); + PSI_file_locker_state *state= reinterpret_cast<PSI_file_locker_state*> (locker); + DBUG_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); + DBUG_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); + DBUG_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); + DBUG_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: + DBUG_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) + { + DBUG_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); + DBUG_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--; + + DBUG_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); + DBUG_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: + DBUG_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); + DBUG_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) + { + DBUG_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: + DBUG_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); + DBUG_ASSERT(state != NULL); + DBUG_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_id; + child_wait->m_event_type= parent_statement->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_id; + child_wait->m_event_type= parent_statement->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_thread_internal_id= pfs_thread->m_thread_internal_id; + pfs->m_event_id= event_id; + pfs->m_event_type= EVENT_TYPE_STATEMENT; + pfs->m_end_event_id= 0; + pfs->m_class= klass; + pfs->m_timer_start= 0; + pfs->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_id; + parent_type= parent_statement->m_event_type; + parent_level= parent_statement->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_nesting_event_id= parent_event; + pfs->m_nesting_event_type= parent_type; + pfs->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; + DBUG_ASSERT(state->m_class != NULL); + PFS_statement_class *klass; + /* Only refine statements for mutable instrumentation */ + klass= reinterpret_cast<PFS_statement_class*> (state->m_class); + DBUG_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); + DBUG_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); + DBUG_ASSERT(pfs != NULL); + + /* mutate EVENTS_STATEMENTS_CURRENT.EVENT_NAME */ + pfs->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); + DBUG_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); + DBUG_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); + DBUG_ASSERT(pfs != NULL); + + pfs->m_timer_start= timer_start; + pfs->m_source_file= src_file; + pfs->m_source_line= src_line; + + DBUG_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); + DBUG_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); + DBUG_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); \ + DBUG_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); \ + DBUG_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); + DBUG_ASSERT(state != NULL); + DBUG_ASSERT(da != NULL); + + if (state->m_discarded) + return; + + PFS_statement_class *klass= reinterpret_cast<PFS_statement_class *> (state->m_class); + DBUG_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); + DBUG_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); + DBUG_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_timer_end= timer_end; + pfs->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); + + DBUG_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: + DBUG_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) +{ + DBUG_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); + DBUG_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) +{ + DBUG_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_id; + pfs->m_nesting_event_type= pfs_statement->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); + DBUG_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); + DBUG_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); + DBUG_ASSERT(state != NULL); + DBUG_ASSERT(sid != NULL); + DBUG_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); + DBUG_ASSERT(state != NULL); + + if (state->m_flags & STATE_FLAG_EVENT) + { + PFS_events_transactions *pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction); + DBUG_ASSERT(pfs != NULL); + DBUG_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); + DBUG_ASSERT(state != 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_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) +{ + DBUG_ASSERT(trxid != NULL); + + PSI_transaction_locker_state *state= reinterpret_cast<PSI_transaction_locker_state*> (locker); + DBUG_ASSERT(state != NULL); + + if (state->m_flags & STATE_FLAG_EVENT) + { + PFS_events_transactions *pfs= reinterpret_cast<PFS_events_transactions*> (state->m_transaction); + DBUG_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); \ + DBUG_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); + DBUG_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); + DBUG_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); + DBUG_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); + DBUG_ASSERT(state != NULL); + + PFS_socket *socket= reinterpret_cast<PFS_socket *>(state->m_socket); + DBUG_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: + DBUG_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); + DBUG_ASSERT(thread != NULL); + PFS_events_waits *wait= reinterpret_cast<PFS_events_waits*> (state->m_wait); + DBUG_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--; + + DBUG_ASSERT(wait == thread->m_events_waits_current); + } +} + +void pfs_set_socket_state_v1(PSI_socket *socket, PSI_socket_state state) +{ + DBUG_ASSERT((state == PSI_SOCKET_STATE_IDLE) || (state == PSI_SOCKET_STATE_ACTIVE)); + PFS_socket *pfs= reinterpret_cast<PFS_socket*>(socket); + DBUG_ASSERT(pfs != NULL); + DBUG_ASSERT(pfs->m_idle || (state == PSI_SOCKET_STATE_IDLE)); + DBUG_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); + DBUG_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); + DBUG_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); + DBUG_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); + DBUG_ASSERT(statement_state != NULL); + DBUG_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); + DBUG_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(); + + DBUG_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); + DBUG_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); + DBUG_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); + DBUG_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); + + DBUG_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); + DBUG_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); + DBUG_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); + DBUG_ASSERT(state != NULL); + DBUG_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); + DBUG_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); + DBUG_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--; + + DBUG_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 +}; + +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..69b23be4 --- /dev/null +++ b/storage/perfschema/pfs.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2008, 2016, 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, + 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..b6090d06 --- /dev/null +++ b/storage/perfschema/pfs_account.cc @@ -0,0 +1,747 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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); + DBUG_ASSERT(typed_entry != NULL); + account= *typed_entry; + DBUG_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) +{ + DBUG_ASSERT(user_length <= USERNAME_LENGTH); + DBUG_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) +{ + if (likely(safe_user != NULL && safe_host != NULL)) + { + /* + Aggregate STATUS_BY_ACCOUNT to: + - STATUS_BY_USER + - STATUS_BY_HOST + */ + safe_user->m_status_stats.aggregate(& m_status_stats); + safe_host->m_status_stats.aggregate(& m_status_stats); + m_status_stats.reset(); + return; + } + + if (safe_user != NULL) + { + /* + Aggregate STATUS_BY_ACCOUNT to: + - STATUS_BY_USER + - GLOBAL_STATUS + */ + safe_user->m_status_stats.aggregate(& m_status_stats); + //m_status_stats.aggregate_to(& global_status_var); + m_status_stats.reset(); + return; + } + + 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; + } + + /* + Aggregate STATUS_BY_ACCOUNT to: + - GLOBAL_STATUS + */ + //m_status_stats.aggregate_to(& global_status_var); + 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)) + { + DBUG_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..8a966f4c --- /dev/null +++ b/storage/perfschema/pfs_account.h @@ -0,0 +1,136 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..959b66dc --- /dev/null +++ b/storage/perfschema/pfs_atomic.h @@ -0,0 +1,141 @@ +/* Copyright (c) 2009, 2013, 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, + 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..024f5519 --- /dev/null +++ b/storage/perfschema/pfs_autosize.cc @@ -0,0 +1,315 @@ +/* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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); + + DBUG_ASSERT(param->m_events_waits_history_sizing >= 0); + DBUG_ASSERT(param->m_events_waits_history_long_sizing >= 0); + DBUG_ASSERT(param->m_events_stages_history_sizing >= 0); + DBUG_ASSERT(param->m_events_stages_history_long_sizing >= 0); + DBUG_ASSERT(param->m_events_statements_history_sizing >= 0); + DBUG_ASSERT(param->m_events_statements_history_long_sizing >= 0); + DBUG_ASSERT(param->m_events_transactions_history_sizing >= 0); + DBUG_ASSERT(param->m_events_transactions_history_long_sizing >= 0); + DBUG_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..cd87cb24 --- /dev/null +++ b/storage/perfschema/pfs_buffer_container.cc @@ -0,0 +1,883 @@ +/* Copyright (c) 2014, 2016, 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, + 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..141cbdf2 --- /dev/null +++ b/storage/perfschema/pfs_buffer_container.h @@ -0,0 +1,1626 @@ +/* Copyright (c) 2014, 2016, 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, + 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) + { + DBUG_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) + { + DBUG_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) + { + DBUG_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) + { + DBUG_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; + } + + DBUG_ASSERT(m_max_page_count <= PFS_PAGE_COUNT); + DBUG_ASSERT(0 < m_last_page_size); + DBUG_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 + // ================================================================== + } + + DBUG_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) + { + DBUG_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) + { + DBUG_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; + DBUG_ASSERT(page_index + 1 == m_max_page_count); + return m_last_page_size; + } + + value_type* scan_next(uint & index, uint * found_index) + { + DBUG_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) + { + DBUG_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; + DBUG_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..676a9094 --- /dev/null +++ b/storage/perfschema/pfs_builtin_memory.cc @@ -0,0 +1,382 @@ +/* Copyright (c) 2014, 2019, 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, + 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..1c00275d --- /dev/null +++ b/storage/perfschema/pfs_builtin_memory.h @@ -0,0 +1,143 @@ +/* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..e4577fdb --- /dev/null +++ b/storage/perfschema/pfs_column_types.h @@ -0,0 +1,328 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..9ce9b3b4 --- /dev/null +++ b/storage/perfschema/pfs_column_values.cc @@ -0,0 +1,67 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..ea3d9eb9 --- /dev/null +++ b/storage/perfschema/pfs_column_values.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..3733ad9e --- /dev/null +++ b/storage/perfschema/pfs_con_slice.cc @@ -0,0 +1,82 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..56497ffe --- /dev/null +++ b/storage/perfschema/pfs_con_slice.h @@ -0,0 +1,261 @@ +/* Copyright (c) 2010, 2016, 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 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..0b615773 --- /dev/null +++ b/storage/perfschema/pfs_defaults.cc @@ -0,0 +1,106 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..7751b55b --- /dev/null +++ b/storage/perfschema/pfs_defaults.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2010, 2011, 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, + 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..6f0b93f9 --- /dev/null +++ b/storage/perfschema/pfs_digest.cc @@ -0,0 +1,412 @@ +/* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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. +*/ +static PFS_ALIGNED 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); + DBUG_ASSERT(typed_entry != NULL); + digest= *typed_entry; + DBUG_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) +{ + DBUG_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. */ + DBUG_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..39fe6109 --- /dev/null +++ b/storage/perfschema/pfs_digest.h @@ -0,0 +1,94 @@ +/* Copyright (c) 2011, 2016, 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, + 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..22ed3132 --- /dev/null +++ b/storage/perfschema/pfs_engine_table.cc @@ -0,0 +1,1931 @@ +/* Copyright (c) 2008, 2019, 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, + 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_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)); + DBUG_ASSERT(context != NULL); + + if(context) + { + m_last_version= context->m_current_version; + m_map= context->m_map; + DBUG_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)); + //DBUG_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] & (1 << 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, + + NULL +}; + +/** 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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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() + {} + + ~PFS_internal_schema_access() + {} + + 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; + } + + DBUG_ASSERT(thd->lex != NULL); + if ((thd->lex->sql_command != SQLCOM_TRUNCATE) && + (thd->lex->sql_command != SQLCOM_GRANT)) { + 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_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..883ff07c --- /dev/null +++ b/storage/perfschema/pfs_engine_table.h @@ -0,0 +1,610 @@ +/* Copyright (c) 2008, 2016, 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, + 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() + {} + + /** + 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); + +/** + 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; +}; + +/** + Privileges for read only tables. + The only operation allowed is SELECT. +*/ +class PFS_readonly_acl : public ACL_internal_table_access +{ +public: + PFS_readonly_acl() + {} + + ~PFS_readonly_acl() + {} + + 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() + {} + + ~PFS_truncatable_acl() + {} + + 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() + {} + + ~PFS_updatable_acl() + {} + + 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() + {} + + ~PFS_editable_acl() + {} + + 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() + {} + + ~PFS_unknown_acl() + {} + + 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; + + +/** 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..6d4a2893 --- /dev/null +++ b/storage/perfschema/pfs_events.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..e5a6b9a2 --- /dev/null +++ b/storage/perfschema/pfs_events_stages.cc @@ -0,0 +1,246 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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; + + DBUG_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; + + DBUG_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..0b7d27de --- /dev/null +++ b/storage/perfschema/pfs_events_stages.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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; +extern PFS_ALIGNED 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..e0f1b2bf --- /dev/null +++ b/storage/perfschema/pfs_events_statements.cc @@ -0,0 +1,329 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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; +} + +static inline void copy_events_statements(PFS_events_statements *dest, + const PFS_events_statements *source) +{ + /* Copy all attributes except SQL TEXT and DIGEST */ + memcpy(dest, source, my_offsetof(PFS_events_statements, m_sqltext)); + + /* Copy SQL TEXT */ + int sqltext_length= source->m_sqltext_length; + + if (sqltext_length > 0) + { + memcpy(dest->m_sqltext, source->m_sqltext, sqltext_length); + dest->m_sqltext_length= sqltext_length; + } + else + { + dest->m_sqltext_length= 0; + } + + /* Copy DIGEST */ + dest->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; + + DBUG_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. + */ + copy_events_statements(&thread->m_statements_history[index], 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 ; + + DBUG_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. */ + copy_events_statements(&events_statements_history_long_array[index], 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_class= NULL; +} + +/** 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_class= NULL; +} + +/** 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_class= NULL; +} + +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..8b24a9e7 --- /dev/null +++ b/storage/perfschema/pfs_events_statements.h @@ -0,0 +1,152 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 : public PFS_events +{ + 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; +}; + +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; +extern PFS_ALIGNED 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..88423491 --- /dev/null +++ b/storage/perfschema/pfs_events_transactions.cc @@ -0,0 +1,267 @@ +/* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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; + + DBUG_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 ; + + DBUG_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; + + DBUG_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..dc4fd8b0 --- /dev/null +++ b/storage/perfschema/pfs_events_transactions.h @@ -0,0 +1,132 @@ +/* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..b51ee5fd --- /dev/null +++ b/storage/perfschema/pfs_events_waits.cc @@ -0,0 +1,301 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..87e43459 --- /dev/null +++ b/storage/perfschema/pfs_events_waits.h @@ -0,0 +1,157 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2017, 2019, 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; +extern PFS_ALIGNED 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..26cb693d --- /dev/null +++ b/storage/perfschema/pfs_global.cc @@ -0,0 +1,267 @@ +/* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights + reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 <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 +#ifdef HAVE_MALLOC_H +#include <malloc.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) +{ + DBUG_ASSERT(klass != NULL); + DBUG_ASSERT(size > 0); + + void *ptr= NULL; + +#ifdef PFS_ALIGNEMENT +#ifdef HAVE_POSIX_MEMALIGN + /* Linux */ + if (unlikely(posix_memalign(& ptr, PFS_ALIGNEMENT, size))) + return NULL; +#else +#ifdef HAVE_MEMALIGN + /* Solaris */ + ptr= memalign(PFS_ALIGNEMENT, size); + if (unlikely(ptr == NULL)) + return NULL; +#else +#ifdef HAVE_ALIGNED_MALLOC + /* Windows */ + ptr= _aligned_malloc(size, PFS_ALIGNEMENT); + if (unlikely(ptr == NULL)) + return NULL; +#else +#error "Missing implementation for PFS_ALIGNENT" +#endif /* HAVE_ALIGNED_MALLOC */ +#endif /* HAVE_MEMALIGN */ +#endif /* HAVE_POSIX_MEMALIGN */ +#else /* PFS_ALIGNMENT */ + /* Everything else */ + ptr= malloc(size); + if (unlikely(ptr == NULL)) + return NULL; +#endif + + klass->count_alloc(size); + + if (flags & MY_ZEROFILL) + memset(ptr, 0, size); + return ptr; +} + +void pfs_free(PFS_builtin_memory_class *klass, size_t size, void *ptr) +{ + if (ptr == NULL) + return; + +#ifdef HAVE_POSIX_MEMALIGN + /* Allocated with posix_memalign() */ + free(ptr); +#else +#ifdef HAVE_MEMALIGN + /* Allocated with memalign() */ + free(ptr); +#else +#ifdef HAVE_ALIGNED_MALLOC + /* Allocated with _aligned_malloc() */ + _aligned_free(ptr); +#else + /* Allocated with malloc() */ + free(ptr); +#endif /* HAVE_ALIGNED_MALLOC */ +#endif /* HAVE_MEMALIGN */ +#endif /* HAVE_POSIX_MEMALIGN */ + + 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) +{ + DBUG_ASSERT(klass != NULL); + DBUG_ASSERT(n > 0); + DBUG_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. */ + DBUG_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) +{ + DBUG_ASSERT(host); + DBUG_ASSERT(src_addr); + DBUG_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..d26e81ad --- /dev/null +++ b/storage/perfschema/pfs_global.h @@ -0,0 +1,201 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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; + +#if defined(HAVE_POSIX_MEMALIGN) || defined(HAVE_MEMALIGN) || defined(HAVE_ALIGNED_MALLOC) +#define PFS_ALIGNEMENT CPU_LEVEL1_DCACHE_LINESIZE +#define PFS_ALIGNED MY_ALIGNED(PFS_ALIGNEMENT) +#else +/* + Known platforms that do not provide aligned memory: + - MacOSX Darwin (osx10.5) + For these platforms, compile without the alignment optimization. +*/ +#define PFS_ALIGNED +#endif /* HAVE_POSIX_MEMALIGN || HAVE_MEMALIGN || HAVE_ALIGNED_MALLOC */ + +#ifdef CPU_LEVEL1_DCACHE_LINESIZE +#define PFS_CACHE_LINE_SIZE CPU_LEVEL1_DCACHE_LINESIZE +#else +#define PFS_CACHE_LINE_SIZE 128 +#endif + +/** + 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[PFS_CACHE_LINE_SIZE - 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[PFS_CACHE_LINE_SIZE - 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; + + DBUG_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..b896215e --- /dev/null +++ b/storage/perfschema/pfs_host.cc @@ -0,0 +1,374 @@ +/* Copyright (c) 2010, 2018, 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 */ + +/** + @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); + DBUG_ASSERT(typed_entry != NULL); + host= *typed_entry; + DBUG_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) +{ + DBUG_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) +{ + static PFS_ALIGNED 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)) + { + DBUG_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..37819882 --- /dev/null +++ b/storage/perfschema/pfs_host.h @@ -0,0 +1,120 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..cd820146 --- /dev/null +++ b/storage/perfschema/pfs_instr.cc @@ -0,0 +1,2343 @@ +/* Copyright (c) 2008, 2018, 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, + 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; + +static PFS_ALIGNED 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 */ + DBUG_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); + DBUG_ASSERT(typed_entry != NULL); + file= *typed_entry; + DBUG_ASSERT(file != NULL); + *length= file->m_filename_length; + result= file->m_filename; + return const_cast<uchar*> (reinterpret_cast<const uchar*> (result)); +} +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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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); + + /* + 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) +{ + DBUG_ASSERT(pfs != NULL); + pfs->reset_session_connect_attrs(); + if (pfs->m_account != NULL) + { + pfs->m_account->release(); + pfs->m_account= NULL; + DBUG_ASSERT(pfs->m_user == NULL); + DBUG_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; + + DBUG_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; + + DBUG_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) +{ + DBUG_ASSERT(pfs != NULL); + pfs->m_file_stat.m_open_count--; +} + +/** + Destroy instrumentation for a file instance. + @param thread the executing thread instrumentation + @param pfs the file to destroy +*/ +void destroy_file(PFS_thread *thread, PFS_file *pfs) +{ + DBUG_ASSERT(thread != NULL); + DBUG_ASSERT(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); + DBUG_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) +{ + DBUG_ASSERT(table_stat != NULL); + DBUG_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; + + DBUG_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) +{ + DBUG_ASSERT(table_stat != NULL); + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_ASSERT(from_array != NULL); + DBUG_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) +{ + DBUG_ASSERT(from_array != NULL); + DBUG_ASSERT(to_array_1 != NULL); + DBUG_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) +{ + DBUG_ASSERT(thread->m_account == NULL); + DBUG_ASSERT(thread->m_user == NULL); + DBUG_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..ec7dff7d --- /dev/null +++ b/storage/perfschema/pfs_instr.h @@ -0,0 +1,814 @@ +/* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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; + + /** 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..3b59375c --- /dev/null +++ b/storage/perfschema/pfs_instr_class.cc @@ -0,0 +1,2064 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + 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); + DBUG_ASSERT(typed_entry != NULL); + share= *typed_entry; + DBUG_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) +{ + DBUG_ASSERT(schema_name_length <= NAME_LEN); + DBUG_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 +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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)) \ + { \ + DBUG_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]; + DBUG_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; + + DBUG_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) +{ + DBUG_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..573cfe33 --- /dev/null +++ b/storage/perfschema/pfs_instr_class.h @@ -0,0 +1,646 @@ +/* Copyright (c) 2008, 2016, 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, + 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 + { + DBUG_ASSERT(m_type == PFS_CLASS_STAGE); + return m_flags & PSI_FLAG_STAGE_PROGRESS; + } + + bool is_shared_exclusive() const + { + DBUG_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; +}; + +#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..3730e61c --- /dev/null +++ b/storage/perfschema/pfs_lock.h @@ -0,0 +1,310 @@ +/* Copyright (c) 2009, 2016, 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, + 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. */ + DBUG_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. */ + DBUG_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. */ + DBUG_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. */ + DBUG_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..c4e92d7d --- /dev/null +++ b/storage/perfschema/pfs_memory.cc @@ -0,0 +1,98 @@ +/* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..4dc21b89 --- /dev/null +++ b/storage/perfschema/pfs_memory.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..dcb79916 --- /dev/null +++ b/storage/perfschema/pfs_prepared_stmt.cc @@ -0,0 +1,145 @@ +/* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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_nesting_event_id; + else + pfs->m_owner_event_id= pfs_stmt->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..1b017b50 --- /dev/null +++ b/storage/perfschema/pfs_prepared_stmt.h @@ -0,0 +1,95 @@ +/* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..8e6d02c7 --- /dev/null +++ b/storage/perfschema/pfs_program.cc @@ -0,0 +1,322 @@ +/* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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); + DBUG_ASSERT(typed_entry != NULL); + program= *typed_entry; + DBUG_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) +{ + DBUG_ASSERT(object_name_length <= COL_OBJECT_NAME_SIZE); + DBUG_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..0cbd24b8 --- /dev/null +++ b/storage/perfschema/pfs_program.h @@ -0,0 +1,103 @@ +/* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..63deba92 --- /dev/null +++ b/storage/perfschema/pfs_server.cc @@ -0,0 +1,448 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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); + DBUG_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..68f6f71d --- /dev/null +++ b/storage/perfschema/pfs_server.h @@ -0,0 +1,309 @@ +/* Copyright (c) 2008, 2018, 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, + 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..96b367b6 --- /dev/null +++ b/storage/perfschema/pfs_setup_actor.cc @@ -0,0 +1,342 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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); + DBUG_ASSERT(typed_entry != NULL); + setup_actor= *typed_entry; + DBUG_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) +{ + DBUG_ASSERT(user_length <= USERNAME_LENGTH); + DBUG_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..3d00c52a --- /dev/null +++ b/storage/perfschema/pfs_setup_actor.h @@ -0,0 +1,112 @@ +/* Copyright (c) 2010, 2016, 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, + 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..47afd130 --- /dev/null +++ b/storage/perfschema/pfs_setup_object.cc @@ -0,0 +1,338 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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); + DBUG_ASSERT(typed_entry != NULL); + setup_object= *typed_entry; + DBUG_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) +{ + DBUG_ASSERT(schema_length <= NAME_LEN); + DBUG_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 + */ + DBUG_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..d49be341 --- /dev/null +++ b/storage/perfschema/pfs_setup_object.h @@ -0,0 +1,107 @@ +/* Copyright (c) 2010, 2016, 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 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..e3e06760 --- /dev/null +++ b/storage/perfschema/pfs_stat.h @@ -0,0 +1,1332 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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; + + DBUG_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; + + DBUG_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..2007d191 --- /dev/null +++ b/storage/perfschema/pfs_status.cc @@ -0,0 +1,166 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..6b35a162 --- /dev/null +++ b/storage/perfschema/pfs_status.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..ceec4ed9 --- /dev/null +++ b/storage/perfschema/pfs_timer.cc @@ -0,0 +1,345 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 prefered 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 prefered 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: + DBUG_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; + DBUG_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; + DBUG_ASSERT(false); + } + return result; +} + +time_normalizer* time_normalizer::get(enum_timer_name timer_name) +{ + uint index= static_cast<uint> (timer_name); + + DBUG_ASSERT(index >= FIRST_TIMER_NAME); + DBUG_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..f2bedc87 --- /dev/null +++ b/storage/perfschema/pfs_timer.h @@ -0,0 +1,152 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..feb99f41 --- /dev/null +++ b/storage/perfschema/pfs_user.cc @@ -0,0 +1,339 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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); + DBUG_ASSERT(typed_entry != NULL); + user= *typed_entry; + DBUG_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) +{ + DBUG_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)) + { + DBUG_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..9553ff8c --- /dev/null +++ b/storage/perfschema/pfs_user.h @@ -0,0 +1,122 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..9be6f12e --- /dev/null +++ b/storage/perfschema/pfs_variable.cc @@ -0,0 +1,1291 @@ +/* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + 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) +{ + DBUG_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; + DBUG_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. + */ + DBUG_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. */ + DBUG_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. */ + DBUG_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; + DBUG_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) +{ + DBUG_ASSERT(show_var); + DBUG_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) +{ + DBUG_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) +{ + DBUG_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; + DBUG_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; + DBUG_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; + DBUG_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. */ + DBUG_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) +{ + DBUG_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. */ + DBUG_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..bfe51c6d --- /dev/null +++ b/storage/perfschema/pfs_variable.h @@ -0,0 +1,716 @@ +/* Copyright (c) 2015, 2018, 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-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) +{ + DBUG_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..638877d8 --- /dev/null +++ b/storage/perfschema/pfs_visitor.cc @@ -0,0 +1,1694 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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) +{ + DBUG_ASSERT(visitor != NULL); + DBUG_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) +{ + DBUG_ASSERT(visitor != NULL); + DBUG_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) +{ + DBUG_ASSERT(visitor != NULL); + DBUG_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) +{ + DBUG_ASSERT(visitor != NULL); + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_ASSERT(visitor != NULL); + DBUG_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) +{ + DBUG_ASSERT(visitor != NULL); + DBUG_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) +{ + DBUG_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) +{ + DBUG_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) +{ + DBUG_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() +{} + +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. + */ + DBUG_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() +{} + +PFS_connection_all_wait_visitor::~PFS_connection_all_wait_visitor() +{} + +void PFS_connection_all_wait_visitor::visit_global() +{ + /* Sum by instances, not by connection */ + DBUG_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() +{} + +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() +{} + +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() +{} + +PFS_connection_all_statement_visitor::~PFS_connection_all_statement_visitor() +{} + +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() +{} + +PFS_connection_stat_visitor::~PFS_connection_stat_visitor() +{} + +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() +{} + +/** 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() +{} + +PFS_instance_wait_visitor::~PFS_instance_wait_visitor() +{} + +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() +{} + +PFS_object_wait_visitor::~PFS_object_wait_visitor() +{} + +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() +{} + +PFS_table_io_wait_visitor::~PFS_table_io_wait_visitor() +{} + +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() +{} + +PFS_table_io_stat_visitor::~PFS_table_io_stat_visitor() +{} + +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() +{} + +PFS_index_io_stat_visitor::~PFS_index_io_stat_visitor() +{} + +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() +{} + +PFS_table_lock_wait_visitor::~PFS_table_lock_wait_visitor() +{} + +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() +{} + +PFS_table_lock_stat_visitor::~PFS_table_lock_stat_visitor() +{} + +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() +{} + +PFS_instance_socket_io_stat_visitor::~PFS_instance_socket_io_stat_visitor() +{} + +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() +{} + +PFS_instance_file_io_stat_visitor::~PFS_instance_file_io_stat_visitor() +{} + +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..a81567e9 --- /dev/null +++ b/storage/perfschema/pfs_visitor.h @@ -0,0 +1,687 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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() {} + virtual ~PFS_connection_visitor() {} + /** 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() {} + virtual ~PFS_instance_visitor() {} + /** 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() {} + virtual ~PFS_object_visitor() {} + /** 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..310c81f2 --- /dev/null +++ b/storage/perfschema/table_accounts.cc @@ -0,0 +1,142 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "HOST CHAR(" HOSTNAME_LENGTH_STR ") collate utf8_bin default null," + "CURRENT_CONNECTIONS bigint not null," + "TOTAL_CONNECTIONS bigint not null)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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: + DBUG_ASSERT(false); + } + } + } + return 0; +} + diff --git a/storage/perfschema/table_accounts.h b/storage/perfschema/table_accounts.h new file mode 100644 index 00000000..7563e85c --- /dev/null +++ b/storage/perfschema/table_accounts.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..b030b882 --- /dev/null +++ b/storage/perfschema/table_all_instr.cc @@ -0,0 +1,188 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..3b5388df --- /dev/null +++ b/storage/perfschema/table_all_instr.h @@ -0,0 +1,124 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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() + {} + +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..42bef1aa --- /dev/null +++ b/storage/perfschema/table_esgs_by_account_by_event_name.cc @@ -0,0 +1,210 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "HOST CHAR(" HOSTNAME_LENGTH_STR ") collate utf8_bin default null," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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..011503aa --- /dev/null +++ b/storage/perfschema/table_esgs_by_account_by_event_name.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..ebacdc6e --- /dev/null +++ b/storage/perfschema/table_esgs_by_host_by_event_name.cc @@ -0,0 +1,211 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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..14ddbb8b --- /dev/null +++ b/storage/perfschema/table_esgs_by_host_by_event_name.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..c266bfaf --- /dev/null +++ b/storage/perfschema/table_esgs_by_thread_by_event_name.cc @@ -0,0 +1,202 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + 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: /* 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..d3798e41 --- /dev/null +++ b/storage/perfschema/table_esgs_by_thread_by_event_name.h @@ -0,0 +1,132 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..e6e6c321 --- /dev/null +++ b/storage/perfschema/table_esgs_by_user_by_event_name.cc @@ -0,0 +1,210 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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..464165a0 --- /dev/null +++ b/storage/perfschema/table_esgs_by_user_by_event_name.h @@ -0,0 +1,133 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..71145844 --- /dev/null +++ b/storage/perfschema/table_esgs_global_by_event_name.cc @@ -0,0 +1,191 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + 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: /* 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..44b3ea5c --- /dev/null +++ b/storage/perfschema/table_esgs_global_by_event_name.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..589f62b9 --- /dev/null +++ b/storage/perfschema/table_esms_by_account_by_event_name.cc @@ -0,0 +1,232 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "HOST CHAR(" HOSTNAME_LENGTH_STR ") collate utf8_bin default null," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "SUM_LOCK_TIME BIGINT unsigned not null," + "SUM_ERRORS BIGINT unsigned not null," + "SUM_WARNINGS BIGINT unsigned not null," + "SUM_ROWS_AFFECTED BIGINT unsigned not null," + "SUM_ROWS_SENT BIGINT unsigned not null," + "SUM_ROWS_EXAMINED BIGINT unsigned not null," + "SUM_CREATED_TMP_DISK_TABLES BIGINT unsigned not null," + "SUM_CREATED_TMP_TABLES BIGINT unsigned not null," + "SUM_SELECT_FULL_JOIN BIGINT unsigned not null," + "SUM_SELECT_FULL_RANGE_JOIN BIGINT unsigned not null," + "SUM_SELECT_RANGE BIGINT unsigned not null," + "SUM_SELECT_RANGE_CHECK BIGINT unsigned not null," + "SUM_SELECT_SCAN BIGINT unsigned not null," + "SUM_SORT_MERGE_PASSES BIGINT unsigned not null," + "SUM_SORT_RANGE BIGINT unsigned not null," + "SUM_SORT_ROWS BIGINT unsigned not null," + "SUM_SORT_SCAN BIGINT unsigned not null," + "SUM_NO_INDEX_USED BIGINT unsigned not null," + "SUM_NO_GOOD_INDEX_USED BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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..3881dd5d --- /dev/null +++ b/storage/perfschema/table_esms_by_account_by_event_name.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..acb9e059 --- /dev/null +++ b/storage/perfschema/table_esms_by_digest.cc @@ -0,0 +1,226 @@ +/* Copyright (c) 2010, 2016, 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 */ + +/** + @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 +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)," + "DIGEST VARCHAR(32)," + "DIGEST_TEXT LONGTEXT," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "SUM_LOCK_TIME BIGINT unsigned not null," + "SUM_ERRORS BIGINT unsigned not null," + "SUM_WARNINGS BIGINT unsigned not null," + "SUM_ROWS_AFFECTED BIGINT unsigned not null," + "SUM_ROWS_SENT BIGINT unsigned not null," + "SUM_ROWS_EXAMINED BIGINT unsigned not null," + "SUM_CREATED_TMP_DISK_TABLES BIGINT unsigned not null," + "SUM_CREATED_TMP_TABLES BIGINT unsigned not null," + "SUM_SELECT_FULL_JOIN BIGINT unsigned not null," + "SUM_SELECT_FULL_RANGE_JOIN BIGINT unsigned not null," + "SUM_SELECT_RANGE BIGINT unsigned not null," + "SUM_SELECT_RANGE_CHECK BIGINT unsigned not null," + "SUM_SELECT_SCAN BIGINT unsigned not null," + "SUM_SORT_MERGE_PASSES BIGINT unsigned not null," + "SUM_SORT_RANGE BIGINT unsigned not null," + "SUM_SORT_ROWS BIGINT unsigned not null," + "SUM_SORT_SCAN BIGINT unsigned not null," + "SUM_NO_INDEX_USED BIGINT unsigned not null," + "SUM_NO_GOOD_INDEX_USED BIGINT unsigned not null," + "FIRST_SEEN TIMESTAMP(0) NOT NULL default 0," + "LAST_SEEN TIMESTAMP(0) NOT NULL default 0)") }, + false /* perpetual */ +}; + +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. + */ + DBUG_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..9aa005bb --- /dev/null +++ b/storage/perfschema/table_esms_by_digest.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..f5f6372d --- /dev/null +++ b/storage/perfschema/table_esms_by_host_by_event_name.cc @@ -0,0 +1,233 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "SUM_LOCK_TIME BIGINT unsigned not null," + "SUM_ERRORS BIGINT unsigned not null," + "SUM_WARNINGS BIGINT unsigned not null," + "SUM_ROWS_AFFECTED BIGINT unsigned not null," + "SUM_ROWS_SENT BIGINT unsigned not null," + "SUM_ROWS_EXAMINED BIGINT unsigned not null," + "SUM_CREATED_TMP_DISK_TABLES BIGINT unsigned not null," + "SUM_CREATED_TMP_TABLES BIGINT unsigned not null," + "SUM_SELECT_FULL_JOIN BIGINT unsigned not null," + "SUM_SELECT_FULL_RANGE_JOIN BIGINT unsigned not null," + "SUM_SELECT_RANGE BIGINT unsigned not null," + "SUM_SELECT_RANGE_CHECK BIGINT unsigned not null," + "SUM_SELECT_SCAN BIGINT unsigned not null," + "SUM_SORT_MERGE_PASSES BIGINT unsigned not null," + "SUM_SORT_RANGE BIGINT unsigned not null," + "SUM_SORT_ROWS BIGINT unsigned not null," + "SUM_SORT_SCAN BIGINT unsigned not null," + "SUM_NO_INDEX_USED BIGINT unsigned not null," + "SUM_NO_GOOD_INDEX_USED BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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..807db6d4 --- /dev/null +++ b/storage/perfschema/table_esms_by_host_by_event_name.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..3cb2e4a4 --- /dev/null +++ b/storage/perfschema/table_esms_by_program.cc @@ -0,0 +1,245 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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')," + "OBJECT_SCHEMA varchar(64) NOT NULL," + "OBJECT_NAME varchar(64) NOT NULL," + "COUNT_STAR bigint(20) unsigned NOT NULL," + "SUM_TIMER_WAIT bigint(20) unsigned NOT NULL," + "MIN_TIMER_WAIT bigint(20) unsigned NOT NULL," + "AVG_TIMER_WAIT bigint(20) unsigned NOT NULL," + "MAX_TIMER_WAIT bigint(20) unsigned NOT NULL," + "COUNT_STATEMENTS bigint(20) unsigned NOT NULL," + "SUM_STATEMENTS_WAIT bigint(20) unsigned NOT NULL," + "MIN_STATEMENTS_WAIT bigint(20) unsigned NOT NULL," + "AVG_STATEMENTS_WAIT bigint(20) unsigned NOT NULL," + "MAX_STATEMENTS_WAIT bigint(20) unsigned NOT NULL," + "SUM_LOCK_TIME bigint(20) unsigned NOT NULL," + "SUM_ERRORS bigint(20) unsigned NOT NULL," + "SUM_WARNINGS bigint(20) unsigned NOT NULL," + "SUM_ROWS_AFFECTED bigint(20) unsigned NOT NULL," + "SUM_ROWS_SENT bigint(20) unsigned NOT NULL," + "SUM_ROWS_EXAMINED bigint(20) unsigned NOT NULL," + "SUM_CREATED_TMP_DISK_TABLES bigint(20) unsigned NOT NULL," + "SUM_CREATED_TMP_TABLES bigint(20) unsigned NOT NULL," + "SUM_SELECT_FULL_JOIN bigint(20) unsigned NOT NULL," + "SUM_SELECT_FULL_RANGE_JOIN bigint(20) unsigned NOT NULL," + "SUM_SELECT_RANGE bigint(20) unsigned NOT NULL," + "SUM_SELECT_RANGE_CHECK bigint(20) unsigned NOT NULL," + "SUM_SELECT_SCAN bigint(20) unsigned NOT NULL," + "SUM_SORT_MERGE_PASSES bigint(20) unsigned NOT NULL," + "SUM_SORT_RANGE bigint(20) unsigned NOT NULL," + "SUM_SORT_ROWS bigint(20) unsigned NOT NULL," + "SUM_SORT_SCAN bigint(20) unsigned NOT NULL," + "SUM_NO_INDEX_USED bigint(20) unsigned NOT NULL," + "SUM_NO_GOOD_INDEX_USED bigint(20) unsigned NOT NULL)")}, + false /* perpetual */ +}; + +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. + */ + DBUG_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..cc263878 --- /dev/null +++ b/storage/perfschema/table_esms_by_program.h @@ -0,0 +1,113 @@ +/* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..306edc76 --- /dev/null +++ b/storage/perfschema/table_esms_by_thread_by_event_name.cc @@ -0,0 +1,225 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "SUM_LOCK_TIME BIGINT unsigned not null," + "SUM_ERRORS BIGINT unsigned not null," + "SUM_WARNINGS BIGINT unsigned not null," + "SUM_ROWS_AFFECTED BIGINT unsigned not null," + "SUM_ROWS_SENT BIGINT unsigned not null," + "SUM_ROWS_EXAMINED BIGINT unsigned not null," + "SUM_CREATED_TMP_DISK_TABLES BIGINT unsigned not null," + "SUM_CREATED_TMP_TABLES BIGINT unsigned not null," + "SUM_SELECT_FULL_JOIN BIGINT unsigned not null," + "SUM_SELECT_FULL_RANGE_JOIN BIGINT unsigned not null," + "SUM_SELECT_RANGE BIGINT unsigned not null," + "SUM_SELECT_RANGE_CHECK BIGINT unsigned not null," + "SUM_SELECT_SCAN BIGINT unsigned not null," + "SUM_SORT_MERGE_PASSES BIGINT unsigned not null," + "SUM_SORT_RANGE BIGINT unsigned not null," + "SUM_SORT_ROWS BIGINT unsigned not null," + "SUM_SORT_SCAN BIGINT unsigned not null," + "SUM_NO_INDEX_USED BIGINT unsigned not null," + "SUM_NO_GOOD_INDEX_USED BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + 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: /* 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..41fec4e1 --- /dev/null +++ b/storage/perfschema/table_esms_by_thread_by_event_name.h @@ -0,0 +1,132 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..c0517748 --- /dev/null +++ b/storage/perfschema/table_esms_by_user_by_event_name.cc @@ -0,0 +1,232 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "SUM_LOCK_TIME BIGINT unsigned not null," + "SUM_ERRORS BIGINT unsigned not null," + "SUM_WARNINGS BIGINT unsigned not null," + "SUM_ROWS_AFFECTED BIGINT unsigned not null," + "SUM_ROWS_SENT BIGINT unsigned not null," + "SUM_ROWS_EXAMINED BIGINT unsigned not null," + "SUM_CREATED_TMP_DISK_TABLES BIGINT unsigned not null," + "SUM_CREATED_TMP_TABLES BIGINT unsigned not null," + "SUM_SELECT_FULL_JOIN BIGINT unsigned not null," + "SUM_SELECT_FULL_RANGE_JOIN BIGINT unsigned not null," + "SUM_SELECT_RANGE BIGINT unsigned not null," + "SUM_SELECT_RANGE_CHECK BIGINT unsigned not null," + "SUM_SELECT_SCAN BIGINT unsigned not null," + "SUM_SORT_MERGE_PASSES BIGINT unsigned not null," + "SUM_SORT_RANGE BIGINT unsigned not null," + "SUM_SORT_ROWS BIGINT unsigned not null," + "SUM_SORT_SCAN BIGINT unsigned not null," + "SUM_NO_INDEX_USED BIGINT unsigned not null," + "SUM_NO_GOOD_INDEX_USED BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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..0ed9899d --- /dev/null +++ b/storage/perfschema/table_esms_by_user_by_event_name.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..6e2408be --- /dev/null +++ b/storage/perfschema/table_esms_global_by_event_name.cc @@ -0,0 +1,215 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "SUM_LOCK_TIME BIGINT unsigned not null," + "SUM_ERRORS BIGINT unsigned not null," + "SUM_WARNINGS BIGINT unsigned not null," + "SUM_ROWS_AFFECTED BIGINT unsigned not null," + "SUM_ROWS_SENT BIGINT unsigned not null," + "SUM_ROWS_EXAMINED BIGINT unsigned not null," + "SUM_CREATED_TMP_DISK_TABLES BIGINT unsigned not null," + "SUM_CREATED_TMP_TABLES BIGINT unsigned not null," + "SUM_SELECT_FULL_JOIN BIGINT unsigned not null," + "SUM_SELECT_FULL_RANGE_JOIN BIGINT unsigned not null," + "SUM_SELECT_RANGE BIGINT unsigned not null," + "SUM_SELECT_RANGE_CHECK BIGINT unsigned not null," + "SUM_SELECT_SCAN BIGINT unsigned not null," + "SUM_SORT_MERGE_PASSES BIGINT unsigned not null," + "SUM_SORT_RANGE BIGINT unsigned not null," + "SUM_SORT_ROWS BIGINT unsigned not null," + "SUM_SORT_SCAN BIGINT unsigned not null," + "SUM_NO_INDEX_USED BIGINT unsigned not null," + "SUM_NO_GOOD_INDEX_USED BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + 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: /* 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..d4e34b7c --- /dev/null +++ b/storage/perfschema/table_esms_global_by_event_name.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..f951be21 --- /dev/null +++ b/storage/perfschema/table_ets_by_account_by_event_name.cc @@ -0,0 +1,225 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "HOST CHAR(60) collate utf8_bin default null," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "COUNT_READ_WRITE BIGINT unsigned not null," + "SUM_TIMER_READ_WRITE BIGINT unsigned not null," + "MIN_TIMER_READ_WRITE BIGINT unsigned not null," + "AVG_TIMER_READ_WRITE BIGINT unsigned not null," + "MAX_TIMER_READ_WRITE BIGINT unsigned not null," + "COUNT_READ_ONLY BIGINT unsigned not null," + "SUM_TIMER_READ_ONLY BIGINT unsigned not null," + "MIN_TIMER_READ_ONLY BIGINT unsigned not null," + "AVG_TIMER_READ_ONLY BIGINT unsigned not null," + "MAX_TIMER_READ_ONLY BIGINT unsigned not null)")}, + false /* perpetual */ +}; + +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 */ + DBUG_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..98fb45f7 --- /dev/null +++ b/storage/perfschema/table_ets_by_account_by_event_name.h @@ -0,0 +1,134 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..f0293624 --- /dev/null +++ b/storage/perfschema/table_ets_by_host_by_event_name.cc @@ -0,0 +1,226 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "COUNT_READ_WRITE BIGINT unsigned not null," + "SUM_TIMER_READ_WRITE BIGINT unsigned not null," + "MIN_TIMER_READ_WRITE BIGINT unsigned not null," + "AVG_TIMER_READ_WRITE BIGINT unsigned not null," + "MAX_TIMER_READ_WRITE BIGINT unsigned not null," + "COUNT_READ_ONLY BIGINT unsigned not null," + "SUM_TIMER_READ_ONLY BIGINT unsigned not null," + "MIN_TIMER_READ_ONLY BIGINT unsigned not null," + "AVG_TIMER_READ_ONLY BIGINT unsigned not null," + "MAX_TIMER_READ_ONLY BIGINT unsigned not null)")}, + false /* perpetual */ +}; + +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 */ + DBUG_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..341c3878 --- /dev/null +++ b/storage/perfschema/table_ets_by_host_by_event_name.h @@ -0,0 +1,134 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..81ebf23b --- /dev/null +++ b/storage/perfschema/table_ets_by_thread_by_event_name.cc @@ -0,0 +1,218 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "COUNT_READ_WRITE BIGINT unsigned not null," + "SUM_TIMER_READ_WRITE BIGINT unsigned not null," + "MIN_TIMER_READ_WRITE BIGINT unsigned not null," + "AVG_TIMER_READ_WRITE BIGINT unsigned not null," + "MAX_TIMER_READ_WRITE BIGINT unsigned not null," + "COUNT_READ_ONLY BIGINT unsigned not null," + "SUM_TIMER_READ_ONLY BIGINT unsigned not null," + "MIN_TIMER_READ_ONLY BIGINT unsigned not null," + "AVG_TIMER_READ_ONLY BIGINT unsigned not null," + "MAX_TIMER_READ_ONLY BIGINT unsigned not null)")}, + false /* perpetual */ +}; + +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 */ + 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: /* 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..ad63efa3 --- /dev/null +++ b/storage/perfschema/table_ets_by_thread_by_event_name.h @@ -0,0 +1,138 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..66aae431 --- /dev/null +++ b/storage/perfschema/table_ets_by_user_by_event_name.cc @@ -0,0 +1,225 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "COUNT_READ_WRITE BIGINT unsigned not null," + "SUM_TIMER_READ_WRITE BIGINT unsigned not null," + "MIN_TIMER_READ_WRITE BIGINT unsigned not null," + "AVG_TIMER_READ_WRITE BIGINT unsigned not null," + "MAX_TIMER_READ_WRITE BIGINT unsigned not null," + "COUNT_READ_ONLY BIGINT unsigned not null," + "SUM_TIMER_READ_ONLY BIGINT unsigned not null," + "MIN_TIMER_READ_ONLY BIGINT unsigned not null," + "AVG_TIMER_READ_ONLY BIGINT unsigned not null," + "MAX_TIMER_READ_ONLY BIGINT unsigned not null)")}, + false /* perpetual */ +}; + +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 */ + DBUG_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..c3a1d0b5 --- /dev/null +++ b/storage/perfschema/table_ets_by_user_by_event_name.h @@ -0,0 +1,134 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..6c13871a --- /dev/null +++ b/storage/perfschema/table_ets_global_by_event_name.cc @@ -0,0 +1,200 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "COUNT_READ_WRITE BIGINT unsigned not null," + "SUM_TIMER_READ_WRITE BIGINT unsigned not null," + "MIN_TIMER_READ_WRITE BIGINT unsigned not null," + "AVG_TIMER_READ_WRITE BIGINT unsigned not null," + "MAX_TIMER_READ_WRITE BIGINT unsigned not null," + "COUNT_READ_ONLY BIGINT unsigned not null," + "SUM_TIMER_READ_ONLY BIGINT unsigned not null," + "MIN_TIMER_READ_ONLY BIGINT unsigned not null," + "AVG_TIMER_READ_ONLY BIGINT unsigned not null," + "MAX_TIMER_READ_ONLY BIGINT unsigned not null)")}, + false /* perpetual */ +}; + +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 */ + 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: /* 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..47c8828c --- /dev/null +++ b/storage/perfschema/table_ets_global_by_event_name.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..cd6c79df --- /dev/null +++ b/storage/perfschema/table_events_stages.cc @@ -0,0 +1,541 @@ +/* Copyright (c) 2010, 2018, 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, + 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 +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," + "EVENT_ID BIGINT unsigned not null," + "END_EVENT_ID BIGINT unsigned," + "EVENT_NAME VARCHAR(128) not null," + "SOURCE VARCHAR(64)," + "TIMER_START BIGINT unsigned," + "TIMER_END BIGINT unsigned," + "TIMER_WAIT BIGINT unsigned," + "WORK_COMPLETED BIGINT unsigned," + "WORK_ESTIMATED BIGINT unsigned," + "NESTING_EVENT_ID BIGINT unsigned," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT'))") }, + false /* perpetual */ +}; + +THR_LOCK table_events_stages_history::m_table_lock; + +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," + "EVENT_ID BIGINT unsigned not null," + "END_EVENT_ID BIGINT unsigned," + "EVENT_NAME VARCHAR(128) not null," + "SOURCE VARCHAR(64)," + "TIMER_START BIGINT unsigned," + "TIMER_END BIGINT unsigned," + "TIMER_WAIT BIGINT unsigned," + "WORK_COMPLETED BIGINT unsigned," + "WORK_ESTIMATED BIGINT unsigned," + "NESTING_EVENT_ID BIGINT unsigned," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT'))") }, + false /* perpetual */ +}; + +THR_LOCK table_events_stages_history_long::m_table_lock; + +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," + "EVENT_ID BIGINT unsigned not null," + "END_EVENT_ID BIGINT unsigned," + "EVENT_NAME VARCHAR(128) not null," + "SOURCE VARCHAR(64)," + "TIMER_START BIGINT unsigned," + "TIMER_END BIGINT unsigned," + "TIMER_WAIT BIGINT unsigned," + "WORK_COMPLETED BIGINT unsigned," + "WORK_ESTIMATED BIGINT unsigned," + "NESTING_EVENT_ID BIGINT unsigned," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT'))") }, + false /* perpetual */ +}; + +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 */ + DBUG_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: + DBUG_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; + + DBUG_ASSERT(events_stages_history_per_thread != 0); + set_position(pos); + + DBUG_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..16e3fc96 --- /dev/null +++ b/storage/perfschema/table_events_stages.h @@ -0,0 +1,221 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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() + {} + + 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: + /** 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() + {} + +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: + /** 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() + {} + +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: + /** 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() + {} + +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..c3667f06 --- /dev/null +++ b/storage/perfschema/table_events_statements.cc @@ -0,0 +1,940 @@ +/* Copyright (c) 2010, 2018, 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, + 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 +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," + "EVENT_ID BIGINT unsigned not null," + "END_EVENT_ID BIGINT unsigned," + "EVENT_NAME VARCHAR(128) not null," + "SOURCE VARCHAR(64)," + "TIMER_START BIGINT unsigned," + "TIMER_END BIGINT unsigned," + "TIMER_WAIT BIGINT unsigned," + "LOCK_TIME bigint unsigned not null," + "SQL_TEXT LONGTEXT," + "DIGEST VARCHAR(32)," + "DIGEST_TEXT LONGTEXT," + "CURRENT_SCHEMA VARCHAR(64)," + "OBJECT_TYPE VARCHAR(64)," + "OBJECT_SCHEMA VARCHAR(64)," + "OBJECT_NAME VARCHAR(64)," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned," + "MYSQL_ERRNO INTEGER," + "RETURNED_SQLSTATE VARCHAR(5)," + "MESSAGE_TEXT VARCHAR(128)," + "ERRORS BIGINT unsigned not null," + "WARNINGS BIGINT unsigned not null," + "ROWS_AFFECTED BIGINT unsigned not null," + "ROWS_SENT BIGINT unsigned not null," + "ROWS_EXAMINED BIGINT unsigned not null," + "CREATED_TMP_DISK_TABLES BIGINT unsigned not null," + "CREATED_TMP_TABLES BIGINT unsigned not null," + "SELECT_FULL_JOIN BIGINT unsigned not null," + "SELECT_FULL_RANGE_JOIN BIGINT unsigned not null," + "SELECT_RANGE BIGINT unsigned not null," + "SELECT_RANGE_CHECK BIGINT unsigned not null," + "SELECT_SCAN BIGINT unsigned not null," + "SORT_MERGE_PASSES BIGINT unsigned not null," + "SORT_RANGE BIGINT unsigned not null," + "SORT_ROWS BIGINT unsigned not null," + "SORT_SCAN BIGINT unsigned not null," + "NO_INDEX_USED BIGINT unsigned not null," + "NO_GOOD_INDEX_USED BIGINT unsigned not null," + "NESTING_EVENT_ID BIGINT unsigned," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT')," + "NESTING_EVENT_LEVEL INT)") }, + false /* perpetual */ +}; + +THR_LOCK table_events_statements_history::m_table_lock; + +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," + "EVENT_ID BIGINT unsigned not null," + "END_EVENT_ID BIGINT unsigned," + "EVENT_NAME VARCHAR(128) not null," + "SOURCE VARCHAR(64)," + "TIMER_START BIGINT unsigned," + "TIMER_END BIGINT unsigned," + "TIMER_WAIT BIGINT unsigned," + "LOCK_TIME bigint unsigned not null," + "SQL_TEXT LONGTEXT," + "DIGEST VARCHAR(32)," + "DIGEST_TEXT LONGTEXT," + "CURRENT_SCHEMA VARCHAR(64)," + "OBJECT_TYPE VARCHAR(64)," + "OBJECT_SCHEMA VARCHAR(64)," + "OBJECT_NAME VARCHAR(64)," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned," + "MYSQL_ERRNO INTEGER," + "RETURNED_SQLSTATE VARCHAR(5)," + "MESSAGE_TEXT VARCHAR(128)," + "ERRORS BIGINT unsigned not null," + "WARNINGS BIGINT unsigned not null," + "ROWS_AFFECTED BIGINT unsigned not null," + "ROWS_SENT BIGINT unsigned not null," + "ROWS_EXAMINED BIGINT unsigned not null," + "CREATED_TMP_DISK_TABLES BIGINT unsigned not null," + "CREATED_TMP_TABLES BIGINT unsigned not null," + "SELECT_FULL_JOIN BIGINT unsigned not null," + "SELECT_FULL_RANGE_JOIN BIGINT unsigned not null," + "SELECT_RANGE BIGINT unsigned not null," + "SELECT_RANGE_CHECK BIGINT unsigned not null," + "SELECT_SCAN BIGINT unsigned not null," + "SORT_MERGE_PASSES BIGINT unsigned not null," + "SORT_RANGE BIGINT unsigned not null," + "SORT_ROWS BIGINT unsigned not null," + "SORT_SCAN BIGINT unsigned not null," + "NO_INDEX_USED BIGINT unsigned not null," + "NO_GOOD_INDEX_USED BIGINT unsigned not null," + "NESTING_EVENT_ID BIGINT unsigned," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT')," + "NESTING_EVENT_LEVEL INT)") }, + false /* perpetual */ +}; + +THR_LOCK table_events_statements_history_long::m_table_lock; + +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," + "EVENT_ID BIGINT unsigned not null," + "END_EVENT_ID BIGINT unsigned," + "EVENT_NAME VARCHAR(128) not null," + "SOURCE VARCHAR(64)," + "TIMER_START BIGINT unsigned," + "TIMER_END BIGINT unsigned," + "TIMER_WAIT BIGINT unsigned," + "LOCK_TIME bigint unsigned not null," + "SQL_TEXT LONGTEXT," + "DIGEST VARCHAR(32)," + "DIGEST_TEXT LONGTEXT," + "CURRENT_SCHEMA VARCHAR(64)," + "OBJECT_TYPE VARCHAR(64)," + "OBJECT_SCHEMA VARCHAR(64)," + "OBJECT_NAME VARCHAR(64)," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned," + "MYSQL_ERRNO INTEGER," + "RETURNED_SQLSTATE VARCHAR(5)," + "MESSAGE_TEXT VARCHAR(128)," + "ERRORS BIGINT unsigned not null," + "WARNINGS BIGINT unsigned not null," + "ROWS_AFFECTED BIGINT unsigned not null," + "ROWS_SENT BIGINT unsigned not null," + "ROWS_EXAMINED BIGINT unsigned not null," + "CREATED_TMP_DISK_TABLES BIGINT unsigned not null," + "CREATED_TMP_TABLES BIGINT unsigned not null," + "SELECT_FULL_JOIN BIGINT unsigned not null," + "SELECT_FULL_RANGE_JOIN BIGINT unsigned not null," + "SELECT_RANGE BIGINT unsigned not null," + "SELECT_RANGE_CHECK BIGINT unsigned not null," + "SELECT_SCAN BIGINT unsigned not null," + "SORT_MERGE_PASSES BIGINT unsigned not null," + "SORT_RANGE BIGINT unsigned not null," + "SORT_ROWS BIGINT unsigned not null," + "SORT_SCAN BIGINT unsigned not null," + "NO_INDEX_USED BIGINT unsigned not null," + "NO_GOOD_INDEX_USED BIGINT unsigned not null," + "NESTING_EVENT_ID BIGINT unsigned," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT')," + "NESTING_EVENT_LEVEL INT)") }, + false /* perpetual */ +}; + +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_class; + PFS_statement_class *klass= sanitize_statement_class(unsafe); + if (unlikely(klass == NULL)) + return; + + m_row.m_thread_internal_id= statement->m_thread_internal_id; + m_row.m_event_id= statement->m_event_id; + m_row.m_end_event_id= statement->m_end_event_id; + m_row.m_nesting_event_id= statement->m_nesting_event_id; + m_row.m_nesting_event_type= statement->m_nesting_event_type; + m_row.m_nesting_event_level= statement->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_timer_end; + } + + m_normalizer->to_pico(statement->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 */ + DBUG_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: + DBUG_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; + } + + DBUG_ASSERT(m_pos.m_index_2 < statement_stack_max); + + statement= &pfs_thread->m_statement_stack[m_pos.m_index_2]; + + if (statement->m_class != NULL) + { + 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_class != NULL) + { + 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; + + DBUG_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) + { + DBUG_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_class != NULL) + { + 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_class != NULL) + { + 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_class == NULL) + 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..02127cad --- /dev/null +++ b/storage/perfschema/table_events_statements.h @@ -0,0 +1,314 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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() + {} + + 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: + /** 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() + {} + +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: + /** 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() + {} + +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: + /** 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() + {} + +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..1d717979 --- /dev/null +++ b/storage/perfschema/table_events_transactions.cc @@ -0,0 +1,718 @@ +/* Copyright (c) 2010, 2018, 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, + 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 +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," + "EVENT_ID BIGINT unsigned not null," + "END_EVENT_ID BIGINT unsigned," + "EVENT_NAME VARCHAR(128) not null," + "STATE ENUM('ACTIVE', 'COMMITTED', 'ROLLED BACK')," + "TRX_ID BIGINT unsigned," + "GTID VARCHAR(64)," + "XID_FORMAT_ID INTEGER," + "XID_GTRID VARCHAR(130)," + "XID_BQUAL VARCHAR(130)," + "XA_STATE VARCHAR(64)," + "SOURCE VARCHAR(64)," + "TIMER_START BIGINT unsigned," + "TIMER_END BIGINT unsigned," + "TIMER_WAIT BIGINT unsigned," + "ACCESS_MODE ENUM('READ ONLY', 'READ WRITE')," + "ISOLATION_LEVEL VARCHAR(64)," + "AUTOCOMMIT ENUM('YES','NO') not null," + "NUMBER_OF_SAVEPOINTS BIGINT unsigned," + "NUMBER_OF_ROLLBACK_TO_SAVEPOINT BIGINT unsigned," + "NUMBER_OF_RELEASE_SAVEPOINT BIGINT unsigned," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned," + "NESTING_EVENT_ID BIGINT unsigned," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT'))")}, + false /* perpetual */ +}; + +THR_LOCK table_events_transactions_history::m_table_lock; + +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," + "EVENT_ID BIGINT unsigned not null," + "END_EVENT_ID BIGINT unsigned," + "EVENT_NAME VARCHAR(128) not null," + "STATE ENUM('ACTIVE', 'COMMITTED', 'ROLLED BACK')," + "TRX_ID BIGINT unsigned," + "GTID VARCHAR(64)," + "XID_FORMAT_ID INTEGER," + "XID_GTRID VARCHAR(130)," + "XID_BQUAL VARCHAR(130)," + "XA_STATE VARCHAR(64)," + "SOURCE VARCHAR(64)," + "TIMER_START BIGINT unsigned," + "TIMER_END BIGINT unsigned," + "TIMER_WAIT BIGINT unsigned," + "ACCESS_MODE ENUM('READ ONLY', 'READ WRITE')," + "ISOLATION_LEVEL VARCHAR(64)," + "AUTOCOMMIT ENUM('YES','NO') not null," + "NUMBER_OF_SAVEPOINTS BIGINT unsigned," + "NUMBER_OF_ROLLBACK_TO_SAVEPOINT BIGINT unsigned," + "NUMBER_OF_RELEASE_SAVEPOINT BIGINT unsigned," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned," + "NESTING_EVENT_ID BIGINT unsigned," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT'))")}, + false /* perpetual */ +}; + +THR_LOCK table_events_transactions_history_long::m_table_lock; + +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," + "EVENT_ID BIGINT unsigned not null," + "END_EVENT_ID BIGINT unsigned," + "EVENT_NAME VARCHAR(128) not null," + "STATE ENUM('ACTIVE', 'COMMITTED', 'ROLLED BACK')," + "TRX_ID BIGINT unsigned," + "GTID VARCHAR(64)," + "XID_FORMAT_ID INTEGER," + "XID_GTRID VARCHAR(130)," + "XID_BQUAL VARCHAR(130)," + "XA_STATE VARCHAR(64)," + "SOURCE VARCHAR(64)," + "TIMER_START BIGINT unsigned," + "TIMER_END BIGINT unsigned," + "TIMER_WAIT BIGINT unsigned," + "ACCESS_MODE ENUM('READ ONLY', 'READ WRITE')," + "ISOLATION_LEVEL VARCHAR(64)," + "AUTOCOMMIT ENUM('YES','NO') not null," + "NUMBER_OF_SAVEPOINTS BIGINT unsigned," + "NUMBER_OF_ROLLBACK_TO_SAVEPOINT BIGINT unsigned," + "NUMBER_OF_RELEASE_SAVEPOINT BIGINT unsigned," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned," + "NESTING_EVENT_ID BIGINT unsigned," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT'))")}, + false /* perpetual */ +}; + +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) +{ + DBUG_ASSERT(buf_len >= XID_BUFFER_SIZE); + DBUG_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) +{ + DBUG_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 */ + DBUG_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: + DBUG_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; + + DBUG_ASSERT(events_transactions_history_per_thread != 0); + set_position(pos); + + DBUG_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..9987319a --- /dev/null +++ b/storage/perfschema/table_events_transactions.h @@ -0,0 +1,254 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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: + /** 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: + /** 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..aa73344f --- /dev/null +++ b/storage/perfschema/table_events_waits.cc @@ -0,0 +1,1143 @@ +/* Copyright (c) 2008, 2018, 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, + 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 +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," + "EVENT_ID BIGINT unsigned not null," + "END_EVENT_ID BIGINT unsigned," + "EVENT_NAME VARCHAR(128) not null," + "SOURCE VARCHAR(64)," + "TIMER_START BIGINT unsigned," + "TIMER_END BIGINT unsigned," + "TIMER_WAIT BIGINT unsigned," + "SPINS INTEGER unsigned," + "OBJECT_SCHEMA VARCHAR(64)," + "OBJECT_NAME VARCHAR(512)," + "INDEX_NAME VARCHAR(64)," + "OBJECT_TYPE VARCHAR(64)," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null," + "NESTING_EVENT_ID BIGINT unsigned," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT')," + "OPERATION VARCHAR(32) not null," + "NUMBER_OF_BYTES BIGINT," + "FLAGS INTEGER unsigned)") }, + false /* perpetual */ +}; + +THR_LOCK table_events_waits_history::m_table_lock; + +PFS_engine_table_share +table_events_waits_history::m_share= +{ + { C_STRING_WITH_LEN("events_waits_history") }, + &pfs_truncatable_acl, + table_events_waits_history::create, + NULL, /* write_row */ + table_events_waits_history::delete_all_rows, + 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," + "EVENT_ID BIGINT unsigned not null," + "END_EVENT_ID BIGINT unsigned," + "EVENT_NAME VARCHAR(128) not null," + "SOURCE VARCHAR(64)," + "TIMER_START BIGINT unsigned," + "TIMER_END BIGINT unsigned," + "TIMER_WAIT BIGINT unsigned," + "SPINS INTEGER unsigned," + "OBJECT_SCHEMA VARCHAR(64)," + "OBJECT_NAME VARCHAR(512)," + "INDEX_NAME VARCHAR(64)," + "OBJECT_TYPE VARCHAR(64)," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null," + "NESTING_EVENT_ID BIGINT unsigned," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT')," + "OPERATION VARCHAR(32) not null," + "NUMBER_OF_BYTES BIGINT," + "FLAGS INTEGER unsigned)") }, + false /* perpetual */ +}; + +THR_LOCK table_events_waits_history_long::m_table_lock; + +PFS_engine_table_share +table_events_waits_history_long::m_share= +{ + { C_STRING_WITH_LEN("events_waits_history_long") }, + &pfs_truncatable_acl, + table_events_waits_history_long::create, + NULL, /* write_row */ + table_events_waits_history_long::delete_all_rows, + 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," + "EVENT_ID BIGINT unsigned not null," + "END_EVENT_ID BIGINT unsigned," + "EVENT_NAME VARCHAR(128) not null," + "SOURCE VARCHAR(64)," + "TIMER_START BIGINT unsigned," + "TIMER_END BIGINT unsigned," + "TIMER_WAIT BIGINT unsigned," + "SPINS INTEGER unsigned," + "OBJECT_SCHEMA VARCHAR(64)," + "OBJECT_NAME VARCHAR(512)," + "INDEX_NAME VARCHAR(64)," + "OBJECT_TYPE VARCHAR(64)," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null," + "NESTING_EVENT_ID BIGINT unsigned," + "NESTING_EVENT_TYPE ENUM('TRANSACTION', 'STATEMENT', 'STAGE', 'WAIT')," + "OPERATION VARCHAR(32) not null," + "NUMBER_OF_BYTES BIGINT," + "FLAGS INTEGER unsigned)") }, + false /* perpetual */ +}; + +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 */ + DBUG_ASSERT(table->s->null_bytes == 2); + buf[0]= 0; + buf[1]= 0; + + /* + Some columns are unreliable, because they are joined with other buffers, + which could have changed and been reused for something else. + These columns are: + - THREAD_ID (m_thread joins with PFS_thread), + - SCHEMA_NAME (m_schema_name joins with PFS_table_share) + - OBJECT_NAME (m_object_name joins with PFS_table_share) + */ + for (; (f= *fields) ; fields++) + { + if (read_all || bitmap_is_set(table->read_set, f->field_index)) + { + switch(f->field_index) + { + case 0: /* THREAD_ID */ + set_field_ulonglong(f, m_row.m_thread_internal_id); + break; + case 1: /* EVENT_ID */ + set_field_ulonglong(f, m_row.m_event_id); + break; + case 2: /* END_EVENT_ID */ + if (m_row.m_end_event_id > 0) + set_field_ulonglong(f, m_row.m_end_event_id - 1); + else + f->set_null(); + break; + case 3: /* EVENT_NAME */ + set_field_varchar_utf8(f, m_row.m_name, m_row.m_name_length); + break; + case 4: /* SOURCE */ + set_field_varchar_utf8(f, m_row.m_source, m_row.m_source_length); + break; + case 5: /* TIMER_START */ + if (m_row.m_timer_start != 0) + set_field_ulonglong(f, m_row.m_timer_start); + else + f->set_null(); + break; + case 6: /* TIMER_END */ + if (m_row.m_timer_end != 0) + set_field_ulonglong(f, m_row.m_timer_end); + else + f->set_null(); + break; + case 7: /* TIMER_WAIT */ + if (m_row.m_timer_wait != 0) + set_field_ulonglong(f, m_row.m_timer_wait); + else + f->set_null(); + break; + case 8: /* SPINS */ + f->set_null(); + break; + case 9: /* OBJECT_SCHEMA */ + if (m_row.m_object_schema_length > 0) + { + set_field_varchar_utf8(f, m_row.m_object_schema, + m_row.m_object_schema_length); + } + else + f->set_null(); + break; + case 10: /* OBJECT_NAME */ + if (m_row.m_object_name_length > 0) + { + set_field_varchar_utf8(f, m_row.m_object_name, + m_row.m_object_name_length); + } + else + f->set_null(); + break; + case 11: /* INDEX_NAME */ + if (m_row.m_index_name_length > 0) + { + set_field_varchar_utf8(f, m_row.m_index_name, + m_row.m_index_name_length); + } + else + f->set_null(); + break; + case 12: /* OBJECT_TYPE */ + if (m_row.m_object_type_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: + DBUG_ASSERT(false); + } + } + } + return 0; +} + +PFS_engine_table* table_events_waits_current::create(void) +{ + return new table_events_waits_current(); +} + +table_events_waits_current::table_events_waits_current() + : table_events_waits_common(&m_share, &m_pos), + m_pos(), m_next_pos() +{} + +void table_events_waits_current::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_events_waits_current::rnd_next(void) +{ + PFS_thread *pfs_thread; + PFS_events_waits *wait; + 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 + + DBUG_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; + + DBUG_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) + { + DBUG_ASSERT(m_pos.m_index_2 < events_waits_history_per_thread); + + if ( ! pfs_thread->m_waits_history_full && + (m_pos.m_index_2 >= pfs_thread->m_waits_history_index)) + return HA_ERR_RECORD_DELETED; + + wait= &pfs_thread->m_waits_history[m_pos.m_index_2]; + + if (wait->m_wait_class != NO_WAIT_CLASS) + { + 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..9173ec30 --- /dev/null +++ b/storage/perfschema/table_events_waits.h @@ -0,0 +1,267 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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() + {} + + 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: + /** 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() + {} + +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: + /** 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() + {} + +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: + /** 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() + {} + +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..71ae6eae --- /dev/null +++ b/storage/perfschema/table_events_waits_summary.cc @@ -0,0 +1,236 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + 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: /* 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: + DBUG_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..51d8a62e --- /dev/null +++ b/storage/perfschema/table_events_waits_summary.h @@ -0,0 +1,97 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..05c874ea --- /dev/null +++ b/storage/perfschema/table_ews_by_account_by_event_name.cc @@ -0,0 +1,271 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "HOST CHAR(" HOSTNAME_LENGTH_STR ") collate utf8_bin default null," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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; + DBUG_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; + DBUG_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 */ + DBUG_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..e64a61b2 --- /dev/null +++ b/storage/perfschema/table_ews_by_account_by_event_name.h @@ -0,0 +1,139 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..129709b8 --- /dev/null +++ b/storage/perfschema/table_ews_by_host_by_event_name.cc @@ -0,0 +1,273 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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; + DBUG_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; + DBUG_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 */ + DBUG_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..074ec0be --- /dev/null +++ b/storage/perfschema/table_ews_by_host_by_event_name.h @@ -0,0 +1,139 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..11b8a84d --- /dev/null +++ b/storage/perfschema/table_ews_by_thread_by_event_name.cc @@ -0,0 +1,279 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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: + DBUG_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: + DBUG_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 */ + 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: /* 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..d3850e93 --- /dev/null +++ b/storage/perfschema/table_ews_by_thread_by_event_name.h @@ -0,0 +1,138 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..5425d773 --- /dev/null +++ b/storage/perfschema/table_ews_by_user_by_event_name.cc @@ -0,0 +1,272 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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; + DBUG_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; + DBUG_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 */ + DBUG_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..b088c8e2 --- /dev/null +++ b/storage/perfschema/table_ews_by_user_by_event_name.h @@ -0,0 +1,139 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..13839ddb --- /dev/null +++ b/storage/perfschema/table_ews_global_by_event_name.cc @@ -0,0 +1,437 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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: + DBUG_ASSERT(m_pos.m_index_2 >= 1); + DBUG_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: + DBUG_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 */ + 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: /* 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..a9deb97a --- /dev/null +++ b/storage/perfschema/table_ews_global_by_event_name.h @@ -0,0 +1,135 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..ca77b606 --- /dev/null +++ b/storage/perfschema/table_file_instances.cc @@ -0,0 +1,174 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "OPEN_COUNT INTEGER unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + 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: /* 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: + DBUG_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..cb3d39d4 --- /dev/null +++ b/storage/perfschema/table_file_instances.h @@ -0,0 +1,96 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..404b5818 --- /dev/null +++ b/storage/perfschema/table_file_summary_by_event_name.cc @@ -0,0 +1,259 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "COUNT_READ BIGINT unsigned not null," + "SUM_TIMER_READ BIGINT unsigned not null," + "MIN_TIMER_READ BIGINT unsigned not null," + "AVG_TIMER_READ BIGINT unsigned not null," + "MAX_TIMER_READ BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_READ BIGINT not null," + "COUNT_WRITE BIGINT unsigned not null," + "SUM_TIMER_WRITE BIGINT unsigned not null," + "MIN_TIMER_WRITE BIGINT unsigned not null," + "AVG_TIMER_WRITE BIGINT unsigned not null," + "MAX_TIMER_WRITE BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_WRITE BIGINT not null," + "COUNT_MISC BIGINT unsigned not null," + "SUM_TIMER_MISC BIGINT unsigned not null," + "MIN_TIMER_MISC BIGINT unsigned not null," + "AVG_TIMER_MISC BIGINT unsigned not null," + "MAX_TIMER_MISC BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + 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: /* 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: + DBUG_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..ae2124fe --- /dev/null +++ b/storage/perfschema/table_file_summary_by_event_name.h @@ -0,0 +1,97 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..b05d1f24 --- /dev/null +++ b/storage/perfschema/table_file_summary_by_instance.cc @@ -0,0 +1,279 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "COUNT_READ BIGINT unsigned not null," + "SUM_TIMER_READ BIGINT unsigned not null," + "MIN_TIMER_READ BIGINT unsigned not null," + "AVG_TIMER_READ BIGINT unsigned not null," + "MAX_TIMER_READ BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_READ BIGINT not null," + "COUNT_WRITE BIGINT unsigned not null," + "SUM_TIMER_WRITE BIGINT unsigned not null," + "MIN_TIMER_WRITE BIGINT unsigned not null," + "AVG_TIMER_WRITE BIGINT unsigned not null," + "MAX_TIMER_WRITE BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_WRITE BIGINT not null," + "COUNT_MISC BIGINT unsigned not null," + "SUM_TIMER_MISC BIGINT unsigned not null," + "MIN_TIMER_MISC BIGINT unsigned not null," + "AVG_TIMER_MISC BIGINT unsigned not null," + "MAX_TIMER_MISC BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + 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: /* 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: + DBUG_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..7ea90bac --- /dev/null +++ b/storage/perfschema/table_file_summary_by_instance.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..a8d5bacf --- /dev/null +++ b/storage/perfschema/table_global_status.cc @@ -0,0 +1,190 @@ +/* Copyright (c) 2015, 2016, 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 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 +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," + "VARIABLE_VALUE VARCHAR(1024))") }, + true /* perpetual */ +}; + +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 */ + DBUG_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: + DBUG_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..a97ea9d0 --- /dev/null +++ b/storage/perfschema/table_global_status.h @@ -0,0 +1,118 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..117e06e3 --- /dev/null +++ b/storage/perfschema/table_global_variables.cc @@ -0,0 +1,184 @@ +/* Copyright (c) 2015, 2018, 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 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 +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 /* perpetual */ +}; + +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); + DBUG_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 */ + DBUG_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: + DBUG_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..b9124e76 --- /dev/null +++ b/storage/perfschema/table_global_variables.h @@ -0,0 +1,117 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..55b13971 --- /dev/null +++ b/storage/perfschema/table_helper.cc @@ -0,0 +1,896 @@ +/* Copyright (c) 2010, 2016, 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 */ + +/** + @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: + DBUG_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: + DBUG_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: + DBUG_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: + DBUG_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: + DBUG_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: + DBUG_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: + DBUG_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: + DBUG_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: + DBUG_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: + DBUG_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: + DBUG_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: + DBUG_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: + DBUG_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: + DBUG_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: + DBUG_ASSERT(false); + } +} + +void PFS_variable_name_row::make_row(const char* str, size_t length) +{ + DBUG_ASSERT(length <= sizeof(m_str)); + DBUG_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) +{ + DBUG_ASSERT(cs != NULL); + DBUG_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..3ce74648 --- /dev/null +++ b/storage/perfschema/table_helper.h @@ -0,0 +1,702 @@ +/* Copyright (c) 2008, 2016, 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 */ + +#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: + DBUG_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..e482ead2 --- /dev/null +++ b/storage/perfschema/table_host_cache.cc @@ -0,0 +1,372 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "HOST VARCHAR(255) collate utf8_bin," + "HOST_VALIDATED ENUM ('YES', 'NO') not null," + "SUM_CONNECT_ERRORS BIGINT not null," + "COUNT_HOST_BLOCKED_ERRORS BIGINT not null," + "COUNT_NAMEINFO_TRANSIENT_ERRORS BIGINT not null," + "COUNT_NAMEINFO_PERMANENT_ERRORS BIGINT not null," + "COUNT_FORMAT_ERRORS BIGINT not null," + "COUNT_ADDRINFO_TRANSIENT_ERRORS BIGINT not null," + "COUNT_ADDRINFO_PERMANENT_ERRORS BIGINT not null," + "COUNT_FCRDNS_ERRORS BIGINT not null," + "COUNT_HOST_ACL_ERRORS BIGINT not null," + "COUNT_NO_AUTH_PLUGIN_ERRORS BIGINT not null," + "COUNT_AUTH_PLUGIN_ERRORS BIGINT not null," + "COUNT_HANDSHAKE_ERRORS BIGINT not null," + "COUNT_PROXY_USER_ERRORS BIGINT not null," + "COUNT_PROXY_USER_ACL_ERRORS BIGINT not null," + "COUNT_AUTHENTICATION_ERRORS BIGINT not null," + "COUNT_SSL_ERRORS BIGINT not null," + "COUNT_MAX_USER_CONNECTIONS_ERRORS BIGINT not null," + "COUNT_MAX_USER_CONNECTIONS_PER_HOUR_ERRORS BIGINT not null," + "COUNT_DEFAULT_DATABASE_ERRORS BIGINT not null," + "COUNT_INIT_CONNECT_ERRORS BIGINT not null," + "COUNT_LOCAL_ERRORS BIGINT not null," + "COUNT_UNKNOWN_ERRORS BIGINT not null," + "FIRST_SEEN TIMESTAMP(0) NOT NULL default 0," + "LAST_SEEN TIMESTAMP(0) NOT NULL default 0," + "FIRST_ERROR_SEEN TIMESTAMP(0) null default 0," + "LAST_ERROR_SEEN TIMESTAMP(0) null default 0)") }, + false /* perpetual */ +}; + +PFS_engine_table* table_host_cache::create(void) +{ + table_host_cache *t= new table_host_cache(); + if (t != NULL) + { + THD *thd= current_thd; + DBUG_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; + + DBUG_ASSERT(m_all_rows == NULL); + DBUG_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); + DBUG_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; + + DBUG_ASSERT(m_row); + + /* Set the null bits */ + DBUG_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: + DBUG_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..57cd0fa0 --- /dev/null +++ b/storage/perfschema/table_host_cache.h @@ -0,0 +1,150 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..8e71bfc6 --- /dev/null +++ b/storage/perfschema/table_hosts.cc @@ -0,0 +1,149 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "CURRENT_CONNECTIONS bigint not null," + "TOTAL_CONNECTIONS bigint not null)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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: + DBUG_ASSERT(false); + } + } + } + return 0; +} + diff --git a/storage/perfschema/table_hosts.h b/storage/perfschema/table_hosts.h new file mode 100644 index 00000000..61c04d12 --- /dev/null +++ b/storage/perfschema/table_hosts.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..9f78750e --- /dev/null +++ b/storage/perfschema/table_md_locks.cc @@ -0,0 +1,207 @@ +/* Copyright (c) 2012, 2018, 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, + 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 +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," + "OBJECT_SCHEMA VARCHAR(64)," + "OBJECT_NAME VARCHAR(64)," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null," + "LOCK_TYPE VARCHAR(32) not null," + "LOCK_DURATION VARCHAR(32) not null," + "LOCK_STATUS VARCHAR(32) not null," + "SOURCE VARCHAR(64)," + "OWNER_THREAD_ID BIGINT unsigned," + "OWNER_EVENT_ID BIGINT unsigned)")}, + false /* perpetual */ +}; + +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 */ + DBUG_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: + DBUG_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..40b6d2f6 --- /dev/null +++ b/storage/perfschema/table_md_locks.h @@ -0,0 +1,106 @@ +/* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..5c83c225 --- /dev/null +++ b/storage/perfschema/table_mems_by_account_by_event_name.cc @@ -0,0 +1,221 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "HOST CHAR(60) collate utf8_bin default null," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_ALLOC BIGINT unsigned not null," + "COUNT_FREE BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_ALLOC BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_FREE BIGINT unsigned not null," + "LOW_COUNT_USED BIGINT not null," + "CURRENT_COUNT_USED BIGINT not null," + "HIGH_COUNT_USED BIGINT not null," + "LOW_NUMBER_OF_BYTES_USED BIGINT not null," + "CURRENT_NUMBER_OF_BYTES_USED BIGINT not null," + "HIGH_NUMBER_OF_BYTES_USED BIGINT not null)")}, + false /* perpetual */ +}; + +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 */ + DBUG_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..b6151246 --- /dev/null +++ b/storage/perfschema/table_mems_by_account_by_event_name.h @@ -0,0 +1,129 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..0921dfa4 --- /dev/null +++ b/storage/perfschema/table_mems_by_host_by_event_name.cc @@ -0,0 +1,221 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_ALLOC BIGINT unsigned not null," + "COUNT_FREE BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_ALLOC BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_FREE BIGINT unsigned not null," + "LOW_COUNT_USED BIGINT not null," + "CURRENT_COUNT_USED BIGINT not null," + "HIGH_COUNT_USED BIGINT not null," + "LOW_NUMBER_OF_BYTES_USED BIGINT not null," + "CURRENT_NUMBER_OF_BYTES_USED BIGINT not null," + "HIGH_NUMBER_OF_BYTES_USED BIGINT not null)")}, + false /* perpetual */ +}; + +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 */ + DBUG_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..1a55db2c --- /dev/null +++ b/storage/perfschema/table_mems_by_host_by_event_name.h @@ -0,0 +1,129 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..9e36cb76 --- /dev/null +++ b/storage/perfschema/table_mems_by_thread_by_event_name.cc @@ -0,0 +1,214 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_ALLOC BIGINT unsigned not null," + "COUNT_FREE BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_ALLOC BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_FREE BIGINT unsigned not null," + "LOW_COUNT_USED BIGINT not null," + "CURRENT_COUNT_USED BIGINT not null," + "HIGH_COUNT_USED BIGINT not null," + "LOW_NUMBER_OF_BYTES_USED BIGINT not null," + "CURRENT_NUMBER_OF_BYTES_USED BIGINT not null," + "HIGH_NUMBER_OF_BYTES_USED BIGINT not null)")}, + false /* perpetual */ +}; + +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 */ + 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: /* 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..f8608198 --- /dev/null +++ b/storage/perfschema/table_mems_by_thread_by_event_name.h @@ -0,0 +1,129 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..5d5ee89d --- /dev/null +++ b/storage/perfschema/table_mems_by_user_by_event_name.cc @@ -0,0 +1,221 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "EVENT_NAME VARCHAR(128) not null," + "COUNT_ALLOC BIGINT unsigned not null," + "COUNT_FREE BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_ALLOC BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_FREE BIGINT unsigned not null," + "LOW_COUNT_USED BIGINT not null," + "CURRENT_COUNT_USED BIGINT not null," + "HIGH_COUNT_USED BIGINT not null," + "LOW_NUMBER_OF_BYTES_USED BIGINT not null," + "CURRENT_NUMBER_OF_BYTES_USED BIGINT not null," + "HIGH_NUMBER_OF_BYTES_USED BIGINT not null)")}, + false /* perpetual */ +}; + +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 */ + DBUG_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..2791e1d3 --- /dev/null +++ b/storage/perfschema/table_mems_by_user_by_event_name.h @@ -0,0 +1,129 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..93496e38 --- /dev/null +++ b/storage/perfschema/table_mems_global_by_event_name.cc @@ -0,0 +1,241 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "COUNT_ALLOC BIGINT unsigned not null," + "COUNT_FREE BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_ALLOC BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_FREE BIGINT unsigned not null," + "LOW_COUNT_USED BIGINT not null," + "CURRENT_COUNT_USED BIGINT not null," + "HIGH_COUNT_USED BIGINT not null," + "LOW_NUMBER_OF_BYTES_USED BIGINT not null," + "CURRENT_NUMBER_OF_BYTES_USED BIGINT not null," + "HIGH_NUMBER_OF_BYTES_USED BIGINT not null)")}, + false /* perpetual */ +}; + +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 */ + 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: /* 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..14a54142 --- /dev/null +++ b/storage/perfschema/table_mems_global_by_event_name.h @@ -0,0 +1,132 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..f693a0f1 --- /dev/null +++ b/storage/perfschema/table_os_global_by_type.cc @@ -0,0 +1,303 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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)," + "OBJECT_SCHEMA VARCHAR(64)," + "OBJECT_NAME VARCHAR(64)," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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: + DBUG_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..37e6db1e --- /dev/null +++ b/storage/perfschema/table_os_global_by_type.h @@ -0,0 +1,131 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..1c1d9c5a --- /dev/null +++ b/storage/perfschema/table_performance_timers.cc @@ -0,0 +1,177 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "TIMER_FREQUENCY BIGINT," + "TIMER_RESOLUTION BIGINT," + "TIMER_OVERHEAD BIGINT)") }, + false /* perpetual */ +}; + +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); + DBUG_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; + + DBUG_ASSERT(m_row); + + /* Set the null bits */ + DBUG_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: + DBUG_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..fe5f9cd5 --- /dev/null +++ b/storage/perfschema/table_performance_timers.h @@ -0,0 +1,92 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..6e93523d --- /dev/null +++ b/storage/perfschema/table_prepared_stmt_instances.cc @@ -0,0 +1,294 @@ +/* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "STATEMENT_ID bigint(20) unsigned NOT NULL," + "STATEMENT_NAME varchar(64) default NULL," + "SQL_TEXT longtext NOT NULL," + "OWNER_THREAD_ID bigint(20) unsigned NOT NULL," + "OWNER_EVENT_ID bigint(20) unsigned NOT NULL," + "OWNER_OBJECT_TYPE enum('EVENT','FUNCTION','PROCEDURE','TABLE','TRIGGER') DEFAULT NULL," + "OWNER_OBJECT_SCHEMA varchar(64) DEFAULT NULL," + "OWNER_OBJECT_NAME varchar(64) DEFAULT NULL," + "TIMER_PREPARE bigint(20) unsigned NOT NULL," + "COUNT_REPREPARE bigint(20) unsigned NOT NULL," + "COUNT_EXECUTE bigint(20) unsigned NOT NULL," + "SUM_TIMER_EXECUTE bigint(20) unsigned NOT NULL," + "MIN_TIMER_EXECUTE bigint(20) unsigned NOT NULL," + "AVG_TIMER_EXECUTE bigint(20) unsigned NOT NULL," + "MAX_TIMER_EXECUTE bigint(20) unsigned NOT NULL," + "SUM_LOCK_TIME bigint(20) unsigned NOT NULL," + "SUM_ERRORS bigint(20) unsigned NOT NULL," + "SUM_WARNINGS bigint(20) unsigned NOT NULL," + "SUM_ROWS_AFFECTED bigint(20) unsigned NOT NULL," + "SUM_ROWS_SENT bigint(20) unsigned NOT NULL," + "SUM_ROWS_EXAMINED bigint(20) unsigned NOT NULL," + "SUM_CREATED_TMP_DISK_TABLES bigint(20) unsigned NOT NULL," + "SUM_CREATED_TMP_TABLES bigint(20) unsigned NOT NULL," + "SUM_SELECT_FULL_JOIN bigint(20) unsigned NOT NULL," + "SUM_SELECT_FULL_RANGE_JOIN bigint(20) unsigned NOT NULL," + "SUM_SELECT_RANGE bigint(20) unsigned NOT NULL," + "SUM_SELECT_RANGE_CHECK bigint(20) unsigned NOT NULL," + "SUM_SELECT_SCAN bigint(20) unsigned NOT NULL," + "SUM_SORT_MERGE_PASSES bigint(20) unsigned NOT NULL," + "SUM_SORT_RANGE bigint(20) unsigned NOT NULL," + "SUM_SORT_ROWS bigint(20) unsigned NOT NULL," + "SUM_SORT_SCAN bigint(20) unsigned NOT NULL," + "SUM_NO_INDEX_USED bigint(20) unsigned NOT NULL," + "SUM_NO_GOOD_INDEX_USED bigint(20) unsigned NOT NULL)")}, + false /* perpetual */ +}; + +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. + */ + DBUG_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..a17184d4 --- /dev/null +++ b/storage/perfschema/table_prepared_stmt_instances.h @@ -0,0 +1,132 @@ +/* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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_replication_applier_configuration.cc b/storage/perfschema/table_replication_applier_configuration.cc new file mode 100644 index 00000000..06de6c6e --- /dev/null +++ b/storage/perfschema/table_replication_applier_configuration.cc @@ -0,0 +1,194 @@ +/* + Copyright (c) 2013, 2018, 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 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" +#include "table_replication_applier_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 replication */ + +#ifdef HAVE_REPLICATION +THR_LOCK table_replication_applier_configuration::m_table_lock; + +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 CHAR(64) collate utf8_general_ci not null," + "DESIRED_DELAY INTEGER not null)") }, + false /* perpetual */ +}; + +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= 0; //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; + + /* + Note: + There are no NULL columns in this table, + so there are no null bits reserved for NULL flags per column. + There are no VARCHAR columns either, so the record is not + in HA_OPTION_PACK_RECORD format as most other performance_schema tables. + When HA_OPTION_PACK_RECORD is not set, + the table record reserves an extra null byte, see open_binary_frm(). + */ + + DBUG_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: /** desired_delay */ + set_field_ulong(f, static_cast<ulong>(m_row.desired_delay)); + break; + default: + DBUG_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..18321ac0 --- /dev/null +++ b/storage/perfschema/table_replication_applier_configuration.h @@ -0,0 +1,107 @@ +/* + Copyright (c) 2013, 2018, 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 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" +//#include "rpl_msr.h" +//#include "rpl_info.h" /*CHANNEL_NAME_LENGTH*/ + +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(); + + /** 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..7083d3ac --- /dev/null +++ b/storage/perfschema/table_replication_applier_status.cc @@ -0,0 +1,222 @@ +/* + Copyright (c) 2013, 2018, 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 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 +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 CHAR(64) collate utf8_general_ci not null," + "SERVICE_STATE ENUM('ON','OFF') not null," + "REMAINING_DELAY INTEGER unsigned," + "COUNT_TRANSACTIONS_RETRIES BIGINT unsigned not null)") }, + false /* perpetual */ +}; + + +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->info_thd_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->info_thd_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 == stage_sql_thd_waiting_until_delay.m_name) + { + time_t t= my_time(0), sql_delay_end= 0; //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; + + DBUG_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: /* 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: + DBUG_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..95845f38 --- /dev/null +++ b/storage/perfschema/table_replication_applier_status.h @@ -0,0 +1,118 @@ +/* + Copyright (c) 2013, 2018, 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 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 "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 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(); + + /** 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..beb8620b --- /dev/null +++ b/storage/perfschema/table_replication_applier_status_by_coordinator.cc @@ -0,0 +1,249 @@ +/* + Copyright (c) 2013, 2018, 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 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 +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 CHAR(64) collate utf8_general_ci not null," + "THREAD_ID BIGINT UNSIGNED," + "SERVICE_STATE ENUM('ON','OFF') not null," + "LAST_ERROR_NUMBER INTEGER not null," + "LAST_ERROR_MESSAGE VARCHAR(1024) not null," + "LAST_ERROR_TIMESTAMP TIMESTAMP(0) not null)") }, + false /* perpetual */ +}; + +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); + + /* + Construct and display SQL Thread's (Coordinator) information in + 'replication_applier_status_by_coordinator' table only in the case of + multi threaded slave mode. Code should do nothing in the case of single + threaded slave mode. In case of single threaded slave mode SQL Thread's + status will be reported as part of + 'replication_applier_status_by_worker' table. + */ + if (mi && mi->host[0] && /*mi->rli.get_worker_count() > */ 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; + + DBUG_ASSERT(mi != NULL); + + 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); + + 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; + + 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= 0;//(ulonglong)mi->rli.last_error().skr*1000000; + } + + mysql_mutex_unlock(&mi->rli.err_lock); + 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; + + DBUG_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: /*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; + default: + DBUG_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..8c1d4c4f --- /dev/null +++ b/storage/perfschema/table_replication_applier_status_by_coordinator.h @@ -0,0 +1,124 @@ +/* + Copyright (c) 2013, 2018, 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 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 "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 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; +}; + +/** 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(); + + /** 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..4c7ca612 --- /dev/null +++ b/storage/perfschema/table_replication_applier_status_by_worker.cc @@ -0,0 +1,412 @@ +/* + Copyright (c) 2013, 2018, 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 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_info.h" +#include "rpl_rli.h" +#include "rpl_mi.h" +#include "sql_parse.h" +//#include "rpl_rli_pdb.h" +//#include "rpl_msr.h" /*Multi source replication */ + +THR_LOCK table_replication_applier_status_by_worker::m_table_lock; + +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 CHAR(64) collate utf8_general_ci not null," + "WORKER_ID BIGINT UNSIGNED not null," + "THREAD_ID BIGINT UNSIGNED," + "SERVICE_STATE ENUM('ON','OFF') not null," + "LAST_SEEN_TRANSACTION CHAR(57) not null," + "LAST_ERROR_NUMBER INTEGER not null," + "LAST_ERROR_MESSAGE VARCHAR(1024) not null," + "LAST_ERROR_TIMESTAMP TIMESTAMP(0) not null)") }, + false /* perpetual */ +}; + +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 an estimate, number of master info's multipled by worker threads + */ + return master_info_index->master_info_hash.records*32; +} + + +int table_replication_applier_status_by_worker::rnd_next(void) +{ + Slave_worker *worker; + Master_info *mi; + size_t wc; + + mysql_mutex_lock(&LOCK_active_mi); + + for (m_pos.set_at(&m_next_pos); + m_pos.has_more_channels(master_info_index->master_info_hash.records); + m_pos.next_channel()) + { + mi= (Master_info *)my_hash_element(&master_info_index->master_info_hash, m_pos.m_index_1); + + if (mi && mi->host[0]) + { + wc= mi->rli->get_worker_count(); + + if (wc == 0) + { + /* Single Thread Slave */ + make_row(mi); + m_next_pos.set_channel_after(&m_pos); + channel_map.unlock(); + return 0; + } + + for (; m_pos.m_index_2 < wc; m_pos.next_worker()) + { + /* Multi Thread Slave */ + + worker = mi->rli->get_worker(m_pos.m_index_2); + if (worker) + { + make_row(worker); + m_next_pos.set_after(&m_pos); + channel_map.unlock(); + return 0; + } + } + } + } + + mysql_mutex_unlock(&LOCK_active_mi); + return HA_ERR_END_OF_FILE; +} + +int table_replication_applier_status_by_worker::rnd_pos(const void *pos) +{ + Slave_worker *worker; + Master_info *mi; + int res= HA_ERR_RECORD_DELETED; + size_t wc; + + set_position(pos); + + mysql_mutex_lock(&LOCK_active_mi); + + mi= (Master_info *)my_hash_element(&master_info_index->master_info_hash, m_pos.m_index_1); + + if (!mi || !mi->host[0]) + goto end; + + wc = mi->rli->get_worker_count(); + + if (wc == 0) + { + /* Single Thread Slave */ + make_row(mi); + res=0; + } + else + { + /* Multi Thread Slave */ + if (m_pos.m_index_2 < wc) + { + worker = mi->rli->get_worker(m_pos.m_index_2); + if (worker != NULL) + { + make_row(worker); + res=0; + } + } + } + +end: + mysql_mutex_unlock(&LOCK_active_mi); + return res; +} + +/** + Function to display SQL Thread's status as part of + 'replication_applier_status_by_worker' in single threaded slave mode. + + @param[in] Master_info + + @retval void +*/ +void table_replication_applier_status_by_worker::make_row(Master_info *mi) +{ + m_row_exists= false; + + m_row.worker_id= 0; + + m_row.thread_id= 0; + + DBUG_ASSERT(mi != NULL); + DBUG_ASSERT(mi->rli != NULL); + + mysql_mutex_lock(&mi->rli->data_lock); + + m_row.channel_name_length= strlen(mi->get_channel()); + memcpy(m_row.channel_name, (char*)mi->get_channel(), m_row.channel_name_length); + + if (mi->rli->slave_running) + { + PSI_thread *psi= thd_get_psi(mi->rli->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; + } + 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 (mi->rli->currently_executing_gtid.type == GTID_GROUP) + { + global_sid_lock->rdlock(); + m_row.last_seen_transaction_length= + mi->rli->currently_executing_gtid.to_string(global_sid_map, + m_row.last_seen_transaction); + global_sid_lock->unlock(); + } + else if (mi->rli->currently_executing_gtid.type == ANONYMOUS_GROUP) + { + m_row.last_seen_transaction_length= + mi->rli->currently_executing_gtid.to_string((rpl_sid *)NULL, + m_row.last_seen_transaction); + } + else + { + /* + For SQL thread currently_executing_gtid, type is set to + AUTOMATIC_GROUP when the SQL thread is not executing any + transaction. For this case, the field should be empty. + */ + DBUG_ASSERT(mi->rli->currently_executing_gtid.type == AUTOMATIC_GROUP); + 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= 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); + mysql_mutex_unlock(&mi->rli->data_lock); + m_row_exists= true; +} + +void table_replication_applier_status_by_worker::make_row(Slave_worker *w) +{ + m_row_exists= false; + + m_row.worker_id= w->get_internal_id(); + + m_row.thread_id= 0; + + m_row.channel_name_length= strlen(w->get_channel()); + memcpy(m_row.channel_name, (char*)w->get_channel(), m_row.channel_name_length); + + mysql_mutex_lock(&w->jobs_lock); + if (w->running_status == Slave_worker::RUNNING) + { + PSI_thread *psi= thd_get_psi(w->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; + } + else /* no instrumentation found */ + m_row.thread_id_is_null= true; + } + else + m_row.thread_id_is_null= true; + + if (w->running_status == Slave_worker::RUNNING) + m_row.service_state= PS_RPL_YES; + else + m_row.service_state= PS_RPL_NO; + + m_row.last_error_number= (unsigned int) w->last_error().number; + + if (w->currently_executing_gtid.type == GTID_GROUP) + { + global_sid_lock->rdlock(); + m_row.last_seen_transaction_length= + w->currently_executing_gtid.to_string(global_sid_map, + m_row.last_seen_transaction); + global_sid_lock->unlock(); + } + else if (w->currently_executing_gtid.type == ANONYMOUS_GROUP) + { + m_row.last_seen_transaction_length= + w->currently_executing_gtid.to_string((rpl_sid *)NULL, + m_row.last_seen_transaction); + } + else + { + /* + For worker->currently_executing_gtid, type is set to + AUTOMATIC_GROUP when the worker is not executing any + transaction. For this case, the field should be empty. + */ + DBUG_ASSERT(w->currently_executing_gtid.type == AUTOMATIC_GROUP); + m_row.last_seen_transaction_length= 0; + memcpy(m_row.last_seen_transaction, "", 1); + } + + m_row.last_error_number= (unsigned int) w->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*)w->last_error().message; + m_row.last_error_message_length= strlen(temp_store); + memcpy(m_row.last_error_message, w->last_error().message, + m_row.last_error_message_length); + + /** time in millisecond since epoch */ + m_row.last_error_timestamp= (ulonglong)w->last_error().skr*1000000; + } + mysql_mutex_unlock(&w->jobs_lock); + + 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; + + DBUG_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: /*worker_id*/ + set_field_ulonglong(f, m_row.worker_id); + break; + case 2: /*thread_id*/ + if(m_row.thread_id_is_null) + f->set_null(); + else + set_field_ulonglong(f, m_row.thread_id); + break; + case 3: /*service_state*/ + set_field_enum(f, m_row.service_state); + break; + case 4: /*last_seen_transaction*/ + set_field_char_utf8(f, m_row.last_seen_transaction, m_row.last_seen_transaction_length); + break; + case 5: /*last_error_number*/ + set_field_ulong(f, m_row.last_error_number); + break; + case 6: /*last_error_message*/ + set_field_varchar_utf8(f, m_row.last_error_message, m_row.last_error_message_length); + break; + case 7: /*last_error_timestamp*/ + set_field_timestamp(f, m_row.last_error_timestamp); + break; + default: + DBUG_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..0a881dae --- /dev/null +++ b/storage/perfschema/table_replication_applier_status_by_worker.h @@ -0,0 +1,182 @@ +/* + Copyright (c) 2013, 2018, 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 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 "rpl_rli_pdb.h" +//#include "rpl_msr.h" +//#include "rpl_info.h" /*CHANNEL_NAME_LENGTH*/ +#include "my_thread.h" + +class Slave_worker; +class Master_info; + +/** + @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; + /* + worker_id is added to the table because thread is killed at STOP SLAVE + but the status needs to show up, so worker_id is used as a permanent + identifier. + */ + ulonglong worker_id; + 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; +}; + +/** + Position in table replication_applier_status_by_worker. + Index 1 for replication channel. + Index 2 for worker: + - position [0] is for Single Thread Slave (Master_info) + - position [1] .. [N] is for Multi Thread Slave (Slave_worker) +*/ +struct pos_replication_applier_status_by_worker : public PFS_double_index +{ + + pos_replication_applier_status_by_worker() : PFS_double_index(0, 0) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= 0; + } + + inline bool has_more_channels(uint num) + { return (m_index_1 < num); } + + inline void next_channel(void) + { + m_index_1++; + m_index_2= 0; + } + + inline void next_worker() + { + m_index_2++; + } + + inline void + set_channel_after(const pos_replication_applier_status_by_worker *other) + { + m_index_1 = other->m_index_1 + 1; + m_index_2 = 0; + } +}; + + +/** 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: + void make_row(Slave_worker *); + /* + Master_info to construct a row to display SQL Thread's status + information in STS mode + */ + void make_row(Master_info *); + + /** 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(); + + /** 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..6d358240 --- /dev/null +++ b/storage/perfschema/table_replication_connection_configuration.cc @@ -0,0 +1,333 @@ +/* + Copyright (c) 2013, 2018, 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 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 +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 CHAR(64) collate utf8_general_ci not null," + "HOST CHAR(60) collate utf8_bin not null," + "PORT INTEGER not null," + "USER CHAR(32) collate utf8_bin not null," + "NETWORK_INTERFACE CHAR(60) collate utf8_bin not null," + "AUTO_POSITION ENUM('1','0') not null," + "SSL_ALLOWED ENUM('YES','NO','IGNORED') not null," + "SSL_CA_FILE VARCHAR(512) not null," + "SSL_CA_PATH VARCHAR(512) not null," + "SSL_CERTIFICATE VARCHAR(512) not null," + "SSL_CIPHER VARCHAR(512) not null," + "SSL_KEY VARCHAR(512) not null," + "SSL_VERIFY_SERVER_CERTIFICATE ENUM('YES','NO') not null," + "SSL_CRL_FILE VARCHAR(255) not null," + "SSL_CRL_PATH VARCHAR(255) not null," + "CONNECTION_RETRY_INTERVAL INTEGER not null," + "CONNECTION_RETRY_COUNT BIGINT unsigned not null," + "HEARTBEAT_INTERVAL DOUBLE(10,3) unsigned not null COMMENT 'Number of seconds after which a heartbeat will be sent .'," + "TLS_VERSION VARCHAR(255) not null)") }, + false /* perpetual */ +}; + + +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) +{ + char * temp_store; + + 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.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); + + temp_store= const_cast<char*>(""); //(char*)mi->bind_addr; + m_row.network_interface_length= static_cast<uint>(strlen(temp_store)); + memcpy(m_row.network_interface, temp_store, m_row.network_interface_length); + + if (mi->using_gtid) + m_row.auto_position= PS_RPL_YES; + else + m_row.auto_position= PS_RPL_NO; + +#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= 0; //(ulong) mi->retry_count; + + m_row.heartbeat_interval= (double)mi->heartbeat_period; + + temp_store= (char*)""; //mi->tls_version; + m_row.tls_version_length= static_cast<uint>(strlen(temp_store)); + memcpy(m_row.tls_version, temp_store, m_row.tls_version_length); + + mysql_mutex_unlock(&mi->rli.data_lock); + mysql_mutex_unlock(&mi->data_lock); + + m_row_exists= true; +} + +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; + + 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_char_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: /** network_interface */ + set_field_char_utf8(f, m_row.network_interface, + m_row.network_interface_length); + break; + case 5: /** auto_position */ + set_field_enum(f, m_row.auto_position); + break; + case 6: /** ssl_allowed */ + set_field_enum(f, m_row. ssl_allowed); + break; + case 7: /**ssl_ca_file */ + set_field_varchar_utf8(f, m_row.ssl_ca_file, + m_row.ssl_ca_file_length); + break; + case 8: /** ssl_ca_path */ + set_field_varchar_utf8(f, m_row.ssl_ca_path, + m_row.ssl_ca_path_length); + break; + case 9: /** ssl_certificate */ + set_field_varchar_utf8(f, m_row.ssl_certificate, + m_row.ssl_certificate_length); + break; + case 10: /** ssl_cipher */ + set_field_varchar_utf8(f, m_row.ssl_cipher, m_row.ssl_cipher_length); + break; + case 11: /** ssl_key */ + set_field_varchar_utf8(f, m_row.ssl_key, m_row.ssl_key_length); + break; + case 12: /** ssl_verify_server_certificate */ + set_field_enum(f, m_row.ssl_verify_server_certificate); + break; + case 13: /** ssl_crl_file */ + set_field_varchar_utf8(f, m_row.ssl_crl_file, + m_row.ssl_crl_file_length); + break; + case 14: /** ssl_crl_path */ + set_field_varchar_utf8(f, m_row.ssl_crl_path, + m_row.ssl_crl_path_length); + break; + case 15: /** connection_retry_interval */ + set_field_ulong(f, m_row.connection_retry_interval); + break; + case 16: /** connect_retry_count */ + set_field_ulonglong(f, m_row.connection_retry_count); + break; + case 17:/** number of seconds after which heartbeat will be sent */ + set_field_double(f, m_row.heartbeat_interval); + break; + case 18: /** tls_version */ + set_field_varchar_utf8(f, m_row.tls_version, + m_row.tls_version_length); + break; + default: + DBUG_ASSERT(false); + } + } + } + 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..d1c8861c --- /dev/null +++ b/storage/perfschema/table_replication_connection_configuration.h @@ -0,0 +1,152 @@ +/* + Copyright (c) 2013, 2018, 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 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" +//#include "rpl_msr.h" +//#include "rpl_info.h" /* CHANNEL_NAME_LENGTH*/ + +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 +}; + +/** + 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; + char network_interface[HOSTNAME_LENGTH]; + uint network_interface_length; + enum_rpl_yes_no auto_position; + 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 tls_version[FN_REFLEN]; + uint tls_version_length; +}; + +/** 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(); + + /** 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..a4230913 --- /dev/null +++ b/storage/perfschema/table_replication_connection_status.cc @@ -0,0 +1,440 @@ +/* + Copyright (c) 2013, 2018, 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 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 +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, /* checked */ + false /* perpetual */ +}; + +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; + + DBUG_ASSERT(mi != NULL); + DBUG_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; + + DBUG_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: + DBUG_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..a1a5eb11 --- /dev/null +++ b/storage/perfschema/table_replication_connection_status.h @@ -0,0 +1,150 @@ +/* + Copyright (c) 2013, 2018, 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 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(); + + /** 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..f85a3e3f --- /dev/null +++ b/storage/perfschema/table_replication_group_member_stats.cc @@ -0,0 +1,372 @@ +/* + Copyright (c) 2014, 2016, 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 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 +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, /* checked */ + false /* perpetual */ +}; + +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); + DBUG_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; + + DBUG_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: + DBUG_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..f2fcaf55 --- /dev/null +++ b/storage/perfschema/table_replication_group_member_stats.h @@ -0,0 +1,116 @@ +/* + Copyright (c) 2014, 2016, 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 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(); + + /** 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..5967b98c --- /dev/null +++ b/storage/perfschema/table_replication_group_members.cc @@ -0,0 +1,281 @@ +/* + Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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, /* checked */ + false /* perpetual */ +}; + +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); + DBUG_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; + + DBUG_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: + DBUG_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..8ce875eb --- /dev/null +++ b/storage/perfschema/table_replication_group_members.h @@ -0,0 +1,108 @@ +/* + Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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(); + + /** 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..0e8be68d --- /dev/null +++ b/storage/perfschema/table_session_account_connect_attrs.cc @@ -0,0 +1,82 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "ATTR_NAME VARCHAR(32) NOT NULL," + "ATTR_VALUE VARCHAR(1024)," + "ORDINAL_POSITION INT" + ") CHARACTER SET utf8 COLLATE utf8_bin") }, + false /* perpetual */ +}; + +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. */ + DBUG_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..043f83da --- /dev/null +++ b/storage/perfschema/table_session_account_connect_attrs.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..09f32fea --- /dev/null +++ b/storage/perfschema/table_session_connect.cc @@ -0,0 +1,295 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 */ + DBUG_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: + DBUG_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..55433e25 --- /dev/null +++ b/storage/perfschema/table_session_connect.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..cc3fae46 --- /dev/null +++ b/storage/perfschema/table_session_connect_attrs.cc @@ -0,0 +1,55 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "ATTR_NAME VARCHAR(32) NOT NULL," + "ATTR_VALUE VARCHAR(1024)," + "ORDINAL_POSITION INT" + ") CHARACTER SET utf8 COLLATE utf8_bin") }, + false /* perpetual */ +}; + +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..09b9d1c4 --- /dev/null +++ b/storage/perfschema/table_session_connect_attrs.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..acff99ca --- /dev/null +++ b/storage/perfschema/table_session_status.cc @@ -0,0 +1,185 @@ +/* Copyright (c) 2015, 2016, 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 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 +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," + "VARIABLE_VALUE VARCHAR(1024))") }, + true /* perpetual */ +}; + +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); + DBUG_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 */ + DBUG_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: + DBUG_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..24634fe4 --- /dev/null +++ b/storage/perfschema/table_session_status.h @@ -0,0 +1,119 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..3ed13145 --- /dev/null +++ b/storage/perfschema/table_session_variables.cc @@ -0,0 +1,187 @@ +/* Copyright (c) 2015, 2018, 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 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 +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 /* perpetual */ +}; + +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); + DBUG_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 */ + DBUG_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: + DBUG_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..576bf73e --- /dev/null +++ b/storage/perfschema/table_session_variables.h @@ -0,0 +1,117 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..e17bf312 --- /dev/null +++ b/storage/perfschema/table_setup_actors.cc @@ -0,0 +1,312 @@ +/* Copyright (c) 2010, 2016, 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, + 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 +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," + "USER CHAR(" USERNAME_CHAR_LENGTH_STR ") collate utf8_bin default '%' not null," + "ROLE CHAR(" USERNAME_CHAR_LENGTH_STR ") collate utf8_bin default '%' not null," + "ENABLED ENUM('YES', 'NO') not null default 'YES'," + "HISTORY ENUM('YES', 'NO') not null default 'YES')") }, + false /* perpetual */ +}; + +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: + DBUG_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 */ + DBUG_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: + DBUG_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: + DBUG_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) +{ + DBUG_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..32a60331 --- /dev/null +++ b/storage/perfschema/table_setup_actors.h @@ -0,0 +1,115 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..1745bf35 --- /dev/null +++ b/storage/perfschema/table_setup_consumers.cc @@ -0,0 +1,275 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "ENABLED ENUM ('YES', 'NO') not null)") }, + false /* perpetual */ +}; + +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); + DBUG_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; + + DBUG_ASSERT(m_row); + + + /* Set the null bits */ + 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: /* 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: + DBUG_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; + + DBUG_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: + DBUG_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..9d601622 --- /dev/null +++ b/storage/perfschema/table_setup_consumers.h @@ -0,0 +1,95 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..69e48b9c --- /dev/null +++ b/storage/perfschema/table_setup_instruments.cc @@ -0,0 +1,372 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "ENABLED ENUM ('YES', 'NO') not null," + "TIMED ENUM ('YES', 'NO') not null)") }, + false /* perpetual */ +}; + +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; + + DBUG_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: + DBUG_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: + DBUG_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: + DBUG_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..42aa2dd9 --- /dev/null +++ b/storage/perfschema/table_setup_instruments.h @@ -0,0 +1,135 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..cccbd64f --- /dev/null +++ b/storage/perfschema/table_setup_objects.cc @@ -0,0 +1,334 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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'," + "OBJECT_SCHEMA VARCHAR(64) default '%'," + "OBJECT_NAME VARCHAR(64) not null default '%'," + "ENABLED ENUM ('YES', 'NO') not null default 'YES'," + "TIMED ENUM ('YES', 'NO') not null default 'YES')") }, + false /* perpetual */ +}; + +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: + DBUG_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 */ + DBUG_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: + DBUG_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: + DBUG_ASSERT(false); + } + } + } + + result= update_derived_flags(); + return result; +} + +int table_setup_objects::delete_row_values(TABLE *table, + const unsigned char *buf, + Field **fields) +{ + DBUG_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..abe36f73 --- /dev/null +++ b/storage/perfschema/table_setup_objects.h @@ -0,0 +1,114 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..d5656ea2 --- /dev/null +++ b/storage/perfschema/table_setup_timers.cc @@ -0,0 +1,205 @@ +/* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "TIMER_NAME ENUM ('CYCLE', 'NANOSECOND', 'MICROSECOND', 'MILLISECOND', 'TICK') not null)") }, + false /* perpetual */ +}; + +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); + DBUG_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; + + DBUG_ASSERT(m_row); + + /* Set the null bits */ + 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: /* 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: + DBUG_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; + + DBUG_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: + DBUG_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..43100bf3 --- /dev/null +++ b/storage/perfschema/table_setup_timers.h @@ -0,0 +1,91 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..24c9b0d0 --- /dev/null +++ b/storage/perfschema/table_socket_instances.cc @@ -0,0 +1,208 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null," + "THREAD_ID BIGINT unsigned," + "SOCKET_ID INTEGER not null," + "IP VARCHAR(64) not null," + "PORT INTEGER not null," + "STATE ENUM('IDLE','ACTIVE') not null)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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: + DBUG_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..4e32bbdc --- /dev/null +++ b/storage/perfschema/table_socket_instances.h @@ -0,0 +1,108 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..bdfff756 --- /dev/null +++ b/storage/perfschema/table_socket_summary_by_event_name.cc @@ -0,0 +1,255 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "COUNT_READ BIGINT unsigned not null," + "SUM_TIMER_READ BIGINT unsigned not null," + "MIN_TIMER_READ BIGINT unsigned not null," + "AVG_TIMER_READ BIGINT unsigned not null," + "MAX_TIMER_READ BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_READ BIGINT unsigned not null," + "COUNT_WRITE BIGINT unsigned not null," + "SUM_TIMER_WRITE BIGINT unsigned not null," + "MIN_TIMER_WRITE BIGINT unsigned not null," + "AVG_TIMER_WRITE BIGINT unsigned not null," + "MAX_TIMER_WRITE BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_WRITE BIGINT unsigned not null," + "COUNT_MISC BIGINT unsigned not null," + "SUM_TIMER_MISC BIGINT unsigned not null," + "MIN_TIMER_MISC BIGINT unsigned not null," + "AVG_TIMER_MISC BIGINT unsigned not null," + "MAX_TIMER_MISC BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + 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: /* 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: + DBUG_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..e25d38b9 --- /dev/null +++ b/storage/perfschema/table_socket_summary_by_event_name.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..372cde61 --- /dev/null +++ b/storage/perfschema/table_socket_summary_by_instance.cc @@ -0,0 +1,271 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "COUNT_READ BIGINT unsigned not null," + "SUM_TIMER_READ BIGINT unsigned not null," + "MIN_TIMER_READ BIGINT unsigned not null," + "AVG_TIMER_READ BIGINT unsigned not null," + "MAX_TIMER_READ BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_READ BIGINT unsigned not null," + "COUNT_WRITE BIGINT unsigned not null," + "SUM_TIMER_WRITE BIGINT unsigned not null," + "MIN_TIMER_WRITE BIGINT unsigned not null," + "AVG_TIMER_WRITE BIGINT unsigned not null," + "MAX_TIMER_WRITE BIGINT unsigned not null," + "SUM_NUMBER_OF_BYTES_WRITE BIGINT unsigned not null," + "COUNT_MISC BIGINT unsigned not null," + "SUM_TIMER_MISC BIGINT unsigned not null," + "MIN_TIMER_MISC BIGINT unsigned not null," + "AVG_TIMER_MISC BIGINT unsigned not null," + "MAX_TIMER_MISC BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + 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: /* 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: + DBUG_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..3f6d1295 --- /dev/null +++ b/storage/perfschema/table_socket_summary_by_instance.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..30a1b6db --- /dev/null +++ b/storage/perfschema/table_status_by_account.cc @@ -0,0 +1,246 @@ +/* Copyright (c) 2015, 2016, 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 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 +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," + "HOST CHAR(60) collate utf8_bin default null," + "VARIABLE_NAME VARCHAR(64) not null," + "VARIABLE_VALUE VARCHAR(1024))") }, + false /* perpetual */ +}; + +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); + DBUG_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 */ + DBUG_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: + DBUG_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..401a184d --- /dev/null +++ b/storage/perfschema/table_status_by_account.h @@ -0,0 +1,156 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..cd41fcaa --- /dev/null +++ b/storage/perfschema/table_status_by_host.cc @@ -0,0 +1,245 @@ +/* Copyright (c) 2015, 2016, 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 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 +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," + "VARIABLE_NAME VARCHAR(64) not null," + "VARIABLE_VALUE VARCHAR(1024))") }, + false /* perpetual */ +}; + +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); + DBUG_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 */ + DBUG_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: + DBUG_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..e4c6bf25 --- /dev/null +++ b/storage/perfschema/table_status_by_host.h @@ -0,0 +1,154 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..d9af7e49 --- /dev/null +++ b/storage/perfschema/table_status_by_thread.cc @@ -0,0 +1,239 @@ +/* Copyright (c) 2015, 2016, 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 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 +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," + "VARIABLE_NAME VARCHAR(64) not null," + "VARIABLE_VALUE VARCHAR(1024))") }, + false /* perpetual */ +}; + +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_vars. */ + 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); + DBUG_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 */ + DBUG_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: + DBUG_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..f8de21dc --- /dev/null +++ b/storage/perfschema/table_status_by_thread.h @@ -0,0 +1,151 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..921a7224 --- /dev/null +++ b/storage/perfschema/table_status_by_user.cc @@ -0,0 +1,246 @@ +/* Copyright (c) 2015, 2016, 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 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 +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," + "VARIABLE_NAME VARCHAR(64) not null," + "VARIABLE_VALUE VARCHAR(1024))") }, + false /* perpetual */ +}; + +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); + DBUG_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 */ + DBUG_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: + DBUG_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..6b4a67b4 --- /dev/null +++ b/storage/perfschema/table_status_by_user.h @@ -0,0 +1,153 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..5b9f90c1 --- /dev/null +++ b/storage/perfschema/table_sync_instances.cc @@ -0,0 +1,472 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null," + "LOCKED_BY_THREAD_ID BIGINT unsigned)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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: + DBUG_ASSERT(false); + } + } + } + + return 0; +} + +THR_LOCK table_rwlock_instances::m_table_lock; + +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," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null," + "WRITE_LOCKED_BY_THREAD_ID BIGINT unsigned," + "READ_LOCKED_BY_COUNT INTEGER unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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: + DBUG_ASSERT(false); + } + } + } + + return 0; +} + +THR_LOCK table_cond_instances::m_table_lock; + +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," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + 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: /* 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: + DBUG_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..6cf8f0f3 --- /dev/null +++ b/storage/perfschema/table_sync_instances.h @@ -0,0 +1,210 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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: + /** 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() + {} + +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: + /** 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() + {} + +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..ab6ed357 --- /dev/null +++ b/storage/perfschema/table_table_handles.cc @@ -0,0 +1,214 @@ +/* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "OBJECT_SCHEMA VARCHAR(64) not null," + "OBJECT_NAME VARCHAR(64) not null," + "OBJECT_INSTANCE_BEGIN BIGINT unsigned not null," + "OWNER_THREAD_ID BIGINT unsigned," + "OWNER_EVENT_ID BIGINT unsigned," + "INTERNAL_LOCK VARCHAR(64)," + "EXTERNAL_LOCK VARCHAR(64))") }, + false /* perpetual */ +}; + +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 */ + DBUG_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: + DBUG_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..0f1f76f4 --- /dev/null +++ b/storage/perfschema/table_table_handles.h @@ -0,0 +1,108 @@ +/* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..a752902c --- /dev/null +++ b/storage/perfschema/table_threads.cc @@ -0,0 +1,365 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "NAME VARCHAR(128) not null," + "TYPE VARCHAR(10) not null," + "PROCESSLIST_ID BIGINT unsigned," + "PROCESSLIST_USER VARCHAR(" USERNAME_CHAR_LENGTH_STR ")," + "PROCESSLIST_HOST VARCHAR(" HOSTNAME_LENGTH_STR ")," + "PROCESSLIST_DB VARCHAR(64)," + "PROCESSLIST_COMMAND VARCHAR(16)," + "PROCESSLIST_TIME BIGINT," + "PROCESSLIST_STATE VARCHAR(64)," + "PROCESSLIST_INFO LONGTEXT," + "PARENT_THREAD_ID BIGINT unsigned," + "ROLE VARCHAR(64)," + "INSTRUMENTED ENUM ('YES', 'NO') not null," + "HISTORY ENUM ('YES', 'NO') not null," + "CONNECTION_TYPE VARCHAR(16)," + "THREAD_OS_ID BIGINT unsigned)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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. + */ + DBUG_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: + DBUG_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: + DBUG_ASSERT(false); + } + } + } + return 0; +} + diff --git a/storage/perfschema/table_threads.h b/storage/perfschema/table_threads.h new file mode 100644 index 00000000..88302ea6 --- /dev/null +++ b/storage/perfschema/table_threads.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..a1a7343e --- /dev/null +++ b/storage/perfschema/table_tiws_by_index_usage.cc @@ -0,0 +1,371 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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)," + "OBJECT_SCHEMA VARCHAR(64)," + "OBJECT_NAME VARCHAR(64)," + "INDEX_NAME VARCHAR(64)," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "COUNT_READ BIGINT unsigned not null," + "SUM_TIMER_READ BIGINT unsigned not null," + "MIN_TIMER_READ BIGINT unsigned not null," + "AVG_TIMER_READ BIGINT unsigned not null," + "MAX_TIMER_READ BIGINT unsigned not null," + "COUNT_WRITE BIGINT unsigned not null," + "SUM_TIMER_WRITE BIGINT unsigned not null," + "MIN_TIMER_WRITE BIGINT unsigned not null," + "AVG_TIMER_WRITE BIGINT unsigned not null," + "MAX_TIMER_WRITE BIGINT unsigned not null," + "COUNT_FETCH BIGINT unsigned not null," + "SUM_TIMER_FETCH BIGINT unsigned not null," + "MIN_TIMER_FETCH BIGINT unsigned not null," + "AVG_TIMER_FETCH BIGINT unsigned not null," + "MAX_TIMER_FETCH BIGINT unsigned not null," + "COUNT_INSERT BIGINT unsigned not null," + "SUM_TIMER_INSERT BIGINT unsigned not null," + "MIN_TIMER_INSERT BIGINT unsigned not null," + "AVG_TIMER_INSERT BIGINT unsigned not null," + "MAX_TIMER_INSERT BIGINT unsigned not null," + "COUNT_UPDATE BIGINT unsigned not null," + "SUM_TIMER_UPDATE BIGINT unsigned not null," + "MIN_TIMER_UPDATE BIGINT unsigned not null," + "AVG_TIMER_UPDATE BIGINT unsigned not null," + "MAX_TIMER_UPDATE BIGINT unsigned not null," + "COUNT_DELETE BIGINT unsigned not null," + "SUM_TIMER_DELETE BIGINT unsigned not null," + "MIN_TIMER_DELETE BIGINT unsigned not null," + "AVG_TIMER_DELETE BIGINT unsigned not null," + "MAX_TIMER_DELETE BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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; + + DBUG_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 */ + DBUG_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: + DBUG_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..cc6ee5eb --- /dev/null +++ b/storage/perfschema/table_tiws_by_index_usage.h @@ -0,0 +1,124 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..abd45a7b --- /dev/null +++ b/storage/perfschema/table_tiws_by_table.cc @@ -0,0 +1,333 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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)," + "OBJECT_SCHEMA VARCHAR(64)," + "OBJECT_NAME VARCHAR(64)," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "COUNT_READ BIGINT unsigned not null," + "SUM_TIMER_READ BIGINT unsigned not null," + "MIN_TIMER_READ BIGINT unsigned not null," + "AVG_TIMER_READ BIGINT unsigned not null," + "MAX_TIMER_READ BIGINT unsigned not null," + "COUNT_WRITE BIGINT unsigned not null," + "SUM_TIMER_WRITE BIGINT unsigned not null," + "MIN_TIMER_WRITE BIGINT unsigned not null," + "AVG_TIMER_WRITE BIGINT unsigned not null," + "MAX_TIMER_WRITE BIGINT unsigned not null," + "COUNT_FETCH BIGINT unsigned not null," + "SUM_TIMER_FETCH BIGINT unsigned not null," + "MIN_TIMER_FETCH BIGINT unsigned not null," + "AVG_TIMER_FETCH BIGINT unsigned not null," + "MAX_TIMER_FETCH BIGINT unsigned not null," + "COUNT_INSERT BIGINT unsigned not null," + "SUM_TIMER_INSERT BIGINT unsigned not null," + "MIN_TIMER_INSERT BIGINT unsigned not null," + "AVG_TIMER_INSERT BIGINT unsigned not null," + "MAX_TIMER_INSERT BIGINT unsigned not null," + "COUNT_UPDATE BIGINT unsigned not null," + "SUM_TIMER_UPDATE BIGINT unsigned not null," + "MIN_TIMER_UPDATE BIGINT unsigned not null," + "AVG_TIMER_UPDATE BIGINT unsigned not null," + "MAX_TIMER_UPDATE BIGINT unsigned not null," + "COUNT_DELETE BIGINT unsigned not null," + "SUM_TIMER_DELETE BIGINT unsigned not null," + "MIN_TIMER_DELETE BIGINT unsigned not null," + "AVG_TIMER_DELETE BIGINT unsigned not null," + "MAX_TIMER_DELETE BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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: + DBUG_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..4100a991 --- /dev/null +++ b/storage/perfschema/table_tiws_by_table.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..8f4bc2ae --- /dev/null +++ b/storage/perfschema/table_tlws_by_table.cc @@ -0,0 +1,485 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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)," + "OBJECT_SCHEMA VARCHAR(64)," + "OBJECT_NAME VARCHAR(64)," + "COUNT_STAR BIGINT unsigned not null," + "SUM_TIMER_WAIT BIGINT unsigned not null," + "MIN_TIMER_WAIT BIGINT unsigned not null," + "AVG_TIMER_WAIT BIGINT unsigned not null," + "MAX_TIMER_WAIT BIGINT unsigned not null," + "COUNT_READ BIGINT unsigned not null," + "SUM_TIMER_READ BIGINT unsigned not null," + "MIN_TIMER_READ BIGINT unsigned not null," + "AVG_TIMER_READ BIGINT unsigned not null," + "MAX_TIMER_READ BIGINT unsigned not null," + "COUNT_WRITE BIGINT unsigned not null," + "SUM_TIMER_WRITE BIGINT unsigned not null," + "MIN_TIMER_WRITE BIGINT unsigned not null," + "AVG_TIMER_WRITE BIGINT unsigned not null," + "MAX_TIMER_WRITE BIGINT unsigned not null," + "COUNT_READ_NORMAL BIGINT unsigned not null," + "SUM_TIMER_READ_NORMAL BIGINT unsigned not null," + "MIN_TIMER_READ_NORMAL BIGINT unsigned not null," + "AVG_TIMER_READ_NORMAL BIGINT unsigned not null," + "MAX_TIMER_READ_NORMAL BIGINT unsigned not null," + "COUNT_READ_WITH_SHARED_LOCKS BIGINT unsigned not null," + "SUM_TIMER_READ_WITH_SHARED_LOCKS BIGINT unsigned not null," + "MIN_TIMER_READ_WITH_SHARED_LOCKS BIGINT unsigned not null," + "AVG_TIMER_READ_WITH_SHARED_LOCKS BIGINT unsigned not null," + "MAX_TIMER_READ_WITH_SHARED_LOCKS BIGINT unsigned not null," + "COUNT_READ_HIGH_PRIORITY BIGINT unsigned not null," + "SUM_TIMER_READ_HIGH_PRIORITY BIGINT unsigned not null," + "MIN_TIMER_READ_HIGH_PRIORITY BIGINT unsigned not null," + "AVG_TIMER_READ_HIGH_PRIORITY BIGINT unsigned not null," + "MAX_TIMER_READ_HIGH_PRIORITY BIGINT unsigned not null," + "COUNT_READ_NO_INSERT BIGINT unsigned not null," + "SUM_TIMER_READ_NO_INSERT BIGINT unsigned not null," + "MIN_TIMER_READ_NO_INSERT BIGINT unsigned not null," + "AVG_TIMER_READ_NO_INSERT BIGINT unsigned not null," + "MAX_TIMER_READ_NO_INSERT BIGINT unsigned not null," + "COUNT_READ_EXTERNAL BIGINT unsigned not null," + "SUM_TIMER_READ_EXTERNAL BIGINT unsigned not null," + "MIN_TIMER_READ_EXTERNAL BIGINT unsigned not null," + "AVG_TIMER_READ_EXTERNAL BIGINT unsigned not null," + "MAX_TIMER_READ_EXTERNAL BIGINT unsigned not null," + "COUNT_WRITE_ALLOW_WRITE BIGINT unsigned not null," + "SUM_TIMER_WRITE_ALLOW_WRITE BIGINT unsigned not null," + "MIN_TIMER_WRITE_ALLOW_WRITE BIGINT unsigned not null," + "AVG_TIMER_WRITE_ALLOW_WRITE BIGINT unsigned not null," + "MAX_TIMER_WRITE_ALLOW_WRITE BIGINT unsigned not null," + "COUNT_WRITE_CONCURRENT_INSERT BIGINT unsigned not null," + "SUM_TIMER_WRITE_CONCURRENT_INSERT BIGINT unsigned not null," + "MIN_TIMER_WRITE_CONCURRENT_INSERT BIGINT unsigned not null," + "AVG_TIMER_WRITE_CONCURRENT_INSERT BIGINT unsigned not null," + "MAX_TIMER_WRITE_CONCURRENT_INSERT BIGINT unsigned not null," + "COUNT_WRITE_DELAYED BIGINT unsigned not null," + "SUM_TIMER_WRITE_DELAYED BIGINT unsigned not null," + "MIN_TIMER_WRITE_DELAYED BIGINT unsigned not null," + "AVG_TIMER_WRITE_DELAYED BIGINT unsigned not null," + "MAX_TIMER_WRITE_DELAYED BIGINT unsigned not null," + "COUNT_WRITE_LOW_PRIORITY BIGINT unsigned not null," + "SUM_TIMER_WRITE_LOW_PRIORITY BIGINT unsigned not null," + "MIN_TIMER_WRITE_LOW_PRIORITY BIGINT unsigned not null," + "AVG_TIMER_WRITE_LOW_PRIORITY BIGINT unsigned not null," + "MAX_TIMER_WRITE_LOW_PRIORITY BIGINT unsigned not null," + "COUNT_WRITE_NORMAL BIGINT unsigned not null," + "SUM_TIMER_WRITE_NORMAL BIGINT unsigned not null," + "MIN_TIMER_WRITE_NORMAL BIGINT unsigned not null," + "AVG_TIMER_WRITE_NORMAL BIGINT unsigned not null," + "MAX_TIMER_WRITE_NORMAL BIGINT unsigned not null," + "COUNT_WRITE_EXTERNAL BIGINT unsigned not null," + "SUM_TIMER_WRITE_EXTERNAL BIGINT unsigned not null," + "MIN_TIMER_WRITE_EXTERNAL BIGINT unsigned not null," + "AVG_TIMER_WRITE_EXTERNAL BIGINT unsigned not null," + "MAX_TIMER_WRITE_EXTERNAL BIGINT unsigned not null)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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: + DBUG_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..7cc4b666 --- /dev/null +++ b/storage/perfschema/table_tlws_by_table.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..734ff7a4 --- /dev/null +++ b/storage/perfschema/table_users.cc @@ -0,0 +1,149 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "CURRENT_CONNECTIONS bigint not null," + "TOTAL_CONNECTIONS bigint not null)") }, + false /* perpetual */ +}; + +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 */ + DBUG_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: + DBUG_ASSERT(false); + } + } + } + return 0; +} + diff --git a/storage/perfschema/table_users.h b/storage/perfschema/table_users.h new file mode 100644 index 00000000..1c3cc647 --- /dev/null +++ b/storage/perfschema/table_users.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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() + {} + +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..77764c00 --- /dev/null +++ b/storage/perfschema/table_uvar_by_thread.cc @@ -0,0 +1,329 @@ +/* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 +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," + "VARIABLE_NAME VARCHAR(64) not null," + "VARIABLE_VALUE LONGBLOB)") }, + false /* perpetual */ +}; + +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 */ + DBUG_ASSERT(table->s->null_bytes == 1); + buf[0]= 0; + + DBUG_ASSERT(m_row.m_variable_name != NULL); + DBUG_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: + DBUG_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..a7334b2b --- /dev/null +++ b/storage/perfschema/table_uvar_by_thread.h @@ -0,0 +1,195 @@ +/* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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) + { + DBUG_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: + /** 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..b43c6cd8 --- /dev/null +++ b/storage/perfschema/table_variables_by_thread.cc @@ -0,0 +1,229 @@ +/* Copyright (c) 2015, 2018, 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 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 +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 /* perpetual */ +}; + +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); + DBUG_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 */ + DBUG_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: + DBUG_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..d628707f --- /dev/null +++ b/storage/perfschema/table_variables_by_thread.h @@ -0,0 +1,151 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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: + /** 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..1bba2673 --- /dev/null +++ b/storage/perfschema/unittest/CMakeLists.txt @@ -0,0 +1,40 @@ +# Copyright (c) 2009, 2019, 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, +# 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..f84ba331 --- /dev/null +++ b/storage/perfschema/unittest/conf.txt @@ -0,0 +1,427 @@ +# Copyright (c) 2009, 2010, 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..43d9d8d2 --- /dev/null +++ b/storage/perfschema/unittest/pfs-t.cc @@ -0,0 +1,1909 @@ +/* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..185b0a65 --- /dev/null +++ b/storage/perfschema/unittest/pfs_account-oom-t.cc @@ -0,0 +1,174 @@ +/* Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..b57ead3e --- /dev/null +++ b/storage/perfschema/unittest/pfs_connect_attr-t.cc @@ -0,0 +1,352 @@ +/* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..e97a1170 --- /dev/null +++ b/storage/perfschema/unittest/pfs_host-oom-t.cc @@ -0,0 +1,148 @@ +/* Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..9472d8aa --- /dev/null +++ b/storage/perfschema/unittest/pfs_instr-oom-t.cc @@ -0,0 +1,459 @@ +/* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..d4401ab7 --- /dev/null +++ b/storage/perfschema/unittest/pfs_instr-t.cc @@ -0,0 +1,488 @@ +/* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..0f9d6ef0 --- /dev/null +++ b/storage/perfschema/unittest/pfs_instr_class-oom-t.cc @@ -0,0 +1,114 @@ +/* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..3dfd725c --- /dev/null +++ b/storage/perfschema/unittest/pfs_instr_class-t.cc @@ -0,0 +1,750 @@ +/* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..d8217800 --- /dev/null +++ b/storage/perfschema/unittest/pfs_misc-t.cc @@ -0,0 +1,95 @@ +/* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..2756680d --- /dev/null +++ b/storage/perfschema/unittest/pfs_noop-t.cc @@ -0,0 +1,242 @@ +/* Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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); + + 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..5a855b2c --- /dev/null +++ b/storage/perfschema/unittest/pfs_server_stubs.cc @@ -0,0 +1,63 @@ +/* Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights + reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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 *ptr, enum sys_var::where here) +{ +} + +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..6c852242 --- /dev/null +++ b/storage/perfschema/unittest/pfs_timer-t.cc @@ -0,0 +1,128 @@ +/* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..864275b7 --- /dev/null +++ b/storage/perfschema/unittest/pfs_user-oom-t.cc @@ -0,0 +1,146 @@ +/* Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..c870b863 --- /dev/null +++ b/storage/perfschema/unittest/stub_global_status_var.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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..95150873 --- /dev/null +++ b/storage/perfschema/unittest/stub_pfs_defaults.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2010, 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 */ + +#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..23b731cb --- /dev/null +++ b/storage/perfschema/unittest/stub_pfs_global.h @@ -0,0 +1,89 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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> + +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. + */ + DBUG_ASSERT(size <= 100*1024*1024); + + if (stub_alloc_always_fails) + return NULL; + + if (--stub_alloc_fails_after_count <= 0) + return NULL; + + void *ptr= malloc(size); + if (ptr != NULL) + 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, ...) +{ +} + + diff --git a/storage/perfschema/unittest/stub_print_error.h b/storage/perfschema/unittest/stub_print_error.h new file mode 100644 index 00000000..8d87fbe5 --- /dev/null +++ b/storage/perfschema/unittest/stub_print_error.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, 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. */ +} + |