diff options
author | Konstantin Osipov <kostja@sun.com> | 2010-02-04 20:34:15 +0300 |
---|---|---|
committer | Konstantin Osipov <kostja@sun.com> | 2010-02-04 20:34:15 +0300 |
commit | a9e22b589677df9035c73e61b80be0fc54d175a5 (patch) | |
tree | bf6e8bd7de75f7f9ec6dbf8903d150ed3511a6da /storage/perfschema | |
parent | 1edca1d7ef6a77d406ea2c1bcaf5d9aff2f4c04c (diff) | |
parent | 8dfc3fbbab0dc7b3be98a47423ae74a6b4933864 (diff) | |
download | mariadb-git-a9e22b589677df9035c73e61b80be0fc54d175a5.tar.gz |
Merge next-mr -> next-4284-merge.
Diffstat (limited to 'storage/perfschema')
64 files changed, 16879 insertions, 0 deletions
diff --git a/storage/perfschema/CMakeLists.txt b/storage/perfschema/CMakeLists.txt new file mode 100644 index 00000000000..dbfab3b6749 --- /dev/null +++ b/storage/perfschema/CMakeLists.txt @@ -0,0 +1,79 @@ +# Copyright (C) 2009 Sun Microsystems, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake") +INCLUDE("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake") + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_SOURCE_DIR}/regex + ${CMAKE_SOURCE_DIR}/extra/yassl/include) + +ADD_DEFINITIONS(-DMYSQL_SERVER) + +SET(PERFSCHEMA_SOURCES ha_perfschema.h + pfs_column_types.h + pfs_column_values.h + pfs_events_waits.h + pfs_global.h + pfs.h + pfs_instr.h + pfs_instr_class.h + pfs_lock.h + pfs_atomic.h + pfs_server.h + pfs_stat.h + pfs_engine_table.h + pfs_timer.h + table_all_instr.h + table_events_waits.h + table_events_waits_summary.h + table_file_instances.h + table_file_summary.h + table_performance_timers.h + table_processlist.h + table_setup_consumers.h + table_setup_instruments.h + table_setup_objects.h + table_setup_timers.h + table_sync_instances.h + ha_perfschema.cc + pfs.cc + pfs_column_values.cc + pfs_events_waits.cc + pfs_global.cc + pfs_instr.cc + pfs_instr_class.cc + pfs_server.cc + pfs_engine_table.cc + pfs_timer.cc + table_all_instr.cc + table_events_waits.cc + table_events_waits_summary.cc + table_file_instances.cc + table_file_summary.cc + table_performance_timers.cc + table_processlist.cc + table_setup_consumers.cc + table_setup_instruments.cc + table_setup_objects.cc + table_setup_timers.cc + table_sync_instances.cc + pfs_atomic.cc + pfs_check.cc +) + +MYSQL_STORAGE_ENGINE(PERFSCHEMA) diff --git a/storage/perfschema/Makefile.am b/storage/perfschema/Makefile.am new file mode 100644 index 00000000000..8c30c812bc6 --- /dev/null +++ b/storage/perfschema/Makefile.am @@ -0,0 +1,76 @@ +# Copyright (C) 2008-2009 Sun Microsystems, Inc +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +#called from the top level Makefile + +SUBDIRS = . unittest + +MYSQLDATAdir = $(localstatedir) +MYSQLSHAREdir = $(pkgdatadir) +MYSQLBASEdir= $(prefix) +MYSQLLIBdir= $(pkglibdir) +pkgplugindir = $(pkglibdir)/plugin +INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include \ + -I$(top_srcdir)/regex \ + -I$(top_srcdir)/sql \ + -I$(srcdir) +WRAPLIBS= + +LDADD = + +DEFS = -DMYSQL_SERVER @DEFS@ + + +noinst_HEADERS = ha_perfschema.h pfs_engine_table.h pfs.h pfs_server.h \ + pfs_global.h pfs_instr_class.h pfs_instr.h \ + pfs_column_types.h pfs_column_values.h \ + table_setup_instruments.h table_performance_timers.h \ + table_setup_timers.h \ + table_setup_consumers.h table_events_waits.h \ + pfs_events_waits.h pfs_timer.h table_processlist.h \ + table_sync_instances.h \ + table_events_waits_summary.h pfs_stat.h \ + table_all_instr.h \ + table_file_instances.h table_file_summary.h \ + table_setup_objects.h pfs_lock.h pfs_atomic.h + +PSE_SOURCES = ha_perfschema.cc pfs_engine_table.cc pfs.cc pfs_server.cc \ + pfs_global.cc pfs_instr_class.cc pfs_instr.cc \ + pfs_column_values.cc \ + table_setup_instruments.cc table_performance_timers.cc \ + table_setup_timers.cc \ + table_setup_consumers.cc table_events_waits.cc \ + pfs_events_waits.cc pfs_timer.cc table_processlist.cc \ + table_sync_instances.cc \ + table_events_waits_summary.cc \ + table_all_instr.cc \ + table_file_instances.cc table_file_summary.cc \ + table_setup_objects.cc pfs_atomic.cc pfs_check.cc + +EXTRA_LIBRARIES = libperfschema.a +noinst_LIBRARIES = @plugin_perfschema_static_target@ + +libperfschema_a_SOURCES= $(PSE_SOURCES) + +EXTRA_DIST = plug.in CMakeLists.txt + +unittests = unittest + +test: + perl $(top_srcdir)/unittest/unit.pl run $(unittests) + +test-verbose: + HARNESS_VERBOSE=1 perl $(top_srcdir)/unittest/unit.pl run $(unittests) + diff --git a/storage/perfschema/ha_perfschema.cc b/storage/perfschema/ha_perfschema.cc new file mode 100644 index 00000000000..68e1f3f3a11 --- /dev/null +++ b/storage/perfschema/ha_perfschema.cc @@ -0,0 +1,382 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/ha_perfschema.cc + Performance schema storage engine (implementation). +*/ + +#include "mysql_priv.h" +#include "ha_perfschema.h" +#include "mysql/plugin.h" +#include "pfs_engine_table.h" +#include "pfs_column_values.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" + +handlerton *pfs_hton= NULL; + +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_init_func(void *p) +{ + DBUG_ENTER("pfs_init_func"); + + pfs_hton= reinterpret_cast<handlerton *> (p); + + pfs_hton->state= SHOW_OPTION_YES; + pfs_hton->create= pfs_create_handler; + pfs_hton->show_status= pfs_show_status; + pfs_hton->flags= HTON_ALTER_NOT_SUPPORTED | + HTON_TEMPORARY_NOT_SUPPORTED | + HTON_NO_PARTITION; + + /* + 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_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 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_mutex_instances_lost", + (char*) &mutex_lost, SHOW_LONG}, + {"Performance_schema_rwlock_instances_lost", + (char*) &rwlock_lost, SHOW_LONG}, + {"Performance_schema_cond_instances_lost", + (char*) &cond_lost, SHOW_LONG}, + {"Performance_schema_thread_instances_lost", + (char*) &thread_lost, SHOW_LONG}, + {"Performance_schema_file_instances_lost", + (char*) &file_lost, SHOW_LONG}, + {"Performance_schema_file_handles_lost", + (char*) &file_handle_lost, SHOW_LONG}, + {"Performance_schema_locker_lost", + (char*) &locker_lost, SHOW_LONG}, + /* table shares, can be flushed */ + {"Performance_schema_table_instances_lost", + (char*) &table_share_lost, SHOW_LONG}, + /* table handles, can be flushed */ + {"Performance_schema_table_handles_lost", + (char*) &table_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"; + +mysql_declare_plugin(perfschema) +{ + MYSQL_STORAGE_ENGINE_PLUGIN, + &pfs_storage_engine, + pfs_engine_name, + "Marc Alff, Sun Microsystems", + "Performance Schema", + PLUGIN_LICENSE_GPL, + pfs_init_func, /* Plugin Init */ + pfs_done_func, /* Plugin Deinit */ + 0x0001 /* 0.1 */, + pfs_status_vars, /* status variables */ + NULL, /* system variables */ + NULL /* config options */ +} +mysql_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() +{} + +static const char *ha_pfs_exts[]= { + NullS +}; + +const char **ha_perfschema::bas_ext() const +{ + return ha_pfs_exts; +} + +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; + + psi_open(); + + DBUG_RETURN(0); +} + +int ha_perfschema::close(void) +{ + DBUG_ENTER("ha_perfschema::close"); + m_table_share= NULL; + delete m_table; + m_table= NULL; + + psi_close(); + + DBUG_RETURN(0); +} + +int ha_perfschema::write_row(uchar *buf) +{ + int result; + + DBUG_ENTER("ha_perfschema::write_row"); + + ha_statistic_increment(&SSV::ha_write_count); + DBUG_ASSERT(m_table_share); + + if (m_table_share->m_write_row) + result= m_table_share->m_write_row(table, buf, table->field); + else + { + my_error(ER_WRONG_PERFSCHEMA_USAGE, MYF(0)); + result= HA_ERR_WRONG_COMMAND; + } + + 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, uchar *new_data) +{ + DBUG_ENTER("ha_perfschema::update_row"); + + DBUG_ASSERT(m_table); + int result= m_table->update_row(table, old_data, new_data, 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(); + + 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"); + + 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++; + } + 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"); + + DBUG_ASSERT(m_table); + int result= m_table->rnd_pos(pos); + if (result == 0) + result= m_table->read_row(table, buf, table->field); + 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->m_records; + 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"); + + DBUG_ASSERT(m_table_share); + if (m_table_share->m_delete_all_rows) + result= m_table_share->m_delete_all_rows(); + else + { + my_error(ER_WRONG_PERFSCHEMA_USAGE, MYF(0)); + result= HA_ERR_WRONG_COMMAND; + } + DBUG_RETURN(result); +} + +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 "); + my_error(ER_WRONG_PERFSCHEMA_USAGE, MYF(0)); + 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"); + DBUG_ASSERT(table_arg); + DBUG_ASSERT(table_arg->s); + if (find_table_share(table_arg->s->db.str, + table_arg->s->table_name.str)) + { + /* + Attempting to create a known performance schema table. + Allowing the create, to create .FRM files, + for the initial database install, and mysql_upgrade. + This should fail once .FRM are removed. + */ + DBUG_RETURN(0); + } + /* + This is not a general purpose engine. + Failure to CREATE TABLE is the expected result. + */ + my_error(ER_WRONG_PERFSCHEMA_USAGE, MYF(0)); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); +} + diff --git a/storage/perfschema/ha_perfschema.h b/storage/perfschema/ha_perfschema.h new file mode 100644 index 00000000000..146b258ff47 --- /dev/null +++ b/storage/perfschema/ha_perfschema.h @@ -0,0 +1,159 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef HA_PERFSCHEMA_H +#define HA_PERFSCHEMA_H + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface /* gcc class implementation */ +#endif + +/** + @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; +extern const char *pfs_engine_name; + +/** A handler for a PERFORMANCE_SCHEMA table. */ +class ha_perfschema : public handler +{ +public: + ha_perfschema(handlerton *hton, TABLE_SHARE *share); + + ~ha_perfschema(); + + const char *table_type(void) const { return pfs_engine_name; } + + const char *index_type(uint) { return ""; } + + const char **bas_ext(void) const; + + /** 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_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE | HA_NO_BLOBS; + } + + /** + 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; } + + double read_time(ha_rows) + { return 1.0; } + + int open(const char *name, int mode, uint test_if_locked); + + int close(void); + + int write_row(uchar *buf); + + void use_hidden_primary_key(); + + int update_row(const uchar *old_data, uchar *new_data); + + int rnd_init(bool scan); + + int rnd_end(void); + + int rnd_next(uchar *buf); + + int rnd_pos(uchar *buf, uchar *pos); + + void position(const uchar *record); + + int info(uint); + + int delete_all_rows(void); + + 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 *, char *, uint , qc_engine_callback *engine_callback, ulonglong *) + { + *engine_callback= 0; + return FALSE; + } + +private: + /** 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/pfs.cc b/storage/perfschema/pfs.cc new file mode 100644 index 00000000000..01b4b3711c1 --- /dev/null +++ b/storage/perfschema/pfs.cc @@ -0,0 +1,2053 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/pfs.cc + The performance schema implementation of all instruments. +*/ + +#include "my_global.h" +#include "pfs.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_global.h" +#include "pfs_column_values.h" +#include "pfs_timer.h" +#include "pfs_events_waits.h" + +/* Pending WL#4895 PERFORMANCE_SCHEMA Instrumenting Table IO */ +#undef HAVE_TABLE_WAIT + +/** + @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_system_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) If the performance schema is not initialized, do nothing + - (b) If the object acted upon is not instrumented, do nothing + - (c) otherwise, notify the performance schema of the operation + about to be performed. + + The implementation of the instrumentation interface can: + - decide that it is not interested by the event, and return NULL. + In this context, 'interested' means whether the instrumentation for + this object + event is turned on in the performance schema configuration + (the SETUP_ tables). + - decide that this event is to be instrumented. + In this case, the instrumentation returns an opaque pointer, + that acts as a listener. + + If a listener is returned, the instrumentation point then: + - (d) invokes the "start" event method + - (e) executes the instrumented code. + - (f) invokes the "end" event method. + + If no listener is returned, only the instrumented code (e) is invoked. + + 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 *locker= NULL; + + ...... (a) .......... (b) + if (PSI_server && that->m_psi) + + .......................... (c) + if ((locker= PSI_server->get_thread_mutex_locker(that->m_psi, + PSI_MUTEX_LOCK))) + + ............... (d) + PSI_server->start_mutex_wait(locker, src_file, src_line); + + ........ (e) + result= pthread_mutex_lock(&that->m_mutex); + + if (locker) + + ............. (f) + PSI_server->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; + + ........ (e) + result= pthread_mutex_lock(&that->m_mutex); + + 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 ? + + Function composition such as F_2_to_3 o F_1_to_2 o F1 is implemented + as PFS_single_stat_chain, where each link in the chain represents + an individual F_{i}_to_{i+1} aggregation step. + + A single call to aggregate_single_stat_chain() updates all the tables + described in the statistics chain. + + @section STAT_CHAIN Statistics chains + + Statistics chains are only used for on the fly aggregates, + and are therefore all based initially on the '_CURRENT' base table that + contains the data recorded. + The following table aggregates are implemented with a statistics chain: + + EVENTS_WAITS_CURRENT --> EVENTS_WAITS_SUMMARY_BY_INSTANCE + --> EVENTS_WAITS_SUMMARY_BY_EVENT_NAME + + This relationship is between classes. + + In terms of object instances, or records, this chain is implemented + as a flyweight. + + 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. + + @section HIGHER_LEVEL Higher level aggregates + + Note: no higher level aggregate is implemented yet, + this section is a place holder. +*/ + +/** + @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 +*/ + +pthread_key(PFS_thread*, THR_PFS); +bool THR_PFS_initialized= false; + +static enum_operation_type mutex_operation_map[]= +{ + OPERATION_TYPE_LOCK, + OPERATION_TYPE_TRYLOCK +}; + +static enum_operation_type rwlock_operation_map[]= +{ + OPERATION_TYPE_READLOCK, + OPERATION_TYPE_WRITELOCK, + OPERATION_TYPE_TRYREADLOCK, + OPERATION_TYPE_TRYWRITELOCK +}; + +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 +}; + +/** + 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_STRING *prefix, const char *category, + char *output, int *output_length) +{ + int len= strlen(category); + char *out_ptr= output; + int 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; + memcpy(out_ptr, category, len); + out_ptr+= len; + *out_ptr= '/'; + out_ptr++; + *output_length= 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]; \ + int prefix_length; \ + int len; \ + int full_length; \ + \ + DBUG_ASSERT(category != NULL); \ + DBUG_ASSERT(info != NULL); \ + if (unlikely(build_prefix(&PREFIX, category, \ + formatted_name, &prefix_length))) \ + { \ + 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, 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; + +static void 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) +} + +static void register_rwlock_v1(const char *category, + PSI_rwlock_info_v1 *info, + int count) +{ + REGISTER_BODY_V1(PSI_rwlock_key, + rwlock_instrument_prefix, + register_rwlock_class) +} + +static void 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) +} + +static void 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) +} + +static void 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) +} + +#define INIT_BODY_V1(T, KEY, ID) \ + PFS_##T##_class *klass; \ + PFS_##T *pfs; \ + PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); \ + if (unlikely(pfs_thread == NULL)) \ + return NULL; \ + if (! pfs_thread->m_enabled) \ + return NULL; \ + klass= find_##T##_class(KEY); \ + if (unlikely(klass == NULL)) \ + return NULL; \ + if (! klass->m_enabled) \ + return NULL; \ + pfs= create_##T(klass, ID); \ + return reinterpret_cast<PSI_##T *> (pfs) + +static PSI_mutex* +init_mutex_v1(PSI_mutex_key key, const void *identity) +{ + INIT_BODY_V1(mutex, key, identity); +} + +static void destroy_mutex_v1(PSI_mutex* mutex) +{ + PFS_mutex *pfs= reinterpret_cast<PFS_mutex*> (mutex); + destroy_mutex(pfs); +} + +static PSI_rwlock* +init_rwlock_v1(PSI_rwlock_key key, const void *identity) +{ + INIT_BODY_V1(rwlock, key, identity); +} + +static void destroy_rwlock_v1(PSI_rwlock* rwlock) +{ + PFS_rwlock *pfs= reinterpret_cast<PFS_rwlock*> (rwlock); + destroy_rwlock(pfs); +} + +static PSI_cond* +init_cond_v1(PSI_cond_key key, const void *identity) +{ + INIT_BODY_V1(cond, key, identity); +} + +static void destroy_cond_v1(PSI_cond* cond) +{ + PFS_cond *pfs= reinterpret_cast<PFS_cond*> (cond); + destroy_cond(pfs); +} + +static PSI_table_share* +get_table_share_v1(const char *schema_name, int schema_name_length, + const char *table_name, int table_name_length, + const void *identity) +{ +#ifdef HAVE_TABLE_WAIT + PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); + if (unlikely(pfs_thread == NULL)) + return NULL; + PFS_table_share* share; + share= find_or_create_table_share(pfs_thread, + schema_name, schema_name_length, + table_name, table_name_length); + return reinterpret_cast<PSI_table_share*> (share); +#else + return NULL; +#endif +} + +static void release_table_share_v1(PSI_table_share* share) +{ + /* + To be implemented by WL#4895 PERFORMANCE_SCHEMA Instrumenting Table IO. + */ +} + +static PSI_table* +open_table_v1(PSI_table_share *share, const void *identity) +{ + PFS_table_share *pfs_table_share= + reinterpret_cast<PFS_table_share*> (share); + PFS_table *pfs_table; + DBUG_ASSERT(pfs_table_share); + pfs_table= create_table(pfs_table_share, identity); + return reinterpret_cast<PSI_table *> (pfs_table); +} + +static void close_table_v1(PSI_table *table) +{ + PFS_table *pfs= reinterpret_cast<PFS_table*> (table); + DBUG_ASSERT(pfs); + destroy_table(pfs); +} + +static void create_file_v1(PSI_file_key key, const char *name, File file) +{ + int index= (int) file; + if (unlikely(index < 0)) + return; + PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); + if (unlikely(pfs_thread == NULL)) + return; + if (! pfs_thread->m_enabled) + return; + PFS_file_class *klass= find_file_class(key); + if (unlikely(klass == NULL)) + return; + if (! klass->m_enabled) + return; + if (likely(index < file_handle_max)) + { + uint len= strlen(name); + PFS_file *pfs= find_or_create_file(pfs_thread, klass, name, len); + file_handle_array[index]= pfs; + } + else + file_handle_lost++; +} + +struct PFS_spawn_thread_arg +{ + PFS_thread *m_parent_thread; + PSI_thread_key m_child_key; + const void *m_child_identity; + void *(*m_user_start_routine)(void*); + void *m_user_arg; +}; + +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); + else + pfs= NULL; + my_pthread_setspecific_ptr(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, MYF(0)); + + /* Then, execute the user code for this thread. */ + (*user_start_routine)(user_arg); + + return NULL; +} + +static int spawn_thread_v1(PSI_thread_key key, + pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine)(void*), void *arg) +{ + PFS_spawn_thread_arg *psi_arg; + + /* psi_arg can not be global, and can not be a local variable. */ + psi_arg= (PFS_spawn_thread_arg*) my_malloc(sizeof(PFS_spawn_thread_arg), + MYF(MY_WME)); + if (unlikely(psi_arg == NULL)) + return EAGAIN; + + psi_arg->m_parent_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); + 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; + + int result= pthread_create(thread, attr, pfs_spawn_thread, psi_arg); + if (unlikely(result != 0)) + my_free(psi_arg, MYF(0)); + return result; +} + +static PSI_thread* +new_thread_v1(PSI_thread_key key, const void *identity, ulong thread_id) +{ + PFS_thread *pfs; + + PFS_thread_class *klass= find_thread_class(key); + if (likely(klass != NULL)) + pfs= create_thread(klass, identity, thread_id); + else + pfs= NULL; + + return reinterpret_cast<PSI_thread*> (pfs); +} + +static void set_thread_id_v1(PSI_thread *thread, unsigned long id) +{ + DBUG_ASSERT(thread); + PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread); + pfs->m_thread_id= id; +} + +static PSI_thread* +get_thread_v1(void) +{ + PFS_thread *pfs= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); + return reinterpret_cast<PSI_thread*> (pfs); +} + +static void set_thread_v1(PSI_thread* thread) +{ + PFS_thread *pfs= reinterpret_cast<PFS_thread*> (thread); + my_pthread_setspecific_ptr(THR_PFS, pfs); +} + +static void delete_current_thread_v1(void) +{ + PFS_thread *thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); + if (thread != NULL) + { + my_pthread_setspecific_ptr(THR_PFS, NULL); + destroy_thread(thread); + } +} + +static PSI_mutex_locker* +get_thread_mutex_locker_v1(PSI_mutex *mutex, PSI_mutex_operation op) +{ + 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(pfs_mutex != NULL); + DBUG_ASSERT(pfs_mutex->m_class != NULL); + if (! flag_events_waits_current) + return NULL; + if (! pfs_mutex->m_class->m_enabled) + return NULL; + PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + if (unlikely(pfs_thread->m_wait_locker_count >= LOCKER_STACK_SIZE)) + { + locker_lost++; + return NULL; + } + PFS_wait_locker *pfs_locker= &pfs_thread->m_wait_locker_stack + [pfs_thread->m_wait_locker_count]; + pfs_locker->m_waits_current.m_wait_class= NO_WAIT_CLASS; + + pfs_locker->m_target.m_mutex= pfs_mutex; + pfs_locker->m_waits_current.m_thread= pfs_thread; + pfs_locker->m_waits_current.m_class= pfs_mutex->m_class; + if (pfs_mutex->m_class->m_timed) + { + pfs_locker->m_timer_name= wait_timer; + pfs_locker->m_waits_current.m_timer_state= TIMER_STATE_STARTING; + } + else + pfs_locker->m_waits_current.m_timer_state= TIMER_STATE_UNTIMED; + pfs_locker->m_waits_current.m_object_instance_addr= pfs_mutex->m_identity; + pfs_locker->m_waits_current.m_event_id= pfs_thread->m_event_id++; + pfs_locker->m_waits_current.m_operation= mutex_operation_map[(int) op]; + pfs_locker->m_waits_current.m_wait_class= WAIT_CLASS_MUTEX; + + pfs_thread->m_wait_locker_count++; + return reinterpret_cast<PSI_mutex_locker*> (pfs_locker); +} + +static PSI_rwlock_locker* +get_thread_rwlock_locker_v1(PSI_rwlock *rwlock, PSI_rwlock_operation op) +{ + 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(pfs_rwlock != NULL); + DBUG_ASSERT(pfs_rwlock->m_class != NULL); + if (! flag_events_waits_current) + return NULL; + if (! pfs_rwlock->m_class->m_enabled) + return NULL; + PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + if (unlikely(pfs_thread->m_wait_locker_count >= LOCKER_STACK_SIZE)) + { + locker_lost++; + return NULL; + } + PFS_wait_locker *pfs_locker= &pfs_thread->m_wait_locker_stack + [pfs_thread->m_wait_locker_count]; + pfs_locker->m_waits_current.m_wait_class= NO_WAIT_CLASS; + + pfs_locker->m_target.m_rwlock= pfs_rwlock; + pfs_locker->m_waits_current.m_thread= pfs_thread; + pfs_locker->m_waits_current.m_class= pfs_rwlock->m_class; + if (pfs_rwlock->m_class->m_timed) + { + pfs_locker->m_timer_name= wait_timer; + pfs_locker->m_waits_current.m_timer_state= TIMER_STATE_STARTING; + } + else + pfs_locker->m_waits_current.m_timer_state= TIMER_STATE_UNTIMED; + pfs_locker->m_waits_current.m_object_instance_addr= pfs_rwlock->m_identity; + pfs_locker->m_waits_current.m_event_id= pfs_thread->m_event_id++; + pfs_locker->m_waits_current.m_operation= + rwlock_operation_map[static_cast<int> (op)]; + pfs_locker->m_waits_current.m_wait_class= WAIT_CLASS_RWLOCK; + + pfs_thread->m_wait_locker_count++; + return reinterpret_cast<PSI_rwlock_locker*> (pfs_locker); +} + +static PSI_cond_locker* +get_thread_cond_locker_v1(PSI_cond *cond, PSI_mutex * /* unused: mutex */, + PSI_cond_operation op) +{ + /* + 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(pfs_cond != NULL); + DBUG_ASSERT(pfs_cond->m_class != NULL); + if (! flag_events_waits_current) + return NULL; + if (! pfs_cond->m_class->m_enabled) + return NULL; + PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + if (unlikely(pfs_thread->m_wait_locker_count >= LOCKER_STACK_SIZE)) + { + locker_lost++; + return NULL; + } + PFS_wait_locker *pfs_locker= &pfs_thread->m_wait_locker_stack + [pfs_thread->m_wait_locker_count]; + pfs_locker->m_waits_current.m_wait_class= NO_WAIT_CLASS; + + pfs_locker->m_target.m_cond= pfs_cond; + pfs_locker->m_waits_current.m_thread= pfs_thread; + pfs_locker->m_waits_current.m_class= pfs_cond->m_class; + if (pfs_cond->m_class->m_timed) + { + pfs_locker->m_timer_name= wait_timer; + pfs_locker->m_waits_current.m_timer_state= TIMER_STATE_STARTING; + } + else + pfs_locker->m_waits_current.m_timer_state= TIMER_STATE_UNTIMED; + pfs_locker->m_waits_current.m_object_instance_addr= pfs_cond->m_identity; + pfs_locker->m_waits_current.m_event_id= pfs_thread->m_event_id++; + pfs_locker->m_waits_current.m_operation= + cond_operation_map[static_cast<int> (op)]; + pfs_locker->m_waits_current.m_wait_class= WAIT_CLASS_COND; + + pfs_thread->m_wait_locker_count++; + return reinterpret_cast<PSI_cond_locker*> (pfs_locker); +} + +static PSI_table_locker* +get_thread_table_locker_v1(PSI_table *table) +{ + PFS_table *pfs_table= reinterpret_cast<PFS_table*> (table); + DBUG_ASSERT(pfs_table != NULL); + DBUG_ASSERT(pfs_table->m_share != NULL); + if (! flag_events_waits_current) + return NULL; + if (! pfs_table->m_share->m_enabled) + return NULL; + PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + if (unlikely(pfs_thread->m_wait_locker_count >= LOCKER_STACK_SIZE)) + { + locker_lost++; + return NULL; + } + PFS_wait_locker *pfs_locker= &pfs_thread->m_wait_locker_stack + [pfs_thread->m_wait_locker_count]; + pfs_locker->m_waits_current.m_wait_class= NO_WAIT_CLASS; + + pfs_locker->m_target.m_table= pfs_table; + pfs_locker->m_waits_current.m_thread= pfs_thread; + pfs_locker->m_waits_current.m_class= &global_table_class; + if (pfs_table->m_share->m_timed) + { + pfs_locker->m_timer_name= wait_timer; + pfs_locker->m_waits_current.m_timer_state= TIMER_STATE_STARTING; + } + else + pfs_locker->m_waits_current.m_timer_state= TIMER_STATE_UNTIMED; + pfs_locker->m_waits_current.m_object_instance_addr= pfs_table->m_identity; + pfs_locker->m_waits_current.m_event_id= pfs_thread->m_event_id++; + pfs_locker->m_waits_current.m_wait_class= WAIT_CLASS_TABLE; + + pfs_thread->m_wait_locker_count++; + return reinterpret_cast<PSI_table_locker*> (pfs_locker); +} + +static PSI_file_locker* +get_thread_file_name_locker_v1(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)); + + if (! flag_events_waits_current) + return NULL; + PFS_file_class *klass= find_file_class(key); + if (unlikely(klass == NULL)) + return NULL; + if (! klass->m_enabled) + return NULL; + PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + if (unlikely(pfs_thread->m_wait_locker_count >= LOCKER_STACK_SIZE)) + { + locker_lost++; + return NULL; + } + uint len= strlen(name); + PFS_file *pfs_file= find_or_create_file(pfs_thread, klass, name, len); + if (unlikely(pfs_file == NULL)) + return NULL; + + PFS_wait_locker *pfs_locker= &pfs_thread->m_wait_locker_stack + [pfs_thread->m_wait_locker_count]; + pfs_locker->m_waits_current.m_wait_class= NO_WAIT_CLASS; + + pfs_locker->m_target.m_file= pfs_file; + pfs_locker->m_waits_current.m_thread= pfs_thread; + pfs_locker->m_waits_current.m_class= pfs_file->m_class; + if (pfs_file->m_class->m_timed) + { + pfs_locker->m_timer_name= wait_timer; + pfs_locker->m_waits_current.m_timer_state= TIMER_STATE_STARTING; + } + else + pfs_locker->m_waits_current.m_timer_state= TIMER_STATE_UNTIMED; + pfs_locker->m_waits_current.m_object_instance_addr= pfs_file; + pfs_locker->m_waits_current.m_object_name= pfs_file->m_filename; + pfs_locker->m_waits_current.m_object_name_length= + pfs_file->m_filename_length; + pfs_locker->m_waits_current.m_event_id= pfs_thread->m_event_id++; + pfs_locker->m_waits_current.m_operation= + file_operation_map[static_cast<int> (op)]; + pfs_locker->m_waits_current.m_wait_class= WAIT_CLASS_FILE; + + pfs_thread->m_wait_locker_count++; + return reinterpret_cast<PSI_file_locker*> (pfs_locker); +} + +static PSI_file_locker* +get_thread_file_stream_locker_v1(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(pfs_file != NULL); + DBUG_ASSERT(pfs_file->m_class != NULL); + + if (! flag_events_waits_current) + return NULL; + if (! pfs_file->m_class->m_enabled) + return NULL; + PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + if (unlikely(pfs_thread->m_wait_locker_count >= LOCKER_STACK_SIZE)) + { + locker_lost++; + return NULL; + } + PFS_wait_locker *pfs_locker= &pfs_thread->m_wait_locker_stack + [pfs_thread->m_wait_locker_count]; + pfs_locker->m_waits_current.m_wait_class= NO_WAIT_CLASS; + + pfs_locker->m_target.m_file= pfs_file; + pfs_locker->m_waits_current.m_thread= pfs_thread; + pfs_locker->m_waits_current.m_class= pfs_file->m_class; + if (pfs_file->m_class->m_timed) + { + pfs_locker->m_timer_name= wait_timer; + pfs_locker->m_waits_current.m_timer_state= TIMER_STATE_STARTING; + } + else + pfs_locker->m_waits_current.m_timer_state= TIMER_STATE_UNTIMED; + pfs_locker->m_waits_current.m_object_instance_addr= pfs_file; + pfs_locker->m_waits_current.m_object_name= pfs_file->m_filename; + pfs_locker->m_waits_current.m_object_name_length= + pfs_file->m_filename_length; + pfs_locker->m_waits_current.m_event_id= pfs_thread->m_event_id++; + pfs_locker->m_waits_current.m_operation= + file_operation_map[static_cast<int> (op)]; + pfs_locker->m_waits_current.m_wait_class= WAIT_CLASS_FILE; + + pfs_thread->m_wait_locker_count++; + return reinterpret_cast<PSI_file_locker*> (pfs_locker); +} + +static PSI_file_locker* +get_thread_file_descriptor_locker_v1(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)); + + if (! flag_events_waits_current) + return NULL; + if (likely((index >= 0) && (index < file_handle_max))) + { + PFS_file *pfs_file= file_handle_array[index]; + if (likely(pfs_file != NULL)) + { + PFS_thread *pfs_thread; + + /* + 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; + + DBUG_ASSERT(pfs_file->m_class != NULL); + if (! pfs_file->m_class->m_enabled) + return NULL; + pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); + if (unlikely(pfs_thread == NULL)) + return NULL; + if (! pfs_thread->m_enabled) + return NULL; + if (unlikely(pfs_thread->m_wait_locker_count >= LOCKER_STACK_SIZE)) + { + locker_lost++; + return NULL; + } + PFS_wait_locker *pfs_locker= &pfs_thread->m_wait_locker_stack + [pfs_thread->m_wait_locker_count]; + pfs_locker->m_waits_current.m_wait_class= NO_WAIT_CLASS; + + pfs_locker->m_target.m_file= pfs_file; + pfs_locker->m_waits_current.m_thread= pfs_thread; + pfs_locker->m_waits_current.m_class= pfs_file->m_class; + if (pfs_file->m_class->m_timed) + { + pfs_locker->m_timer_name= wait_timer; + pfs_locker->m_waits_current.m_timer_state= TIMER_STATE_STARTING; + } + else + pfs_locker->m_waits_current.m_timer_state= TIMER_STATE_UNTIMED; + pfs_locker->m_waits_current.m_object_instance_addr= pfs_file; + pfs_locker->m_waits_current.m_object_name= pfs_file->m_filename; + pfs_locker->m_waits_current.m_object_name_length= + pfs_file->m_filename_length; + pfs_locker->m_waits_current.m_event_id= pfs_thread->m_event_id++; + pfs_locker->m_waits_current.m_operation= + file_operation_map[static_cast<int> (op)]; + pfs_locker->m_waits_current.m_wait_class= WAIT_CLASS_FILE; + + pfs_thread->m_wait_locker_count++; + return reinterpret_cast<PSI_file_locker*> (pfs_locker); + } + } + return NULL; +} + +static void unlock_mutex_v1(PSI_thread * thread, 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. + */ + PFS_thread *pfs_thread= reinterpret_cast<PFS_thread*> (thread); + DBUG_ASSERT(pfs_thread != NULL); + + if (unlikely(! flag_events_waits_current)) + return; + if (! pfs_mutex->m_class->m_enabled) + return; + if (! pfs_thread->m_enabled) + return; + + if (pfs_mutex->m_class->m_timed) + { + ulonglong locked_time; + locked_time= get_timer_value(wait_timer) - pfs_mutex->m_last_locked; + aggregate_single_stat_chain(&pfs_mutex->m_lock_stat, locked_time); + } +#endif +} + +static void unlock_rwlock_v1(PSI_thread *thread, PSI_rwlock *rwlock) +{ + PFS_rwlock *pfs_rwlock= reinterpret_cast<PFS_rwlock*> (rwlock); + DBUG_ASSERT(pfs_rwlock != NULL); + 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) + { + /* 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. */ + PFS_thread *pfs_thread= reinterpret_cast<PFS_thread*> (thread); + DBUG_ASSERT(pfs_thread != NULL); + + if (unlikely(! flag_events_waits_current)) + return; + if (! pfs_rwlock->m_class->m_enabled) + return; + if (! pfs_thread->m_enabled) + return; + + ulonglong locked_time; + if (last_writer) + { + if (pfs_rwlock->m_class->m_timed) + { + locked_time= get_timer_value(wait_timer) - pfs_rwlock->m_last_written; + aggregate_single_stat_chain(&pfs_rwlock->m_write_lock_stat, locked_time); + } + } + else if (last_reader) + { + if (pfs_rwlock->m_class->m_timed) + { + locked_time= get_timer_value(wait_timer) - pfs_rwlock->m_last_read; + aggregate_single_stat_chain(&pfs_rwlock->m_read_lock_stat, locked_time); + } + } +#endif +} + +static void signal_cond_v1(PSI_thread *thread, PSI_cond* cond) +{ + PFS_cond *pfs_cond= reinterpret_cast<PFS_cond*> (cond); + DBUG_ASSERT(pfs_cond != NULL); + + pfs_cond->m_cond_stat.m_signal_count++; +} + +static void broadcast_cond_v1(PSI_thread *thread, PSI_cond* cond) +{ + PFS_cond *pfs_cond= reinterpret_cast<PFS_cond*> (cond); + DBUG_ASSERT(pfs_cond != NULL); + + pfs_cond->m_cond_stat.m_broadcast_count++; +} + +static void start_mutex_wait_v1(PSI_mutex_locker* locker, + const char *src_file, uint src_line) +{ + PFS_wait_locker *pfs_locker= reinterpret_cast<PFS_wait_locker*> (locker); + DBUG_ASSERT(pfs_locker != NULL); + + PFS_events_waits *wait= &pfs_locker->m_waits_current; + if (wait->m_timer_state == TIMER_STATE_STARTING) + { + wait->m_timer_start= get_timer_value(pfs_locker->m_timer_name); + wait->m_timer_state= TIMER_STATE_STARTED; + } + wait->m_source_file= src_file; + wait->m_source_line= src_line; +} + +static void end_mutex_wait_v1(PSI_mutex_locker* locker, int rc) +{ + PFS_wait_locker *pfs_locker= reinterpret_cast<PFS_wait_locker*> (locker); + DBUG_ASSERT(pfs_locker != NULL); + PFS_events_waits *wait= &pfs_locker->m_waits_current; + + if (wait->m_timer_state == TIMER_STATE_STARTED) + { + wait->m_timer_end= get_timer_value(pfs_locker->m_timer_name); + wait->m_timer_state= TIMER_STATE_TIMED; + } + if (flag_events_waits_history) + insert_events_waits_history(wait->m_thread, wait); + if (flag_events_waits_history_long) + insert_events_waits_history_long(wait); + + if (rc == 0) + { + /* Thread safe: we are protected by the instrumented mutex */ + PFS_single_stat_chain *stat; + PFS_mutex *mutex= pfs_locker->m_target.m_mutex; + mutex->m_owner= wait->m_thread; + mutex->m_last_locked= wait->m_timer_end; + + ulonglong wait_time= wait->m_timer_end - wait->m_timer_start; + aggregate_single_stat_chain(&mutex->m_wait_stat, wait_time); + stat= find_per_thread_mutex_class_wait_stat(wait->m_thread, + mutex->m_class); + aggregate_single_stat_chain(stat, wait_time); + } + wait->m_thread->m_wait_locker_count--; +} + +static void start_rwlock_rdwait_v1(PSI_rwlock_locker* locker, + const char *src_file, uint src_line) +{ + PFS_wait_locker *pfs_locker= reinterpret_cast<PFS_wait_locker*> (locker); + DBUG_ASSERT(pfs_locker != NULL); + + PFS_events_waits *wait= &pfs_locker->m_waits_current; + if (wait->m_timer_state == TIMER_STATE_STARTING) + { + wait->m_timer_start= get_timer_value(pfs_locker->m_timer_name); + wait->m_timer_state= TIMER_STATE_STARTED; + } + wait->m_source_file= src_file; + wait->m_source_line= src_line; +} + +static void end_rwlock_rdwait_v1(PSI_rwlock_locker* locker, int rc) +{ + PFS_wait_locker *pfs_locker= reinterpret_cast<PFS_wait_locker*> (locker); + DBUG_ASSERT(pfs_locker != NULL); + PFS_events_waits *wait= &pfs_locker->m_waits_current; + + if (wait->m_timer_state == TIMER_STATE_STARTED) + { + wait->m_timer_end= get_timer_value(pfs_locker->m_timer_name); + wait->m_timer_state= TIMER_STATE_TIMED; + } + if (flag_events_waits_history) + insert_events_waits_history(wait->m_thread, wait); + if (flag_events_waits_history_long) + insert_events_waits_history_long(wait); + + 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. + */ + PFS_single_stat_chain *stat; + PFS_rwlock *rwlock= pfs_locker->m_target.m_rwlock; + if (rwlock->m_readers == 0) + rwlock->m_last_read= wait->m_timer_end; + rwlock->m_writer= NULL; + rwlock->m_readers++; + + ulonglong wait_time= wait->m_timer_end - wait->m_timer_start; + aggregate_single_stat_chain(&rwlock->m_wait_stat, wait_time); + stat= find_per_thread_rwlock_class_wait_stat(wait->m_thread, + rwlock->m_class); + aggregate_single_stat_chain(stat, wait_time); + } + wait->m_thread->m_wait_locker_count--; +} + +static void start_rwlock_wrwait_v1(PSI_rwlock_locker* locker, + const char *src_file, uint src_line) +{ + PFS_wait_locker *pfs_locker= reinterpret_cast<PFS_wait_locker*> (locker); + DBUG_ASSERT(pfs_locker != NULL); + + PFS_events_waits *wait= &pfs_locker->m_waits_current; + if (wait->m_timer_state == TIMER_STATE_STARTING) + { + wait->m_timer_start= get_timer_value(pfs_locker->m_timer_name); + wait->m_timer_state= TIMER_STATE_STARTED; + } + wait->m_source_file= src_file; + wait->m_source_line= src_line; +} + +static void end_rwlock_wrwait_v1(PSI_rwlock_locker* locker, int rc) +{ + PFS_wait_locker *pfs_locker= reinterpret_cast<PFS_wait_locker*> (locker); + DBUG_ASSERT(pfs_locker != NULL); + PFS_events_waits *wait= &pfs_locker->m_waits_current; + + if (wait->m_timer_state == TIMER_STATE_STARTED) + { + wait->m_timer_end= get_timer_value(pfs_locker->m_timer_name); + wait->m_timer_state= TIMER_STATE_TIMED; + } + if (flag_events_waits_history) + insert_events_waits_history(wait->m_thread, wait); + if (flag_events_waits_history_long) + insert_events_waits_history_long(wait); + + if (rc == 0) + { + /* Thread safe : we are protected by the instrumented rwlock */ + PFS_single_stat_chain *stat; + PFS_rwlock *rwlock= pfs_locker->m_target.m_rwlock; + rwlock->m_writer= wait->m_thread; + rwlock->m_last_written= wait->m_timer_end; + /* Reset the readers stats, they could be off */ + rwlock->m_readers= 0; + rwlock->m_last_read= 0; + + ulonglong wait_time= wait->m_timer_end - wait->m_timer_start; + aggregate_single_stat_chain(&rwlock->m_wait_stat, wait_time); + stat= find_per_thread_rwlock_class_wait_stat(wait->m_thread, + rwlock->m_class); + aggregate_single_stat_chain(stat, wait_time); + } + wait->m_thread->m_wait_locker_count--; +} + +static void start_cond_wait_v1(PSI_cond_locker* locker, + const char *src_file, uint src_line) +{ + PFS_wait_locker *pfs_locker= reinterpret_cast<PFS_wait_locker*> (locker); + DBUG_ASSERT(pfs_locker != NULL); + + PFS_events_waits *wait= &pfs_locker->m_waits_current; + if (wait->m_timer_state == TIMER_STATE_STARTING) + { + wait->m_timer_start= get_timer_value(pfs_locker->m_timer_name); + wait->m_timer_state= TIMER_STATE_STARTED; + } + wait->m_source_file= src_file; + wait->m_source_line= src_line; +} + +static void end_cond_wait_v1(PSI_cond_locker* locker, int rc) +{ + PFS_wait_locker *pfs_locker= reinterpret_cast<PFS_wait_locker*> (locker); + DBUG_ASSERT(pfs_locker != NULL); + PFS_events_waits *wait= &pfs_locker->m_waits_current; + + if (wait->m_timer_state == TIMER_STATE_STARTED) + { + wait->m_timer_end= get_timer_value(pfs_locker->m_timer_name); + wait->m_timer_state= TIMER_STATE_TIMED; + } + if (flag_events_waits_history) + insert_events_waits_history(wait->m_thread, wait); + if (flag_events_waits_history_long) + insert_events_waits_history_long(wait); + + if (rc == 0) + { + /* + Not thread safe, race conditions will occur. + A first race condition is: + - thread 1 waits on cond A + - thread 2 waits on cond B + threads 1 and 2 compete when updating the same cond A + statistics, possibly missing a min / max / sum / count. + A second race condition is: + - thread 1 waits on cond A + - thread 2 destroys cond A + - thread 2 or 3 creates cond B in the same condition slot + thread 1 will then aggregate statistics about defunct A + in condition B. + This is accepted, the data will be slightly inaccurate. + */ + PFS_single_stat_chain *stat; + PFS_cond *cond= pfs_locker->m_target.m_cond; + + ulonglong wait_time= wait->m_timer_end - wait->m_timer_start; + aggregate_single_stat_chain(&cond->m_wait_stat, wait_time); + stat= find_per_thread_cond_class_wait_stat(wait->m_thread, + cond->m_class); + aggregate_single_stat_chain(stat, wait_time); + } + wait->m_thread->m_wait_locker_count--; +} + +static void start_table_wait_v1(PSI_table_locker* locker, + const char *src_file, uint src_line) +{ + PFS_wait_locker *pfs_locker= reinterpret_cast<PFS_wait_locker*> (locker); + DBUG_ASSERT(pfs_locker != NULL); + + PFS_events_waits *wait= &pfs_locker->m_waits_current; + if (wait->m_timer_state == TIMER_STATE_STARTING) + { + wait->m_timer_start= get_timer_value(pfs_locker->m_timer_name); + wait->m_timer_state= TIMER_STATE_STARTED; + } + wait->m_source_file= src_file; + wait->m_source_line= src_line; + wait->m_operation= OPERATION_TYPE_LOCK; + PFS_table_share *share= pfs_locker->m_target.m_table->m_share; + wait->m_schema_name= share->m_schema_name; + wait->m_schema_name_length= share->m_schema_name_length; + wait->m_object_name= share->m_table_name; + wait->m_object_name_length= share->m_table_name_length; +} + +static void end_table_wait_v1(PSI_table_locker* locker) +{ + PFS_wait_locker *pfs_locker= reinterpret_cast<PFS_wait_locker*> (locker); + DBUG_ASSERT(pfs_locker != NULL); + PFS_events_waits *wait= &pfs_locker->m_waits_current; + + if (wait->m_timer_state == TIMER_STATE_STARTED) + { + wait->m_timer_end= get_timer_value(pfs_locker->m_timer_name); + wait->m_timer_state= TIMER_STATE_TIMED; + } + if (flag_events_waits_history) + insert_events_waits_history(wait->m_thread, wait); + if (flag_events_waits_history_long) + insert_events_waits_history_long(wait); + + PFS_table *table= pfs_locker->m_target.m_table; + ulonglong wait_time= wait->m_timer_end - wait->m_timer_start; + aggregate_single_stat_chain(&table->m_wait_stat, wait_time); + + /* + There is currently no per table and per thread aggregation. + The number of tables in the application is arbitrary, and may be high. + The number of slots per thread to hold aggregates is fixed, + and is constrained by memory. + Implementing a per thread and per table aggregate has not been + decided yet. + If it's implemented, it's likely that the user will have to specify, + per table name, if the aggregate per thread is to be computed or not. + This will mean a SETUP_ table. + */ + wait->m_thread->m_wait_locker_count--; +} + +static void start_file_wait_v1(PSI_file_locker *locker, + size_t count, + const char *src_file, + uint src_line); + +static void end_file_wait_v1(PSI_file_locker *locker, + size_t count); + +static PSI_file* start_file_open_wait_v1(PSI_file_locker *locker, + const char *src_file, + uint src_line) +{ + PFS_wait_locker *pfs_locker= reinterpret_cast<PFS_wait_locker*> (locker); + DBUG_ASSERT(pfs_locker != NULL); + + start_file_wait_v1(locker, 0, src_file, src_line); + + PFS_file *pfs_file= pfs_locker->m_target.m_file; + return reinterpret_cast<PSI_file*> (pfs_file); +} + +static void end_file_open_wait_v1(PSI_file_locker *locker) +{ + end_file_wait_v1(locker, 0); +} + +static void end_file_open_wait_and_bind_to_descriptor_v1 + (PSI_file_locker *locker, File file) +{ + int index= (int) file; + PFS_wait_locker *pfs_locker= reinterpret_cast<PFS_wait_locker*> (locker); + DBUG_ASSERT(pfs_locker != NULL); + + end_file_wait_v1(locker, 0); + + PFS_file *pfs_file= pfs_locker->m_target.m_file; + DBUG_ASSERT(pfs_file != NULL); + + if (likely(index >= 0)) + { + if (likely(index < file_handle_max)) + file_handle_array[index]= pfs_file; + else + file_handle_lost++; + } + else + release_file(pfs_file); +} + +static void start_file_wait_v1(PSI_file_locker *locker, + size_t count, + const char *src_file, + uint src_line) +{ + PFS_wait_locker *pfs_locker= reinterpret_cast<PFS_wait_locker*> (locker); + DBUG_ASSERT(pfs_locker != NULL); + + PFS_events_waits *wait= &pfs_locker->m_waits_current; + if (wait->m_timer_state == TIMER_STATE_STARTING) + { + wait->m_timer_start= get_timer_value(pfs_locker->m_timer_name); + wait->m_timer_state= TIMER_STATE_STARTED; + } + wait->m_source_file= src_file; + wait->m_source_line= src_line; + wait->m_number_of_bytes= count; +} + +static void end_file_wait_v1(PSI_file_locker *locker, + size_t count) +{ + PFS_wait_locker *pfs_locker= reinterpret_cast<PFS_wait_locker*> (locker); + DBUG_ASSERT(pfs_locker != NULL); + PFS_events_waits *wait= &pfs_locker->m_waits_current; + + wait->m_number_of_bytes= count; + if (wait->m_timer_state == TIMER_STATE_STARTED) + { + wait->m_timer_end= get_timer_value(pfs_locker->m_timer_name); + wait->m_timer_state= TIMER_STATE_TIMED; + } + if (flag_events_waits_history) + insert_events_waits_history(wait->m_thread, wait); + if (flag_events_waits_history_long) + insert_events_waits_history_long(wait); + + PFS_single_stat_chain *stat; + PFS_file *file= pfs_locker->m_target.m_file; + + ulonglong wait_time= wait->m_timer_end - wait->m_timer_start; + aggregate_single_stat_chain(&file->m_wait_stat, wait_time); + stat= find_per_thread_file_class_wait_stat(wait->m_thread, + file->m_class); + aggregate_single_stat_chain(stat, wait_time); + + PFS_file_class *klass= file->m_class; + + switch(wait->m_operation) + { + case OPERATION_TYPE_FILEREAD: + file->m_file_stat.m_count_read++; + file->m_file_stat.m_read_bytes+= count; + klass->m_file_stat.m_count_read++; + klass->m_file_stat.m_read_bytes+= count; + break; + case OPERATION_TYPE_FILEWRITE: + file->m_file_stat.m_count_write++; + file->m_file_stat.m_write_bytes+= count; + klass->m_file_stat.m_count_write++; + klass->m_file_stat.m_write_bytes+= count; + break; + case OPERATION_TYPE_FILECLOSE: + case OPERATION_TYPE_FILESTREAMCLOSE: + case OPERATION_TYPE_FILESTAT: + release_file(pfs_locker->m_target.m_file); + break; + case OPERATION_TYPE_FILEDELETE: + destroy_file(wait->m_thread, pfs_locker->m_target.m_file); + break; + default: + break; + } + + wait->m_thread->m_wait_locker_count--; +} + +PSI_v1 PFS_v1= +{ + register_mutex_v1, + register_rwlock_v1, + register_cond_v1, + register_thread_v1, + register_file_v1, + init_mutex_v1, + destroy_mutex_v1, + init_rwlock_v1, + destroy_rwlock_v1, + init_cond_v1, + destroy_cond_v1, + get_table_share_v1, + release_table_share_v1, + open_table_v1, + close_table_v1, + create_file_v1, + spawn_thread_v1, + new_thread_v1, + set_thread_id_v1, + get_thread_v1, + set_thread_v1, + delete_current_thread_v1, + get_thread_mutex_locker_v1, + get_thread_rwlock_locker_v1, + get_thread_cond_locker_v1, + get_thread_table_locker_v1, + get_thread_file_name_locker_v1, + get_thread_file_stream_locker_v1, + get_thread_file_descriptor_locker_v1, + unlock_mutex_v1, + unlock_rwlock_v1, + signal_cond_v1, + broadcast_cond_v1, + start_mutex_wait_v1, + end_mutex_wait_v1, + start_rwlock_rdwait_v1, + end_rwlock_rdwait_v1, + start_rwlock_wrwait_v1, + end_rwlock_wrwait_v1, + start_cond_wait_v1, + end_cond_wait_v1, + start_table_wait_v1, + end_table_wait_v1, + start_file_open_wait_v1, + end_file_open_wait_v1, + end_file_open_wait_and_bind_to_descriptor_v1, + start_file_wait_v1, + end_file_wait_v1 +}; + +static void* get_interface(int version) +{ + switch (version) + { + case PSI_VERSION_1: + return &PFS_v1; + default: + return NULL; + } +} + +struct PSI_bootstrap PFS_bootstrap= +{ + get_interface +}; + diff --git a/storage/perfschema/pfs.h b/storage/perfschema/pfs.h new file mode 100644 index 00000000000..7af59ffce57 --- /dev/null +++ b/storage/perfschema/pfs.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef PFS_H +#define PFS_H + +/** + @file storage/perfschema/pfs.h + Performance schema instrumentation (declarations). +*/ + +#define HAVE_PSI_1 + +#include <mysql_priv.h> +#include <mysql/psi/psi.h> + +extern struct PSI_bootstrap PFS_bootstrap; +extern pthread_key(PFS_thread*, THR_PFS); +extern bool THR_PFS_initialized; + +#endif + diff --git a/storage/perfschema/pfs_atomic.cc b/storage/perfschema/pfs_atomic.cc new file mode 100644 index 00000000000..c33bb251767 --- /dev/null +++ b/storage/perfschema/pfs_atomic.cc @@ -0,0 +1,78 @@ +/* Copyright (C) 2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/pfs_atomic.cc + Atomic operations (implementation). +*/ + +#include <my_global.h> +#include <my_pthread.h> +#include "pfs_atomic.h" + +/* + Using SAFE_MUTEX is impossible, because of recursion. + - code locks mutex X + - P_S records the event + - P_S needs an atomic counter A + - safe mutex called for m_mutex[hash(A)] + - safe mutex allocates/free memory + - safe mutex locks THR_LOCK_malloc + - P_S records the event + - P_S needs an atomic counter B + - safe mutex called for m_mutex[hash(B)] + + When hash(A) == hash(B), safe_mutex complains rightly that + the mutex is already locked. + In some cases, A == B, in particular for events_waits_history_long_index. + + In short, the implementation of PFS_atomic should not cause events + to be recorded in the performance schema. + + Also, because SAFE_MUTEX redefines pthread_mutex_t, etc, + this code is not inlined in pfs_atomic.h, but located here in pfs_atomic.cc. + + What is needed is a plain, unmodified, pthread_mutex_t. + This is provided by my_atomic_rwlock_t. +*/ + +/** + Internal rwlock array. + Using a single rwlock for all atomic operations would be a bottleneck. + Using a rwlock per performance schema structure would be too costly in + memory, and use too many rwlock. + The PFS_atomic implementation computes a hash value from the + atomic variable, to spread the bottleneck across 256 buckets, + while still providing --transparently for the caller-- an atomic + operation. +*/ +my_atomic_rwlock_t PFS_atomic::m_rwlock_array[256]; + +void PFS_atomic::init(void) +{ + uint i; + + for (i=0; i< array_elements(m_rwlock_array); i++) + my_atomic_rwlock_init(&m_rwlock_array[i]); +} + +void PFS_atomic::cleanup(void) +{ + uint i; + + for (i=0; i< array_elements(m_rwlock_array); i++) + my_atomic_rwlock_destroy(&m_rwlock_array[i]); +} + diff --git a/storage/perfschema/pfs_atomic.h b/storage/perfschema/pfs_atomic.h new file mode 100644 index 00000000000..7833c5f1c7a --- /dev/null +++ b/storage/perfschema/pfs_atomic.h @@ -0,0 +1,148 @@ +/* Copyright (C) 2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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: + static void init(); + static void cleanup(); + + /** Atomic load. */ + static inline int32 load_32(volatile int32 *ptr) + { + int32 result; + rdlock(ptr); + result= my_atomic_load32(ptr); + rdunlock(ptr); + return result; + } + + /** Atomic load. */ + static inline uint32 load_u32(volatile uint32 *ptr) + { + uint32 result; + rdlock(ptr); + result= (uint32) my_atomic_load32((int32*) ptr); + rdunlock(ptr); + return result; + } + + /** Atomic store. */ + static inline void store_32(volatile int32 *ptr, int32 value) + { + wrlock(ptr); + my_atomic_store32(ptr, value); + wrunlock(ptr); + } + + /** Atomic store. */ + static inline void store_u32(volatile uint32 *ptr, uint32 value) + { + wrlock(ptr); + my_atomic_store32((int32*) ptr, (int32) value); + wrunlock(ptr); + } + + /** Atomic add. */ + static inline int32 add_32(volatile int32 *ptr, int32 value) + { + int32 result; + wrlock(ptr); + result= my_atomic_add32(ptr, value); + wrunlock(ptr); + return result; + } + + /** Atomic add. */ + static inline uint32 add_u32(volatile uint32 *ptr, uint32 value) + { + uint32 result; + wrlock(ptr); + result= (uint32) my_atomic_add32((int32*) ptr, (int32) value); + wrunlock(ptr); + return result; + } + + /** Atomic compare and swap. */ + static inline bool cas_32(volatile int32 *ptr, int32 *old_value, + int32 new_value) + { + bool result; + wrlock(ptr); + result= my_atomic_cas32(ptr, old_value, new_value); + wrunlock(ptr); + return result; + } + + /** Atomic compare and swap. */ + static inline bool cas_u32(volatile uint32 *ptr, uint32 *old_value, + uint32 new_value) + { + bool result; + wrlock(ptr); + result= my_atomic_cas32((int32*) ptr, (int32*) old_value, + (uint32) new_value); + wrunlock(ptr); + return result; + } + +private: + static my_atomic_rwlock_t m_rwlock_array[256]; + + static inline my_atomic_rwlock_t *get_rwlock(volatile void *ptr) + { + /* + Divide an address by 8 to remove alignment, + modulo 256 to fall in the array. + */ + uint index= (((intptr) ptr) >> 3) & 0xFF; + my_atomic_rwlock_t *result= &m_rwlock_array[index]; + return result; + } + + static inline void rdlock(volatile void *ptr) + { + my_atomic_rwlock_rdlock(get_rwlock(ptr)); + } + + static inline void wrlock(volatile void *ptr) + { + my_atomic_rwlock_wrlock(get_rwlock(ptr)); + } + + static inline void rdunlock(volatile void *ptr) + { + my_atomic_rwlock_rdunlock(get_rwlock(ptr)); + } + + static inline void wrunlock(volatile void *ptr) + { + my_atomic_rwlock_wrunlock(get_rwlock(ptr)); + } +}; + +#endif + diff --git a/storage/perfschema/pfs_check.cc b/storage/perfschema/pfs_check.cc new file mode 100644 index 00000000000..5d3746d371c --- /dev/null +++ b/storage/perfschema/pfs_check.cc @@ -0,0 +1,62 @@ +/* Copyright (C) 2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/pfs_check.cc + Check the performance schema table structure. + The code in this file is implemented in pfs_check.cc + instead of pfs_server.cc, to separate dependencies to server + structures (THD, ...) in a dedicated file. + This code organization helps a lot maintenance of the unit tests. +*/ + +#include "my_global.h" +#include "mysql_priv.h" +#include "pfs_server.h" +#include "pfs_engine_table.h" + +/* +*/ + +/** + Check that the performance schema tables + have the expected structure. + Discrepancies are written in the server log, + but are not considered fatal, so this function does not + return an error code: + - some differences are compatible, and should not cause a failure + - some differences are not compatible, but then the DBA needs an operational + server to be able to DROP+CREATE the tables with the proper structure, + as part of the initial server installation or during an upgrade. + In case of discrepancies, later attempt to perform DML against + the performance schema will be rejected with an error. +*/ +void check_performance_schema() +{ + DBUG_ENTER("check_performance_schema"); + + THD *thd= new THD(); + if (thd == NULL) + DBUG_VOID_RETURN; + + thd->thread_stack= (char*) &thd; + thd->store_globals(); + + PFS_engine_table_share::check_all_tables(thd); + + delete thd; + DBUG_VOID_RETURN; +} + diff --git a/storage/perfschema/pfs_column_types.h b/storage/perfschema/pfs_column_types.h new file mode 100644 index 00000000000..c6f652d57a0 --- /dev/null +++ b/storage/perfschema/pfs_column_types.h @@ -0,0 +1,119 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 SOURCE columns. */ +#define COL_SOURCE_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 +}; + +#define FIRST_TIMER_NAME (static_cast<int> (TIMER_NAME_CYCLE)) +#define LAST_TIMER_NAME (static_cast<int> (TIMER_NAME_TICK)) +#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 +{ + OPERATION_TYPE_LOCK= 1, + OPERATION_TYPE_TRYLOCK= 2, + + OPERATION_TYPE_READLOCK= 3, + OPERATION_TYPE_WRITELOCK= 4, + OPERATION_TYPE_TRYREADLOCK= 5, + OPERATION_TYPE_TRYWRITELOCK= 6, + + OPERATION_TYPE_WAIT= 7, + OPERATION_TYPE_TIMEDWAIT= 8, + + OPERATION_TYPE_FILECREATE= 9, + OPERATION_TYPE_FILECREATETMP= 10, + OPERATION_TYPE_FILEOPEN= 11, + OPERATION_TYPE_FILESTREAMOPEN= 12, + OPERATION_TYPE_FILECLOSE= 13, + OPERATION_TYPE_FILESTREAMCLOSE= 14, + OPERATION_TYPE_FILEREAD= 15, + OPERATION_TYPE_FILEWRITE= 16, + OPERATION_TYPE_FILESEEK= 17, + OPERATION_TYPE_FILETELL= 18, + OPERATION_TYPE_FILEFLUSH= 19, + OPERATION_TYPE_FILESTAT= 20, + OPERATION_TYPE_FILEFSTAT= 21, + OPERATION_TYPE_FILECHSIZE= 22, + OPERATION_TYPE_FILEDELETE= 23, + OPERATION_TYPE_FILERENAME= 24, + OPERATION_TYPE_FILESYNC= 25 +}; +#define FIRST_OPERATION_TYPE (static_cast<int> (OPERATION_TYPE_LOCK)) +#define LAST_OPERATION_TYPE (static_cast<int> (OPERATION_TYPE_FILESYNC)) +#define COUNT_OPERATION_TYPE (LAST_OPERATION_TYPE - FIRST_OPERATION_TYPE + 1) + +#endif + diff --git a/storage/perfschema/pfs_column_values.cc b/storage/perfschema/pfs_column_values.cc new file mode 100644 index 00000000000..3143cd97e5d --- /dev/null +++ b/storage/perfschema/pfs_column_values.cc @@ -0,0 +1,42 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/pfs_column_values.cc + Literal values for columns used in the performance + schema tables (implementation). +*/ + +#include "mysql_priv.h" +#include "pfs_column_values.h" + +LEX_STRING PERFORMANCE_SCHEMA_str= +{ C_STRING_WITH_LEN("performance_schema") }; + +LEX_STRING mutex_instrument_prefix= +{ C_STRING_WITH_LEN("wait/synch/mutex/") }; + +LEX_STRING rwlock_instrument_prefix= +{ C_STRING_WITH_LEN("wait/synch/rwlock/") }; + +LEX_STRING cond_instrument_prefix= +{ C_STRING_WITH_LEN("wait/synch/cond/") }; + +LEX_STRING thread_instrument_prefix= +{ C_STRING_WITH_LEN("thread/") }; + +LEX_STRING file_instrument_prefix= +{ C_STRING_WITH_LEN("wait/io/file/") }; + diff --git a/storage/perfschema/pfs_column_values.h b/storage/perfschema/pfs_column_values.h new file mode 100644 index 00000000000..6cd2e8687d1 --- /dev/null +++ b/storage/perfschema/pfs_column_values.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef PFS_COLUMN_VALUES_H +#define PFS_COLUMN_VALUES_H + +/** + @file storage/perfschema/pfs_column_values.h + Literal values for columns used in the + performance schema tables (declarations). +*/ + +extern LEX_STRING PERFORMANCE_SCHEMA_str; + +extern LEX_STRING mutex_instrument_prefix; +extern LEX_STRING rwlock_instrument_prefix; +extern LEX_STRING cond_instrument_prefix; +extern LEX_STRING thread_instrument_prefix; +extern LEX_STRING file_instrument_prefix; + +#endif + diff --git a/storage/perfschema/pfs_engine_table.cc b/storage/perfschema/pfs_engine_table.cc new file mode 100644 index 00000000000..fe6783e10eb --- /dev/null +++ b/storage/perfschema/pfs_engine_table.cc @@ -0,0 +1,719 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/pfs_engine_table.cc + Performance schema tables (implementation). +*/ + +#include "mysql_priv.h" +#include "pfs_engine_table.h" + +#include "table_events_waits.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_processlist.h" +#include "table_events_waits_summary.h" +#include "table_sync_instances.h" +#include "table_file_instances.h" +#include "table_file_summary.h" + +/* For show status */ +#include "pfs_column_values.h" +#include "pfs_instr.h" + +/** + @addtogroup Performance_schema_engine + @{ +*/ + +static PFS_engine_table_share *all_shares[]= +{ + &table_events_waits_current::m_share, + &table_events_waits_history::m_share, + &table_events_waits_history_long::m_share, + &table_setup_consumers::m_share, + &table_setup_instruments::m_share, + &table_setup_objects::m_share, + &table_setup_timers::m_share, + &table_performance_timers::m_share, + &table_processlist::m_share, + &table_events_waits_summary_by_thread_by_event_name::m_share, + &table_events_waits_summary_by_event_name::m_share, + &table_events_waits_summary_by_instance::m_share, + &table_file_summary_by_event_name::m_share, + &table_file_summary_by_instance::m_share, + &table_mutex_instances::m_share, + &table_rwlock_instances::m_share, + &table_cond_instances::m_share, + &table_file_instances::m_share, + NULL +}; + +/** + Check all the tables structure. + @param thd current thread +*/ +void PFS_engine_table_share::check_all_tables(THD *thd) +{ + PFS_engine_table_share **current; + + DBUG_EXECUTE_IF("tampered_perfschema_table1", + { + /* Hack SETUP_INSTRUMENT, incompatible change. */ + all_shares[4]->m_field_def->count++; + }); + + for (current= &all_shares[0]; (*current) != NULL; current++) + (*current)->check_one_table(thd); +} + +class PFS_check_intact : public Table_check_intact +{ +protected: + virtual void report_error(uint code, const char *fmt, ...); + +public: + PFS_check_intact() + {} + + ~PFS_check_intact() + {} +}; + +void PFS_check_intact::report_error(uint code, const char *fmt, ...) +{ + va_list args; + char buff[MYSQL_ERRMSG_SIZE]; + + va_start(args, fmt); + my_vsnprintf(buff, sizeof(buff), fmt, args); + va_end(args); + + my_message(code, buff, MYF(0)); +} + +/** + Check integrity of the actual table schema. + The actual table schema (.frm) is compared to the expected schema. + @param thd current thread +*/ +void PFS_engine_table_share::check_one_table(THD *thd) +{ + TABLE_LIST tables; + + tables.init_one_table(PERFORMANCE_SCHEMA_str.str, + PERFORMANCE_SCHEMA_str.length, + m_name.str, m_name.length, + m_name.str, TL_READ); + + /* Work around until Bug#32115 is backported. */ + LEX dummy_lex; + LEX *old_lex= thd->lex; + thd->lex= &dummy_lex; + lex_start(thd); + + if (! simple_open_n_lock_tables(thd, &tables)) + { + PFS_check_intact checker; + + if (!checker.check(tables.table, m_field_def)) + m_checked= true; + close_thread_tables(thd); + } + + lex_end(&dummy_lex); + thd->lex= old_lex; +} + +/** 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); +} + +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) +{ + my_bitmap_map *org_bitmap; + + /* + Make sure the table structure is as expected before mapping + hard wired columns in read_row_values. + */ + if (! m_share_ptr->m_checked) + { + my_error(ER_WRONG_NATIVE_TABLE_STRUCTURE, MYF(0), + PERFORMANCE_SCHEMA_str.str, m_share_ptr->m_name); + return HA_ERR_TABLE_NEEDS_UPGRADE; + } + + /* 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 */ + org_bitmap= dbug_tmp_use_all_columns(table, table->write_set); + 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, + unsigned char *new_buf, + Field **fields) +{ + my_bitmap_map *org_bitmap; + + /* + Make sure the table structure is as expected before mapping + hard wired columns in update_row_values. + */ + if (! m_share_ptr->m_checked) + { + my_error(ER_WRONG_NATIVE_TABLE_STRUCTURE, MYF(0), + PERFORMANCE_SCHEMA_str.str, m_share_ptr->m_name); + return HA_ERR_TABLE_NEEDS_UPGRADE; + } + + /* We internally read from Fields to support the write interface */ + 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; +} + +/** + 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); +} + +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_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_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_utf8_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); +} + +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(); +} + +int PFS_readonly_table::update_row_values(TABLE *, + const unsigned char *, + unsigned char *, + Field **) +{ + my_error(ER_WRONG_PERFSCHEMA_USAGE, MYF(0)); + return HA_ERR_WRONG_COMMAND; +} + +class PFS_internal_schema_access : public ACL_internal_schema_access +{ +public: + PFS_internal_schema_access() + {} + + ~PFS_internal_schema_access() + {} + + ACL_internal_access_result check(ulong want_access, + ulong *save_priv) const; + + const ACL_internal_table_access *lookup(const char *name) const; +}; + +ACL_internal_access_result +PFS_internal_schema_access::check(ulong want_access, + ulong *save_priv) const +{ + const ulong 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)) + 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); + } +} + +PFS_readonly_acl pfs_readonly_acl; + +ACL_internal_access_result +PFS_readonly_acl::check(ulong want_access, ulong *save_priv) const +{ + const ulong 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)) + return ACL_INTERNAL_ACCESS_DENIED; + + return ACL_INTERNAL_ACCESS_CHECK_GRANT; +} + +PFS_truncatable_acl pfs_truncatable_acl; + +ACL_internal_access_result +PFS_truncatable_acl::check(ulong want_access, ulong *save_priv) const +{ + const ulong 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)) + return ACL_INTERNAL_ACCESS_DENIED; + + return ACL_INTERNAL_ACCESS_CHECK_GRANT; +} + +PFS_updatable_acl pfs_updatable_acl; + +ACL_internal_access_result +PFS_updatable_acl::check(ulong want_access, ulong *save_priv) const +{ + const ulong 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)) + 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(ulong want_access, ulong *save_priv) const +{ + const ulong always_forbidden= /* CREATE_ACL | */ REFERENCES_ACL + | INDEX_ACL | ALTER_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | TRIGGER_ACL; + + if (unlikely(want_access & always_forbidden)) + 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(ulong want_access, ulong *save_priv) const +{ + return ACL_INTERNAL_ACCESS_DENIED; +} + +/** + 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; + uint 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); + + uint total_memory= 0; + + for (i=0; /* empty */; i++) + { + switch (i){ + case 0: + name= "EVENTS_WAITS_CURRENT.ROW_SIZE"; + size= sizeof(PFS_wait_locker); + break; + case 1: + name= "EVENTS_WAITS_CURRENT.ROW_COUNT"; + size= LOCKER_STACK_SIZE * thread_max; + break; + case 2: + name= "EVENTS_WAITS_HISTORY.ROW_SIZE"; + size= sizeof(PFS_events_waits); + break; + case 3: + name= "EVENTS_WAITS_HISTORY.ROW_COUNT"; + size= events_waits_history_per_thread * thread_max; + break; + case 4: + name= "EVENTS_WAITS_HISTORY.MEMORY"; + size= events_waits_history_per_thread * thread_max + * sizeof(PFS_events_waits); + total_memory+= size; + break; + case 5: + name= "EVENTS_WAITS_HISTORY_LONG.ROW_SIZE"; + size= sizeof(PFS_events_waits); + break; + case 6: + name= "EVENTS_WAITS_HISTORY_LONG.ROW_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).ROW_SIZE"; + size= sizeof(PFS_mutex_class); + break; + case 9: + name= "(PFS_MUTEX_CLASS).ROW_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).ROW_SIZE"; + size= sizeof(PFS_rwlock_class); + break; + case 12: + name= "(PFS_RWLOCK_CLASS).ROW_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).ROW_SIZE"; + size= sizeof(PFS_cond_class); + break; + case 15: + name= "(PFS_COND_CLASS).ROW_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).ROW_SIZE"; + size= sizeof(PFS_thread_class); + break; + case 18: + name= "(PFS_THREAD_CLASS).ROW_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).ROW_SIZE"; + size= sizeof(PFS_file_class); + break; + case 21: + name= "(PFS_FILE_CLASS).ROW_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.ROW_SIZE"; + size= sizeof(PFS_mutex); + break; + case 24: + name= "MUTEX_INSTANCES.ROW_COUNT"; + size= mutex_max; + break; + case 25: + name= "MUTEX_INSTANCES.MEMORY"; + size= mutex_max * sizeof(PFS_mutex); + total_memory+= size; + break; + case 26: + name= "RWLOCK_INSTANCES.ROW_SIZE"; + size= sizeof(PFS_rwlock); + break; + case 27: + name= "RWLOCK_INSTANCES.ROW_COUNT"; + size= rwlock_max; + break; + case 28: + name= "RWLOCK_INSTANCES.MEMORY"; + size= rwlock_max * sizeof(PFS_rwlock); + total_memory+= size; + break; + case 29: + name= "COND_INSTANCES.ROW_SIZE"; + size= sizeof(PFS_cond); + break; + case 30: + name= "COND_INSTANCES.ROW_COUNT"; + size= cond_max; + break; + case 31: + name= "COND_INSTANCES.MEMORY"; + size= cond_max * sizeof(PFS_cond); + total_memory+= size; + break; + case 32: + name= "PROCESSLIST.ROW_SIZE"; + size= sizeof(PFS_thread); + break; + case 33: + name= "PROCESSLIST.ROW_COUNT"; + size= thread_max; + break; + case 34: + name= "PROCESSLIST.MEMORY"; + size= thread_max * sizeof(PFS_thread); + total_memory+= size; + break; + case 35: + name= "FILE_INSTANCES.ROW_SIZE"; + size= sizeof(PFS_file); + break; + case 36: + name= "FILE_INSTANCES.ROW_COUNT"; + size= file_max; + break; + case 37: + name= "FILE_INSTANCES.MEMORY"; + size= file_max * sizeof(PFS_file); + total_memory+= size; + break; + case 38: + name= "(PFS_FILE_HANDLE).ROW_SIZE"; + size= sizeof(PFS_file*); + break; + case 39: + name= "(PFS_FILE_HANDLE).ROW_COUNT"; + size= file_handle_max; + break; + case 40: + name= "(PFS_FILE_HANDLE).MEMORY"; + size= file_handle_max * sizeof(PFS_file*); + break; + case 41: + name= "EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME.ROW_SIZE"; + size= sizeof(PFS_single_stat_chain); + break; + case 42: + name= "EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME.ROW_COUNT"; + size= thread_max * instr_class_per_thread; + break; + case 43: + name= "EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME.MEMORY"; + size= thread_max * instr_class_per_thread * sizeof(PFS_single_stat_chain); + total_memory+= size; + break; + /* + This case must be last, + for aggregation in total_memory. + */ + case 44: + name= "PERFORMANCE_SCHEMA.MEMORY"; + size= total_memory; + break; + default: + goto end; + break; + } + + buflen= int10_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); +} + +/** @} */ + + diff --git a/storage/perfschema/pfs_engine_table.h b/storage/perfschema/pfs_engine_table.h new file mode 100644 index 00000000000..6a826c9da7d --- /dev/null +++ b/storage/perfschema/pfs_engine_table.h @@ -0,0 +1,336 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef PFS_ENGINE_TABLE_H +#define PFS_ENGINE_TABLE_H + +/** + @file storage/perfschema/pfs_engine_table.h + Performance schema tables (declarations). +*/ + +class Field; +struct PFS_engine_table_share; + +/** + @addtogroup Performance_schema_engine + @{ +*/ + +/** + 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, + unsigned char *new_buf, Field **fields); + + /** 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); + virtual void reset_position(void)= 0; + + /** Destructor. */ + virtual ~PFS_engine_table() + {} + +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, + unsigned char *new_buf, Field **fields)= 0; + + /** + 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) + {} + + void set_field_ulong(Field *f, ulong value); + void set_field_ulonglong(Field *f, ulonglong value); + void set_field_varchar_utf8(Field *f, const char* str, uint len); + void set_field_enum(Field *f, ulonglong value); + + ulonglong get_field_enum(Field *f); + + /** Table share. */ + const PFS_engine_table_share *m_share_ptr; + /** Opaque pointer to the m_pos position of this cursor. */ + void *m_pos_ptr; +}; + +/** 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, + unsigned char *buf, Field **fields); +/** Callback to delete all rows. */ +typedef int (*pfs_delete_all_rows_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 check_all_tables(THD *thd); + void check_one_table(THD *thd); + static void init_all_locks(void); + static void delete_all_locks(void); + + /** 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; + /** + Number or records. + This number does not need to be precise, + it is used by the optimizer to decide if the table + has 0, 1, or many records. + */ + ha_rows m_records; + /** 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 fields definition. */ + TABLE_FIELD_DEF *m_field_def; + /** Schema integrity flag. */ + bool m_checked; +}; + +/** Adapter for read only PERFORMANCE_SCHEMA tables. */ +class PFS_readonly_table : public PFS_engine_table +{ +protected: + /** + Constructor. + @param share table share + @param pos address of the m_pos position member + */ + PFS_readonly_table(const PFS_engine_table_share *share, void *pos) + : PFS_engine_table(share, pos) + {} + + ~PFS_readonly_table() + {} + + virtual int update_row_values(TABLE *table, const unsigned char *old_buf, + unsigned char *new_buf, Field **fields); + +}; + +class PFS_readonly_acl : public ACL_internal_table_access +{ +public: + PFS_readonly_acl() + {} + + ~PFS_readonly_acl() + {} + + ACL_internal_access_result check(ulong want_access, ulong *save_priv) const; +}; + +extern PFS_readonly_acl pfs_readonly_acl; + +class PFS_truncatable_acl : public ACL_internal_table_access +{ +public: + PFS_truncatable_acl() + {} + + ~PFS_truncatable_acl() + {} + + ACL_internal_access_result check(ulong want_access, ulong *save_priv) const; +}; + +extern PFS_truncatable_acl pfs_truncatable_acl; + +class PFS_updatable_acl : public ACL_internal_table_access +{ +public: + PFS_updatable_acl() + {} + + ~PFS_updatable_acl() + {} + + ACL_internal_access_result check(ulong want_access, ulong *save_priv) const; +}; + +extern PFS_updatable_acl pfs_updatable_acl; + +class PFS_editable_acl : public ACL_internal_table_access +{ +public: + PFS_editable_acl() + {} + + ~PFS_editable_acl() + {} + + ACL_internal_access_result check(ulong want_access, ulong *save_priv) const; +}; + +extern PFS_editable_acl pfs_editable_acl; + +class PFS_unknown_acl : public ACL_internal_table_access +{ +public: + PFS_unknown_acl() + {} + + ~PFS_unknown_acl() + {} + + ACL_internal_access_result check(ulong want_access, ulong *save_priv) const; +}; + +extern PFS_unknown_acl pfs_unknown_acl; + +/** Position of a cursor, for simple iterations. */ +struct PFS_simple_index +{ + /** Current row index. */ + uint m_index; + + PFS_simple_index(uint index) + : m_index(index) + {} + + void set_at(const struct PFS_simple_index *other) + { m_index= other->m_index; } + + void set_after(const struct PFS_simple_index *other) + { m_index= other->m_index + 1; } + + void next(void) + { m_index++; } +}; + +struct PFS_double_index +{ + /** Outer index. */ + uint m_index_1; + /** Current index within index_1. */ + uint m_index_2; + + PFS_double_index(uint index_1, uint index_2) + : m_index_1(index_1), m_index_2(index_2) + {} + + void set_at(const struct PFS_double_index *other) + { + m_index_1= other->m_index_1; + m_index_2= other->m_index_2; + } + + void set_after(const struct PFS_double_index *other) + { + m_index_1= other->m_index_1; + m_index_2= other->m_index_2 + 1; + } +}; + +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; + + 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) + {} + + 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; + } + + 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; + } +}; + +struct PFS_instrument_view_constants +{ + static const uint VIEW_MUTEX= 1; + static const uint VIEW_RWLOCK= 2; + static const uint VIEW_COND= 3; + static const uint VIEW_FILE= 4; +}; + +struct PFS_object_view_constants +{ + static const uint VIEW_TABLE= 1; + static const uint VIEW_EVENT= 2; + static const uint VIEW_PROCEDURE= 3; + static const uint VIEW_FUNCTION= 4; +}; + +bool pfs_show_status(handlerton *hton, THD *thd, + stat_print_fn *print, enum ha_stat_type stat); + +/** @} */ +#endif diff --git a/storage/perfschema/pfs_events_waits.cc b/storage/perfschema/pfs_events_waits.cc new file mode 100644 index 00000000000..22448af7c5f --- /dev/null +++ b/storage/perfschema/pfs_events_waits.cc @@ -0,0 +1,197 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.h" +#include "pfs_events_waits.h" +#include "pfs_atomic.h" +#include "m_string.h" + +ulong events_waits_history_long_size= 0; +/** Consumer flag for table EVENTS_WAITS_CURRENT. */ +bool flag_events_waits_current= true; +/** Consumer flag for table EVENTS_WAITS_HISTORY. */ +bool flag_events_waits_history= true; +/** Consumer flag for table EVENTS_WAITS_HISTORY_LONG. */ +bool flag_events_waits_history_long= true; +/** Consumer flag for table EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME. */ +bool flag_events_waits_summary_by_thread_by_event_name= true; +/** Consumer flag for table EVENTS_WAITS_SUMMARY_BY_EVENT_NAME. */ +bool flag_events_waits_summary_by_event_name= true; +/** Consumer flag for table EVENTS_WAITS_SUMMARY_BY_INSTANCE. */ +bool flag_events_waits_summary_by_instance= true; +bool flag_events_locks_summary_by_thread_by_event_name= true; +bool flag_events_locks_summary_by_event_name= true; +bool flag_events_locks_summary_by_instance= true; +/** Consumer flag for table FILE_SUMMARY_BY_EVENT_NAME. */ +bool flag_file_summary_by_event_name= true; +/** Consumer flag for table FILE_SUMMARY_BY_INSTANCE. */ +bool flag_file_summary_by_instance= true; + +/** True if EVENTS_WAITS_HISTORY_LONG circular buffer is full. */ +bool events_waits_history_long_full= false; +/** Index in EVENTS_WAITS_HISTORY_LONG circular buffer. */ +volatile uint32 events_waits_history_long_index= 0; +/** EVENTS_WAITS_HISTORY_LONG circular buffer. */ +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, 0); + + if (events_waits_history_long_size == 0) + return 0; + + events_waits_history_long_array= + PFS_MALLOC_ARRAY(events_waits_history_long_size, 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(events_waits_history_long_array); + events_waits_history_long_array= NULL; +} + +static void copy_events_waits(PFS_events_waits *dest, + const PFS_events_waits *source) +{ + /* m_wait_class must be the first member of PFS_events_waits. */ + compile_time_assert(offsetof(PFS_events_waits, m_wait_class) == 0); + + char* dest_body= (reinterpret_cast<char*> (dest)) + sizeof(events_waits_class); + const char* source_body= (reinterpret_cast<const char*> (source)) + + sizeof(events_waits_class); + + /* See comments in table_events_waits_common::make_row(). */ + + /* Signal readers they are about to read garbage ... */ + dest->m_wait_class= NO_WAIT_CLASS; + /* ... that this can generate. */ + memcpy_fixed(dest_body, + source_body, + sizeof(PFS_events_waits) - sizeof(events_waits_class)); + /* Signal readers the record is now clean again. */ + dest->m_wait_class= source->m_wait_class; +} + +/** + 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) +{ + 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 truncated data will have + wait->m_wait_class == NO_WAIT_CLASS, + which readers of m_waits_history will filter 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) +{ + uint index= PFS_atomic::add_u32(&events_waits_history_long_index, 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); +} + +/** Reset table EVENTS_WAITS_CURRENT data. */ +void reset_events_waits_current(void) +{ + PFS_thread *pfs_thread= thread_array; + PFS_thread *pfs_thread_last= thread_array + thread_max; + + for ( ; pfs_thread < pfs_thread_last; pfs_thread++) + { + PFS_wait_locker *locker= pfs_thread->m_wait_locker_stack; + PFS_wait_locker *locker_last= locker + LOCKER_STACK_SIZE; + + for ( ; locker < locker_last; locker++) + locker->m_waits_current.m_wait_class= NO_WAIT_CLASS; + } +} + +/** Reset table EVENTS_WAITS_HISTORY data. */ +void reset_events_waits_history(void) +{ + PFS_thread *pfs_thread= thread_array; + PFS_thread *pfs_thread_last= thread_array + thread_max; + + for ( ; pfs_thread < pfs_thread_last; 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_LONG data. */ +void reset_events_waits_history_long(void) +{ + PFS_atomic::store_u32(&events_waits_history_long_index, 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; +} + diff --git a/storage/perfschema/pfs_events_waits.h b/storage/perfschema/pfs_events_waits.h new file mode 100644 index 00000000000..c677e83ad34 --- /dev/null +++ b/storage/perfschema/pfs_events_waits.h @@ -0,0 +1,185 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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" + +struct PFS_mutex; +struct PFS_rwlock; +struct PFS_cond; +struct PFS_table; +struct PFS_file; +struct PFS_thread; +struct PFS_instr_class; + +/** 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 +}; + +/** State of a timer. */ +enum timer_state +{ + /** + Not timed. + In this state, TIMER_START, TIMER_END and TIMER_WAIT are NULL. + */ + TIMER_STATE_UNTIMED, + /** + About to start. + In this state, TIMER_START, TIMER_END and TIMER_WAIT are NULL. + */ + TIMER_STATE_STARTING, + /** + Started, but not yet ended. + In this state, TIMER_START has a value, TIMER_END and TIMER_WAIT are NULL. + */ + TIMER_STATE_STARTED, + /** + Ended. + In this state, TIMER_START, TIMER_END and TIMER_WAIT have a value. + */ + TIMER_STATE_TIMED +}; + +/** Target object a wait event is waiting on. */ +union events_waits_target +{ + /** Mutex waited on. */ + PFS_mutex *m_mutex; + /** RWLock waited on. */ + PFS_rwlock *m_rwlock; + /** Condition waited on. */ + PFS_cond *m_cond; + /** Table waited on. */ + PFS_table *m_table; + /** File waited on. */ + PFS_file *m_file; +}; + +/** A wait event record. */ +struct PFS_events_waits +{ + /** + 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 + */ + volatile events_waits_class m_wait_class; + /** Executing thread. */ + PFS_thread *m_thread; + /** Instrument metadata. */ + PFS_instr_class *m_class; + /** Timer state. */ + enum timer_state m_timer_state; + /** Event id. */ + ulonglong m_event_id; + /** + Timer start. + This member is populated only if m_timed is true. + */ + ulonglong m_timer_start; + /** + Timer end. + This member is populated only if m_timed is true. + */ + ulonglong m_timer_end; + /** Schema name. */ + const char *m_schema_name; + /** Length in bytes of @c m_schema_name. */ + uint m_schema_name_length; + /** Object name. */ + const char *m_object_name; + /** Length in bytes of @c m_object_name. */ + uint m_object_name_length; + /** Address in memory of the object instance waited on. */ + const void *m_object_instance_addr; + /** 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; + /** Operation performed. */ + enum_operation_type m_operation; + /** + Number of bytes read/written. + This member is populated for file READ/WRITE operations only. + */ + size_t m_number_of_bytes; +}; + +/** + A wait locker. + A locker is a transient helper structure used by the instrumentation + during the recording of a wait. +*/ +struct PFS_wait_locker +{ + /** The timer used to measure the wait. */ + enum_timer_name m_timer_name; + /** The object waited on. */ + events_waits_target m_target; + /** The wait data recorded. */ + PFS_events_waits m_waits_current; +}; + +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_events_waits_summary_by_thread_by_event_name; +extern bool flag_events_waits_summary_by_event_name; +extern bool flag_events_waits_summary_by_instance; +extern bool flag_events_locks_summary_by_thread_by_name; +extern bool flag_events_locks_summary_by_event_name; +extern bool flag_events_locks_summary_by_instance; +extern bool flag_file_summary_by_event_name; +extern bool flag_file_summary_by_instance; +extern bool events_waits_history_long_full; +extern volatile 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(); + +#endif + diff --git a/storage/perfschema/pfs_global.cc b/storage/perfschema/pfs_global.cc new file mode 100644 index 00000000000..cac7d0b06e7 --- /dev/null +++ b/storage/perfschema/pfs_global.cc @@ -0,0 +1,66 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/pfs_global.cc + Miscellaneous global dependencies (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "pfs_global.h" + +#include <stdlib.h> +#include <string.h> + +bool pfs_initialized= false; + +/** + Memory allocation for the performance schema. + The memory used internally in the performance schema implementation + is allocated once during startup, and considered static thereafter. +*/ +void *pfs_malloc(size_t size, myf flags) +{ + DBUG_ASSERT(! pfs_initialized); + DBUG_ASSERT(size > 0); + + void *ptr= malloc(size); + if (ptr && (flags & MY_ZEROFILL)) + memset(ptr, 0, size); + return ptr; +} + +void pfs_free(void *ptr) +{ + if (ptr != NULL) + free(ptr); +} + +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); +} + diff --git a/storage/perfschema/pfs_global.h b/storage/perfschema/pfs_global.h new file mode 100644 index 00000000000..37809f8cc2e --- /dev/null +++ b/storage/perfschema/pfs_global.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef PFS_GLOBAL_H +#define PFS_GLOBAL_H + +/** + @file storage/perfschema/pfs_global.h + Miscellaneous global dependencies (declarations). +*/ + +extern bool pfs_initialized; + +void *pfs_malloc(size_t size, myf flags); +#define PFS_MALLOC_ARRAY(n, T, f) \ + reinterpret_cast<T*> (pfs_malloc((n) * sizeof(T), (f))) +void pfs_free(void *ptr); + +inline uint randomized_index(const void *ptr, uint max_size) +{ + if (unlikely(max_size == 0)) + return 0; + + /* + ptr is typically an aligned structure, + so the last bits are not really random, but this has no effect. + Apply a factor A*x to spread + close values of ptr further apart (which helps with arrays), + and to spread values way beyond a typical max_size. + Then, apply a modulo to end within [0, max_size - 1]. + A is big prime numbers, to avoid resonating with max_size, + to have a uniform distribution in [0, max_size - 1]. + The value of A is chosen so that index(ptr) and index(ptr + N) (for arrays) + are likely to be not similar for typical values of max_size + (50, 100, 1000, etc). + In other words, (sizeof(T)*A % max_size) should not be a small number, + to avoid that with 'T array[max_size]', index(array[i]) + and index(array[i + 1]) end up pointing in the same area in [0, max_size - 1]. + */ + return static_cast<uint> + (((reinterpret_cast<intptr> (ptr)) * 2166179) % max_size); +} + +void pfs_print_error(const char *format, ...); + +#endif + diff --git a/storage/perfschema/pfs_instr.cc b/storage/perfschema/pfs_instr.cc new file mode 100644 index 00000000000..2ce3a844290 --- /dev/null +++ b/storage/perfschema/pfs_instr.cc @@ -0,0 +1,962 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/pfs_instr.cc + Performance schema instruments (implementation). +*/ + +#include "my_global.h" +#include "mysql_priv.h" +#include "my_sys.h" +#include "pfs_stat.h" +#include "pfs_instr.h" +#include "pfs_global.h" + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +/** Size of the mutex instances array. @sa mutex_array */ +ulong mutex_max; +/** Number of mutexes instance lost. @sa mutex_array */ +ulong mutex_lost; +/** Size of the rwlock instances array. @sa rwlock_array */ +ulong rwlock_max; +/** Number or rwlock instances lost. @sa rwlock_array */ +ulong rwlock_lost; +/** Size of the conditions instances array. @sa cond_array */ +ulong cond_max; +/** Number of conditions instances lost. @sa cond_array */ +ulong cond_lost; +/** Size of the thread instances array. @sa thread_array */ +ulong thread_max; +/** Number or thread instances lost. @sa thread_array */ +ulong thread_lost; +/** Size of the file instances array. @sa file_array */ +ulong file_max; +/** Number of file instances lost. @sa file_array */ +ulong file_lost; +/** + Size of the file handle array. @sa file_handle_array. + Signed value, for easier comparisons with a file descriptor number. +*/ +long file_handle_max; +/** Number of file handle lost. @sa file_handle_array */ +ulong file_handle_lost; +/** Size of the table instances array. @sa table_array */ +ulong table_max; +/** Number of table instances lost. @sa table_array */ +ulong table_lost; +/** Number of EVENTS_WAITS_HISTORY records per thread. */ +ulong events_waits_history_per_thread; +/** Number of instruments class per thread. */ +ulong instr_class_per_thread; +/** Number of locker lost. @sa LOCKER_STACK_SIZE. */ +ulong locker_lost; + +/** + Mutex instrumentation instances array. + @sa mutex_max + @sa mutex_lost +*/ +PFS_mutex *mutex_array= NULL; + +/** + RWLock instrumentation instances array. + @sa rwlock_max + @sa rwlock_lost +*/ +PFS_rwlock *rwlock_array= NULL; + +/** + Condition instrumentation instances array. + @sa cond_max + @sa cond_lost +*/ +PFS_cond *cond_array= NULL; + +/** + Thread instrumentation instances array. + @sa thread_max + @sa thread_lost +*/ +PFS_thread *thread_array= NULL; + +/** + File instrumentation instances array. + @sa file_max + @sa file_lost + @sa filename_hash +*/ +PFS_file *file_array= NULL; + +/** + File instrumentation handle array. + @sa file_handle_max + @sa file_handle_lost +*/ +PFS_file **file_handle_array= NULL; + +/** + Table instrumentation instances array. + @sa table_max + @sa table_lost +*/ +PFS_table *table_array= NULL; + +static volatile uint32 thread_internal_id_counter= 0; + +static uint per_thread_rwlock_class_start; +static uint per_thread_cond_class_start; +static uint per_thread_file_class_start; +static uint thread_instr_class_waits_sizing; +static PFS_single_stat_chain *thread_instr_class_waits_array= NULL; + +static PFS_events_waits *thread_history_array= NULL; + +/** Hash table for instrumented files. */ +static LF_HASH filename_hash; +/** True if filename_hash is initialized. */ +static bool filename_hash_inited= false; + +/** + Initialize all the instruments instance buffers. + @param param sizing parameters + @return 0 on success +*/ +int init_instruments(const PFS_global_param *param) +{ + uint thread_history_sizing; + uint index; + + mutex_max= param->m_mutex_sizing; + mutex_lost= 0; + rwlock_max= param->m_rwlock_sizing; + rwlock_lost= 0; + cond_max= param->m_cond_sizing; + cond_lost= 0; + file_max= param->m_file_sizing; + file_lost= 0; + file_handle_max= param->m_file_handle_sizing; + file_handle_lost= 0; + table_max= param->m_table_sizing; + table_lost= 0; + thread_max= param->m_thread_sizing; + thread_lost= 0; + + events_waits_history_per_thread= param->m_events_waits_history_sizing; + thread_history_sizing= param->m_thread_sizing + * events_waits_history_per_thread; + + per_thread_rwlock_class_start= param->m_mutex_class_sizing; + per_thread_cond_class_start= per_thread_rwlock_class_start + + param->m_rwlock_class_sizing; + per_thread_file_class_start= per_thread_cond_class_start + + param->m_cond_class_sizing; + instr_class_per_thread= per_thread_file_class_start + + param->m_file_class_sizing; + + thread_instr_class_waits_sizing= param->m_thread_sizing + * instr_class_per_thread; + + mutex_array= NULL; + rwlock_array= NULL; + cond_array= NULL; + file_array= NULL; + file_handle_array= NULL; + table_array= NULL; + thread_array= NULL; + thread_history_array= NULL; + thread_instr_class_waits_array= NULL; + thread_internal_id_counter= 0; + + if (mutex_max > 0) + { + mutex_array= PFS_MALLOC_ARRAY(mutex_max, PFS_mutex, MYF(MY_ZEROFILL)); + if (unlikely(mutex_array == NULL)) + return 1; + } + + if (rwlock_max > 0) + { + rwlock_array= PFS_MALLOC_ARRAY(rwlock_max, PFS_rwlock, MYF(MY_ZEROFILL)); + if (unlikely(rwlock_array == NULL)) + return 1; + } + + if (cond_max > 0) + { + cond_array= PFS_MALLOC_ARRAY(cond_max, PFS_cond, MYF(MY_ZEROFILL)); + if (unlikely(cond_array == NULL)) + return 1; + } + + if (file_max > 0) + { + file_array= PFS_MALLOC_ARRAY(file_max, PFS_file, MYF(MY_ZEROFILL)); + if (unlikely(file_array == NULL)) + return 1; + } + + if (file_handle_max > 0) + { + file_handle_array= PFS_MALLOC_ARRAY(file_handle_max, PFS_file*, MYF(MY_ZEROFILL)); + if (unlikely(file_handle_array == NULL)) + return 1; + } + + if (table_max > 0) + { + table_array= PFS_MALLOC_ARRAY(table_max, PFS_table, MYF(MY_ZEROFILL)); + if (unlikely(table_array == NULL)) + return 1; + } + + if (thread_max > 0) + { + thread_array= PFS_MALLOC_ARRAY(thread_max, PFS_thread, MYF(MY_ZEROFILL)); + if (unlikely(thread_array == NULL)) + return 1; + } + + if (thread_history_sizing > 0) + { + thread_history_array= + PFS_MALLOC_ARRAY(thread_history_sizing, PFS_events_waits, + MYF(MY_ZEROFILL)); + if (unlikely(thread_history_array == NULL)) + return 1; + } + + if (thread_instr_class_waits_sizing > 0) + { + thread_instr_class_waits_array= + PFS_MALLOC_ARRAY(thread_instr_class_waits_sizing, + PFS_single_stat_chain, MYF(MY_ZEROFILL)); + if (unlikely(thread_instr_class_waits_array == NULL)) + return 1; + } + + for (index= 0; index < thread_instr_class_waits_sizing; index++) + { + /* + Currently, this chain is of length 1, + but it's still implemented as a stat chain, + since more aggregations are planned to be implemented in m_parent. + */ + thread_instr_class_waits_array[index].m_control_flag= + &flag_events_waits_summary_by_thread_by_event_name; + thread_instr_class_waits_array[index].m_parent= NULL; + } + + for (index= 0; index < thread_max; index++) + { + thread_array[index].m_waits_history= + &thread_history_array[index * events_waits_history_per_thread]; + thread_array[index].m_instr_class_wait_stats= + &thread_instr_class_waits_array[index * instr_class_per_thread]; + } + + return 0; +} + +/** + Find the per-thread wait statistics for a mutex class. + @param thread input thread + @param klass mutex class + @return the per thread per mutex class wait stat +*/ +PFS_single_stat_chain * +find_per_thread_mutex_class_wait_stat(PFS_thread *thread, + PFS_mutex_class *klass) +{ + PFS_single_stat_chain *stat; + uint index; + + DBUG_ASSERT(thread != NULL); + DBUG_ASSERT(klass != NULL); + index= klass->m_index; + DBUG_ASSERT(index < mutex_class_max); + + stat= &(thread->m_instr_class_wait_stats[index]); + return stat; +} + +/** + Find the per-thread wait statistics for a rwlock class. + @param thread input thread + @param klass rwlock class + @return the per thread per rwlock class wait stat +*/ +PFS_single_stat_chain * +find_per_thread_rwlock_class_wait_stat(PFS_thread *thread, + PFS_rwlock_class *klass) +{ + PFS_single_stat_chain *stat; + uint index; + + DBUG_ASSERT(thread != NULL); + DBUG_ASSERT(klass != NULL); + index= klass->m_index; + DBUG_ASSERT(index < rwlock_class_max); + + stat= &(thread->m_instr_class_wait_stats + [per_thread_rwlock_class_start + index]); + return stat; +} + +/** + Find the per-thread wait statistics for a condition class. + @param thread input thread + @param klass condition class + @return the per thread per condition class wait stat +*/ +PFS_single_stat_chain * +find_per_thread_cond_class_wait_stat(PFS_thread *thread, + PFS_cond_class *klass) +{ + PFS_single_stat_chain *stat; + uint index; + + DBUG_ASSERT(thread != NULL); + DBUG_ASSERT(klass != NULL); + index= klass->m_index; + DBUG_ASSERT(index < cond_class_max); + + stat= &(thread->m_instr_class_wait_stats + [per_thread_cond_class_start + index]); + return stat; +} + +/** + Find the per-thread wait statistics for a file class. + @param thread input thread + @param klass file class + @return the per thread per file class wait stat +*/ +PFS_single_stat_chain * +find_per_thread_file_class_wait_stat(PFS_thread *thread, + PFS_file_class *klass) +{ + PFS_single_stat_chain *stat; + uint index; + + DBUG_ASSERT(thread != NULL); + DBUG_ASSERT(klass != NULL); + index= klass->m_index; + DBUG_ASSERT(index < file_class_max); + + stat= &(thread->m_instr_class_wait_stats + [per_thread_file_class_start + index]); + return stat; +} + +/** Reset the wait statistics per thread. */ +void reset_per_thread_wait_stat(void) +{ + PFS_single_stat_chain *stat= thread_instr_class_waits_array; + PFS_single_stat_chain *stat_last= stat + thread_instr_class_waits_sizing; + + for ( ; stat < stat_last; stat++) + reset_single_stat_link(stat); +} + +/** Cleanup all the instruments buffers. */ +void cleanup_instruments(void) +{ + pfs_free(mutex_array); + mutex_array= NULL; + mutex_max= 0; + pfs_free(rwlock_array); + rwlock_array= NULL; + rwlock_max= 0; + pfs_free(cond_array); + cond_array= NULL; + cond_max= 0; + pfs_free(file_array); + file_array= NULL; + file_max= 0; + pfs_free(file_handle_array); + file_handle_array= NULL; + file_handle_max= 0; + pfs_free(table_array); + table_array= NULL; + table_max= 0; + pfs_free(thread_array); + thread_array= NULL; + thread_max= 0; + pfs_free(thread_history_array); + thread_history_array= NULL; + pfs_free(thread_instr_class_waits_array); + thread_instr_class_waits_array= NULL; +} + +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)); +} + +/** + Initialize the file name hash. + @return 0 on success +*/ +int init_file_hash(void) +{ + if (! filename_hash_inited) + { + lf_hash_init(&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(&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) +{ + int pass; + uint i= randomized_index(identity, mutex_max); + + /* + Pass 1: [random, mutex_max - 1] + Pass 2: [0, mutex_max - 1] + */ + for (pass= 1; pass <= 2; i=0, pass++) + { + PFS_mutex *pfs= mutex_array + i; + PFS_mutex *pfs_last= mutex_array + mutex_max; + for ( ; pfs < pfs_last; pfs++) + { + if (pfs->m_lock.is_free()) + { + if (pfs->m_lock.free_to_dirty()) + { + pfs->m_identity= identity; + pfs->m_class= klass; + pfs->m_wait_stat.m_control_flag= + &flag_events_waits_summary_by_instance; + pfs->m_wait_stat.m_parent= &klass->m_wait_stat; + reset_single_stat_link(&pfs->m_wait_stat); + pfs->m_lock_stat.m_control_flag= + &flag_events_locks_summary_by_instance; + pfs->m_lock_stat.m_parent= &klass->m_lock_stat; + reset_single_stat_link(&pfs->m_lock_stat); + pfs->m_owner= NULL; + pfs->m_last_locked= 0; + pfs->m_lock.dirty_to_allocated(); + return pfs; + } + } + } + } + + mutex_lost++; + return NULL; +} + +/** + Destroy instrumentation for a mutex instance. + @param pfs the mutex to destroy +*/ +void destroy_mutex(PFS_mutex *pfs) +{ + DBUG_ASSERT(pfs != NULL); + pfs->m_lock.allocated_to_free(); +} + +/** + 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) +{ + int pass; + uint i= randomized_index(identity, rwlock_max); + + /* + Pass 1: [random, rwlock_max - 1] + Pass 2: [0, rwlock_max - 1] + */ + for (pass= 1; pass <= 2; i=0, pass++) + { + PFS_rwlock *pfs= rwlock_array + i; + PFS_rwlock *pfs_last= rwlock_array + rwlock_max; + for ( ; pfs < pfs_last; pfs++) + { + if (pfs->m_lock.is_free()) + { + if (pfs->m_lock.free_to_dirty()) + { + pfs->m_identity= identity; + pfs->m_class= klass; + pfs->m_wait_stat.m_control_flag= + &flag_events_waits_summary_by_instance; + pfs->m_wait_stat.m_parent= &klass->m_wait_stat; + reset_single_stat_link(&pfs->m_wait_stat); + pfs->m_lock.dirty_to_allocated(); + pfs->m_read_lock_stat.m_control_flag= + &flag_events_locks_summary_by_instance; + pfs->m_read_lock_stat.m_parent= &klass->m_read_lock_stat; + reset_single_stat_link(&pfs->m_read_lock_stat); + pfs->m_write_lock_stat.m_control_flag= + &flag_events_locks_summary_by_instance; + pfs->m_write_lock_stat.m_parent= &klass->m_write_lock_stat; + reset_single_stat_link(&pfs->m_write_lock_stat); + pfs->m_writer= NULL; + pfs->m_readers= 0; + pfs->m_last_written= 0; + pfs->m_last_read= 0; + return pfs; + } + } + } + } + + rwlock_lost++; + return NULL; +} + +/** + Destroy instrumentation for a rwlock instance. + @param pfs the rwlock to destroy +*/ +void destroy_rwlock(PFS_rwlock *pfs) +{ + DBUG_ASSERT(pfs != NULL); + pfs->m_lock.allocated_to_free(); +} + +/** + 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) +{ + int pass; + uint i= randomized_index(identity, cond_max); + + /* + Pass 1: [random, cond_max - 1] + Pass 2: [0, cond_max - 1] + */ + for (pass= 1; pass <= 2; i=0, pass++) + { + PFS_cond *pfs= cond_array + i; + PFS_cond *pfs_last= cond_array + cond_max; + for ( ; pfs < pfs_last; pfs++) + { + if (pfs->m_lock.is_free()) + { + if (pfs->m_lock.free_to_dirty()) + { + pfs->m_identity= identity; + pfs->m_class= klass; + pfs->m_cond_stat.m_signal_count= 0; + pfs->m_cond_stat.m_broadcast_count= 0; + pfs->m_wait_stat.m_control_flag= + &flag_events_waits_summary_by_instance; + pfs->m_wait_stat.m_parent= &klass->m_wait_stat; + reset_single_stat_link(&pfs->m_wait_stat); + pfs->m_lock.dirty_to_allocated(); + return pfs; + } + } + } + } + + cond_lost++; + return NULL; +} + +/** + Destroy instrumentation for a condition instance. + @param pfs the condition to destroy +*/ +void destroy_cond(PFS_cond *pfs) +{ + DBUG_ASSERT(pfs != NULL); + pfs->m_lock.allocated_to_free(); +} + +/** + Create instrumentation for a thread instance. + @param klass the thread class + @param identity the thread address, + or a value characteristic of this thread + @param thread_id the PROCESSLIST thread id, + or 0 if unknown + @return a thread instance, or NULL +*/ +PFS_thread* create_thread(PFS_thread_class *klass, const void *identity, + ulong thread_id) +{ + int pass; + uint i= randomized_index(identity, thread_max); + + /* + Pass 1: [random, thread_max - 1] + Pass 2: [0, thread_max - 1] + */ + for (pass= 1; pass <= 2; i=0, pass++) + { + PFS_thread *pfs= thread_array + i; + PFS_thread *pfs_last= thread_array + thread_max; + for ( ; pfs < pfs_last; pfs++) + { + if (pfs->m_lock.is_free()) + { + if (pfs->m_lock.free_to_dirty()) + { + pfs->m_thread_internal_id= + PFS_atomic::add_u32(&thread_internal_id_counter, 1); + pfs->m_thread_id= thread_id; + pfs->m_event_id= 1; + pfs->m_enabled= true; + pfs->m_class= klass; + pfs->m_wait_locker_count= 0; + pfs->m_waits_history_full= false; + pfs->m_waits_history_index= 0; + + PFS_single_stat_chain *stat= pfs->m_instr_class_wait_stats; + PFS_single_stat_chain *stat_last= stat + instr_class_per_thread; + for ( ; stat < stat_last; stat++) + reset_single_stat_link(stat); + pfs->m_filename_hash_pins= NULL; + pfs->m_table_share_hash_pins= NULL; + pfs->m_lock.dirty_to_allocated(); + return pfs; + } + } + } + } + + thread_lost++; + return NULL; +} + +/** + 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) +{ + if ((&thread_array[0] <= unsafe) && + (unsafe < &thread_array[thread_max])) + return unsafe; + return NULL; +} + +/** + Destroy instrumentation for a thread instance. + @param pfs the thread to destroy +*/ +void destroy_thread(PFS_thread *pfs) +{ + DBUG_ASSERT(pfs != 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; + } + pfs->m_lock.allocated_to_free(); +} + +/** + 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 + @return a file instance, or NULL +*/ +PFS_file* +find_or_create_file(PFS_thread *thread, PFS_file_class *klass, + const char *filename, uint len) +{ + PFS_file *pfs; + int pass; + + if (! filename_hash_inited) + { + /* File instrumentation can be turned off. */ + file_lost++; + return NULL; + } + + if (unlikely(thread->m_filename_hash_pins == NULL)) + { + thread->m_filename_hash_pins= lf_hash_get_pins(&filename_hash); + if (unlikely(thread->m_filename_hash_pins == NULL)) + { + file_lost++; + return NULL; + } + } + + if (len >= sizeof(pfs->m_filename)) + len= sizeof(pfs->m_filename) - 1; + + PFS_file **entry; + uint retry_count= 0; + const uint retry_max= 3; +search: + entry= reinterpret_cast<PFS_file**> + (lf_hash_search(&filename_hash, thread->m_filename_hash_pins, + filename, len)); + if (entry && (entry != MY_ERRPTR)) + { + pfs= *entry; + pfs->m_file_stat.m_open_count++; + lf_hash_search_unpin(thread->m_filename_hash_pins); + return pfs; + } + + /* filename is not constant, just using it for noise on create */ + uint i= randomized_index(filename, file_max); + + /* + Pass 1: [random, file_max - 1] + Pass 2: [0, file_max - 1] + */ + for (pass= 1; pass <= 2; i=0, pass++) + { + pfs= file_array + i; + PFS_file *pfs_last= file_array + file_max; + + for ( ; pfs < pfs_last; pfs++) + { + if (pfs->m_lock.is_free()) + { + if (pfs->m_lock.free_to_dirty()) + { + pfs->m_class= klass; + strncpy(pfs->m_filename, filename, len); + pfs->m_filename[len]= '\0'; + pfs->m_filename_length= len; + pfs->m_file_stat.m_open_count= 1; + pfs->m_wait_stat.m_control_flag= + &flag_events_waits_summary_by_instance; + pfs->m_wait_stat.m_parent= &klass->m_wait_stat; + reset_single_stat_link(&pfs->m_wait_stat); + + int res; + res= lf_hash_insert(&filename_hash, thread->m_filename_hash_pins, + &pfs); + if (likely(res == 0)) + { + pfs->m_lock.dirty_to_allocated(); + return pfs; + } + + pfs->m_lock.dirty_to_free(); + + if (res > 0) + { + /* Duplicate insert by another thread */ + if (++retry_count > retry_max) + { + /* Avoid infinite loops */ + file_lost++; + return NULL; + } + goto search; + } + + /* OOM in lf_hash_insert */ + file_lost++; + return NULL; + } + } + } + } + + file_lost++; + return NULL; +} + +/** + 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(thread->m_filename_hash_pins != NULL); + DBUG_ASSERT(pfs != NULL); + lf_hash_delete(&filename_hash, thread->m_filename_hash_pins, + pfs->m_filename, pfs->m_filename_length); + pfs->m_lock.allocated_to_free(); +} + +/** + Create instrumentation for a table instance. + @param share the table share + @param identity the table address + @return a table instance, or NULL +*/ +PFS_table* create_table(PFS_table_share *share, const void *identity) +{ + int pass; + uint i= randomized_index(identity, table_max); + + /* + Pass 1: [random, table_max - 1] + Pass 2: [0, table_max - 1] + */ + for (pass= 1; pass <= 2; i=0, pass++) + { + PFS_table *pfs= table_array + i; + PFS_table *pfs_last= table_array + table_max; + for ( ; pfs < pfs_last; pfs++) + { + if (pfs->m_lock.is_free()) + { + if (pfs->m_lock.free_to_dirty()) + { + pfs->m_identity= identity; + pfs->m_share= share; + pfs->m_wait_stat.m_control_flag= + &flag_events_waits_summary_by_instance; + pfs->m_wait_stat.m_parent= &share->m_wait_stat; + reset_single_stat_link(&pfs->m_wait_stat); + pfs->m_lock.dirty_to_allocated(); + return pfs; + } + } + } + } + + table_lost++; + return NULL; +} + +/** + Destroy instrumentation for a table instance. + @param pfs the table to destroy +*/ +void destroy_table(PFS_table *pfs) +{ + DBUG_ASSERT(pfs != NULL); + pfs->m_lock.allocated_to_free(); +} + +static void reset_mutex_waits_by_instance(void) +{ + PFS_mutex *pfs= mutex_array; + PFS_mutex *pfs_last= mutex_array + mutex_max; + + for ( ; pfs < pfs_last; pfs++) + reset_single_stat_link(&pfs->m_wait_stat); +} + +static void reset_rwlock_waits_by_instance(void) +{ + PFS_rwlock *pfs= rwlock_array; + PFS_rwlock *pfs_last= rwlock_array + rwlock_max; + + for ( ; pfs < pfs_last; pfs++) + reset_single_stat_link(&pfs->m_wait_stat); +} + +static void reset_cond_waits_by_instance(void) +{ + PFS_cond *pfs= cond_array; + PFS_cond *pfs_last= cond_array + cond_max; + + for ( ; pfs < pfs_last; pfs++) + reset_single_stat_link(&pfs->m_wait_stat); +} + +static void reset_file_waits_by_instance(void) +{ + PFS_file *pfs= file_array; + PFS_file *pfs_last= file_array + file_max; + + for ( ; pfs < pfs_last; pfs++) + reset_single_stat_link(&pfs->m_wait_stat); +} + +/** 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 the io statistics per file instance. */ +void reset_file_instance_io(void) +{ + PFS_file *pfs= file_array; + PFS_file *pfs_last= file_array + file_max; + + for ( ; pfs < pfs_last; pfs++) + reset_file_stat(&pfs->m_file_stat); +} + +/** @} */ diff --git a/storage/perfschema/pfs_instr.h b/storage/perfschema/pfs_instr.h new file mode 100644 index 00000000000..a150a13fb75 --- /dev/null +++ b/storage/perfschema/pfs_instr.h @@ -0,0 +1,266 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef PFS_INSTR_H +#define PFS_INSTR_H + +/** + @file storage/perfschema/pfs_instr.h + Performance schema instruments (declarations). +*/ + +#include <mysql_priv.h> +#include "pfs_lock.h" +#include "pfs_instr_class.h" +#include "pfs_events_waits.h" +#include "pfs_server.h" +#include "lf.h" + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +struct PFS_thread; + +struct PFS_instr +{ + /** Internal lock. */ + pfs_lock m_lock; + /** Instrument wait statistics chain. */ + PFS_single_stat_chain m_wait_stat; +}; + +/** Instrumented mutex implementation. @see PSI_mutex. */ +struct PFS_mutex : public PFS_instr +{ + /** Mutex identity, typically a pthread_mutex_t. */ + const void *m_identity; + /** Mutex class. */ + PFS_mutex_class *m_class; + /** + Mutex lock usage statistics chain. + This statistic is not exposed in user visible tables yet. + */ + PFS_single_stat_chain m_lock_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_rwlock : public PFS_instr +{ + /** RWLock identity, typically a pthread_rwlock_t. */ + const void *m_identity; + /** RWLock class. */ + PFS_rwlock_class *m_class; + /** + RWLock read lock usage statistics chain. + This statistic is not exposed in user visible tables yet. + */ + PFS_single_stat_chain m_read_lock_stat; + /** + RWLock write lock usage statistics chain. + This statistic is not exposed in user visible tables yet. + */ + PFS_single_stat_chain m_write_lock_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_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_file : public PFS_instr +{ + /** File name. */ + char m_filename[FN_REFLEN]; + /** File name length in bytes. */ + uint m_filename_length; + /** File class. */ + PFS_file_class *m_class; + /** File usage statistics. */ + PFS_file_stat m_file_stat; +}; + +/** Instrumented table implementation. @see PSI_table. */ +struct PFS_table : public PFS_instr +{ + /** Table share. */ + PFS_table_share *m_share; + /** Table identity, typically a handler. */ + const void *m_identity; +}; + +/** + @def LOCKER_STACK_SIZE + Maximum number of nested waits. +*/ +#define LOCKER_STACK_SIZE 3 + +/** Instrumented thread implementation. @see PSI_thread. */ +struct PFS_thread +{ + /** Internal lock. */ + 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; + /** Event ID counter */ + ulonglong m_event_id; + /** Thread instrumentation flag. */ + bool m_enabled; + /** Internal thread identifier, unique. */ + ulong m_thread_internal_id; + /** External (SHOW PROCESSLIST) thread identifier, not unique. */ + ulong m_thread_id; + /** Thread class. */ + PFS_thread_class *m_class; + /** Size of @c m_wait_locker_stack. */ + uint m_wait_locker_count; + /** + Stack of wait lockers. + This member holds the data for the table + PERFORMANCE_SCHEMA.EVENTS_WAITS_CURRENT. + For most locks, only 1 wait locker is used at a given time. + For composite locks, several records are needed: + - 1 for a 'logical' wait (for example on the GLOBAL READ LOCK state) + - 1 for a 'physical' wait (for example on COND_refresh) + */ + PFS_wait_locker m_wait_locker_stack[LOCKER_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; + /** + Per thread waits aggregated statistics. + This member holds the data for the table + PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME. + */ + PFS_single_stat_chain *m_instr_class_wait_stats; +}; + +PFS_thread *sanitize_thread(PFS_thread *unsafe); + +PFS_single_stat_chain* +find_per_thread_mutex_class_wait_stat(PFS_thread *thread, + PFS_mutex_class *klass); + +PFS_single_stat_chain* +find_per_thread_rwlock_class_wait_stat(PFS_thread *thread, + PFS_rwlock_class *klass); + +PFS_single_stat_chain* +find_per_thread_cond_class_wait_stat(PFS_thread *thread, + PFS_cond_class *klass); + +PFS_single_stat_chain* +find_per_thread_file_class_wait_stat(PFS_thread *thread, + PFS_file_class *klass); + +int init_instruments(const PFS_global_param *param); +void cleanup_instruments(); +int init_file_hash(); +void cleanup_file_hash(); +PFS_mutex* create_mutex(PFS_mutex_class *mutex_class, const void *identity); +void destroy_mutex(PFS_mutex *pfs); +PFS_rwlock* create_rwlock(PFS_rwlock_class *klass, const void *identity); +void destroy_rwlock(PFS_rwlock *pfs); +PFS_cond* create_cond(PFS_cond_class *klass, const void *identity); +void destroy_cond(PFS_cond *pfs); + +PFS_thread* create_thread(PFS_thread_class *klass, const void *identity, + ulong thread_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); + +void release_file(PFS_file *pfs); +void destroy_file(PFS_thread *thread, PFS_file *pfs); +PFS_table* create_table(PFS_table_share *share, const void *identity); +void destroy_table(PFS_table *pfs); + +/* For iterators and show status. */ + +extern ulong mutex_max; +extern ulong mutex_lost; +extern ulong rwlock_max; +extern ulong rwlock_lost; +extern ulong cond_max; +extern ulong cond_lost; +extern ulong thread_max; +extern ulong thread_lost; +extern ulong file_max; +extern ulong file_lost; +extern long file_handle_max; +extern ulong file_handle_lost; +extern ulong table_max; +extern ulong table_lost; +extern ulong events_waits_history_per_thread; +extern ulong instr_class_per_thread; +extern ulong locker_lost; + +/* Exposing the data directly, for iterators. */ + +extern PFS_mutex *mutex_array; +extern PFS_rwlock *rwlock_array; +extern PFS_cond *cond_array; +extern PFS_thread *thread_array; +extern PFS_file *file_array; +extern PFS_file **file_handle_array; +extern PFS_table *table_array; + +void reset_events_waits_by_instance(); +void reset_per_thread_wait_stat(); +void reset_file_instance_io(); + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_instr_class.cc b/storage/perfschema/pfs_instr_class.cc new file mode 100644 index 00000000000..ac8aa64b0c5 --- /dev/null +++ b/storage/perfschema/pfs_instr_class.cc @@ -0,0 +1,878 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/pfs_instr_class.cc + Performance schema instruments meta data (implementation). +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_global.h" +#include "pfs_events_waits.h" +#include "pfs_atomic.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; + +/** + Current number of elements in mutex_class_array. + This global variable is written to during: + - the performance schema initialization + - a plugin initialization +*/ +static volatile uint32 mutex_class_dirty_count= 0; +static volatile uint32 mutex_class_allocated_count= 0; +static volatile uint32 rwlock_class_dirty_count= 0; +static volatile uint32 rwlock_class_allocated_count= 0; +static volatile uint32 cond_class_dirty_count= 0; +static volatile 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 table share array. @sa table_share_array */ +ulong table_share_max= 0; +/** Number of table share lost. @sa table_share_array */ +ulong table_share_lost= 0; + +static PFS_mutex_class *mutex_class_array= NULL; +static PFS_rwlock_class *rwlock_class_array= NULL; +static 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 volatile uint32 thread_class_dirty_count= 0; +static volatile uint32 thread_class_allocated_count= 0; + +static PFS_thread_class *thread_class_array= NULL; + +/** + Table instance array. + @sa table_share_max + @sa table_share_lost + @sa table_share_hash +*/ +PFS_table_share *table_share_array= NULL; + +PFS_instr_class global_table_class= +{ + "wait/table", /* name */ + 10, /* name length */ + 0, /* flags */ + true, /* enabled */ + true, /* timed */ + { &flag_events_waits_current, NULL, 0, 0, 0, 0} /* wait stat chain */ +}; + +/** Hash table for instrumented tables. */ +static LF_HASH table_share_hash; +/** True if table_share_hash is initialized. */ +static bool table_share_hash_inited= false; + +static volatile uint32 file_class_dirty_count= 0; +static volatile uint32 file_class_allocated_count= 0; + +static PFS_file_class *file_class_array= NULL; + +/** + 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(mutex_class_max, 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(rwlock_class_max, 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(cond_class_max, 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(mutex_class_array); + mutex_class_array= NULL; + mutex_class_dirty_count= mutex_class_allocated_count= mutex_class_max= 0; + pfs_free(rwlock_class_array); + rwlock_class_array= NULL; + rwlock_class_dirty_count= rwlock_class_allocated_count= rwlock_class_max= 0; + pfs_free(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(thread_class_max, 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(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) +{ + int result= 0; + table_share_max= table_share_sizing; + table_share_lost= 0; + + if (table_share_max > 0) + { + table_share_array= PFS_MALLOC_ARRAY(table_share_max, PFS_table_share, + MYF(MY_ZEROFILL)); + if (unlikely(table_share_array == NULL)) + result= 1; + } + else + table_share_array= NULL; + + return result; +} + +/** Cleanup the table share buffers. */ +void cleanup_table_share(void) +{ + pfs_free(table_share_array); + table_share_array= NULL; + table_share_max= 0; +} + +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)); +} + +/** Initialize the table share hash table. */ +int init_table_share_hash(void) +{ + if ((! table_share_hash_inited) && (table_share_max > 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; + } +} + +/** + 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(file_class_max, 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(file_class_array); + file_class_array= NULL; + file_class_dirty_count= file_class_allocated_count= 0; + file_class_max= 0; +} + +static void init_instr_class(PFS_instr_class *klass, + const char *name, + uint name_length, + int flags) +{ + 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_length= name_length; + klass->m_flags= flags; + klass->m_enabled= true; + klass->m_timed= true; +} + +#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); + entry->m_wait_stat.m_control_flag= + &flag_events_waits_summary_by_event_name; + entry->m_wait_stat.m_parent= NULL; + reset_single_stat_link(&entry->m_wait_stat); + entry->m_lock_stat.m_control_flag= + &flag_events_locks_summary_by_event_name; + entry->m_lock_stat.m_parent= NULL; + reset_single_stat_link(&entry->m_lock_stat); + entry->m_index= index; + /* + 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. + */ + 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); + entry->m_wait_stat.m_control_flag= + &flag_events_waits_summary_by_event_name; + entry->m_wait_stat.m_parent= NULL; + reset_single_stat_link(&entry->m_wait_stat); + entry->m_read_lock_stat.m_control_flag= + &flag_events_locks_summary_by_event_name; + entry->m_read_lock_stat.m_parent= NULL; + reset_single_stat_link(&entry->m_read_lock_stat); + entry->m_write_lock_stat.m_control_flag= + &flag_events_locks_summary_by_event_name; + entry->m_write_lock_stat.m_parent= NULL; + reset_single_stat_link(&entry->m_write_lock_stat); + entry->m_index= index; + PFS_atomic::add_u32(&rwlock_class_allocated_count, 1); + return (index + 1); + } + + 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); + entry->m_wait_stat.m_control_flag= + &flag_events_waits_summary_by_event_name; + entry->m_wait_stat.m_parent= NULL; + reset_single_stat_link(&entry->m_wait_stat); + entry->m_index= index; + PFS_atomic::add_u32(&cond_class_allocated_count, 1); + return (index + 1); + } + + 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); +} + +#define SANITIZE_ARRAY_BODY(ARRAY, MAX, UNSAFE) \ + if ((&ARRAY[0] <= UNSAFE) && \ + (UNSAFE < &ARRAY[MAX])) \ + return UNSAFE; \ + return NULL + +PFS_mutex_class *sanitize_mutex_class(PFS_mutex_class *unsafe) +{ + SANITIZE_ARRAY_BODY(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(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(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); + } + + 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(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); + entry->m_wait_stat.m_control_flag= + &flag_events_waits_summary_by_event_name; + entry->m_wait_stat.m_parent= NULL; + reset_single_stat_link(&entry->m_wait_stat); + entry->m_index= index; + PFS_atomic::add_u32(&file_class_allocated_count, 1); + return (index + 1); + } + + file_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(file_class_array, file_class_max, unsafe); +} + +/** + Find or create a table instance by name. + @param thread the executing instrumented thread + @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 + @return a table instance, or NULL +*/ +PFS_table_share* find_or_create_table_share(PFS_thread *thread, + const char *schema_name, + uint schema_name_length, + const char *table_name, + uint table_name_length) +{ + /* See comments in register_mutex_class */ + int pass; + PFS_table_share_key key; + + if (! table_share_hash_inited) + { + /* Table instrumentation can be turned off. */ + table_share_lost++; + return NULL; + } + + if (unlikely(thread->m_table_share_hash_pins == NULL)) + { + thread->m_table_share_hash_pins= lf_hash_get_pins(&table_share_hash); + if (unlikely(thread->m_table_share_hash_pins == NULL)) + { + table_share_lost++; + return NULL; + } + } + + DBUG_ASSERT(schema_name_length <= NAME_LEN); + DBUG_ASSERT(table_name_length <= NAME_LEN); + + char *ptr= &key.m_hash_key[0]; + memcpy(ptr, schema_name, schema_name_length); + ptr+= schema_name_length; + ptr[0]= 0; ptr++; + memcpy(ptr, table_name, table_name_length); + ptr+= table_name_length; + ptr[0]= 0; ptr++; + key.m_key_length= ptr - &key.m_hash_key[0]; + + PFS_table_share **entry; + uint retry_count= 0; + const uint retry_max= 3; +search: + entry= reinterpret_cast<PFS_table_share**> + (lf_hash_search(&table_share_hash, thread->m_table_share_hash_pins, + &key.m_hash_key[0], key.m_key_length)); + if (entry && (entry != MY_ERRPTR)) + { + PFS_table_share *pfs; + pfs= *entry; + lf_hash_search_unpin(thread->m_table_share_hash_pins); + return pfs; + } + + /* table_name is not constant, just using it for noise on create */ + uint i= randomized_index(table_name, table_share_max); + + /* + Pass 1: [random, table_share_max - 1] + Pass 2: [0, table_share_max - 1] + */ + for (pass= 1; pass <= 2; i=0, pass++) + { + PFS_table_share *pfs= table_share_array + i; + PFS_table_share *pfs_last= table_share_array + table_share_max; + for ( ; pfs < pfs_last; pfs++) + { + if (pfs->m_lock.is_free()) + { + if (pfs->m_lock.free_to_dirty()) + { + pfs->m_key= key; + pfs->m_schema_name= &pfs->m_key.m_hash_key[0]; + pfs->m_schema_name_length= schema_name_length; + pfs->m_table_name= &pfs->m_key.m_hash_key[schema_name_length + 1]; + pfs->m_table_name_length= table_name_length; + pfs->m_wait_stat.m_control_flag= + &flag_events_waits_summary_by_instance; + pfs->m_wait_stat.m_parent= NULL; + reset_single_stat_link(&pfs->m_wait_stat); + pfs->m_enabled= true; + pfs->m_timed= true; + pfs->m_aggregated= false; + + int res; + res= lf_hash_insert(&table_share_hash, + thread->m_table_share_hash_pins, &pfs); + if (likely(res == 0)) + { + pfs->m_lock.dirty_to_allocated(); + return pfs; + } + + pfs->m_lock.dirty_to_free(); + + if (res > 0) + { + /* Duplicate insert by another thread */ + if (++retry_count > retry_max) + { + /* Avoid infinite loops */ + table_share_lost++; + return NULL; + } + goto search; + } + + /* OOM in lf_hash_insert */ + table_share_lost++; + return NULL; + } + } + } + } + + table_share_lost++; + return NULL; +} + +PFS_table_share *sanitize_table_share(PFS_table_share *unsafe) +{ + SANITIZE_ARRAY_BODY(table_share_array, table_share_max, unsafe); +} + +static void reset_mutex_class_waits(void) +{ + PFS_mutex_class *pfs= mutex_class_array; + PFS_mutex_class *pfs_last= mutex_class_array + mutex_class_max; + + for ( ; pfs < pfs_last; pfs++) + reset_single_stat_link(&pfs->m_wait_stat); +} + +static void reset_rwlock_class_waits(void) +{ + PFS_rwlock_class *pfs= rwlock_class_array; + PFS_rwlock_class *pfs_last= rwlock_class_array + rwlock_class_max; + + for ( ; pfs < pfs_last; pfs++) + reset_single_stat_link(&pfs->m_wait_stat); +} + +static void reset_cond_class_waits(void) +{ + PFS_cond_class *pfs= cond_class_array; + PFS_cond_class *pfs_last= cond_class_array + cond_class_max; + + for ( ; pfs < pfs_last; pfs++) + reset_single_stat_link(&pfs->m_wait_stat); +} + +static void reset_file_class_waits(void) +{ + PFS_file_class *pfs= file_class_array; + PFS_file_class *pfs_last= file_class_array + file_class_max; + + for ( ; pfs < pfs_last; pfs++) + reset_single_stat_link(&pfs->m_wait_stat); +} + +/** Reset the wait statistics for every instrument class. */ +void reset_instrument_class_waits(void) +{ + reset_mutex_class_waits(); + reset_rwlock_class_waits(); + reset_cond_class_waits(); + reset_file_class_waits(); +} + +/** 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++) + reset_file_stat(&pfs->m_file_stat); +} + +/** @} */ + diff --git a/storage/perfschema/pfs_instr_class.h b/storage/perfschema/pfs_instr_class.h new file mode 100644 index 00000000000..0a58095d612 --- /dev/null +++ b/storage/perfschema/pfs_instr_class.h @@ -0,0 +1,251 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef PFS_INSTR_CLASS_H +#define PFS_INSTR_CLASS_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 <mysql_priv.h> +#include <mysql/psi/psi.h> +#include "pfs_lock.h" +#include "pfs_stat.h" + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +extern my_bool pfs_enabled; + +/** 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; + +struct PFS_thread; + +/** Information for all instrumentation. */ +struct PFS_instr_class +{ + /** Instrument name. */ + char m_name[PFS_MAX_INFO_NAME_LENGTH]; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** Instrument flags. */ + int m_flags; + /** True if this instrument is enabled. */ + bool m_enabled; + /** True if this instrument is timed. */ + bool m_timed; + /** Wait statistics chain. */ + PFS_single_stat_chain m_wait_stat; +}; + +/** Instrumentation metadata for a MUTEX. */ +struct PFS_mutex_class : public PFS_instr_class +{ + /** + Lock statistics chain. + This statistic is not exposed in user visible tables yet. + */ + PFS_single_stat_chain m_lock_stat; + /** Self index in @c mutex_class_array. */ + uint m_index; +}; + +/** Instrumentation metadata for a RWLOCK. */ +struct PFS_rwlock_class : public PFS_instr_class +{ + /** + Read lock statistics chain. + This statistic is not exposed in user visible tables yet. + */ + PFS_single_stat_chain m_read_lock_stat; + /** + Write lock statistics chain. + This statistic is not exposed in user visible tables yet. + */ + PFS_single_stat_chain m_write_lock_stat; + /** Self index in @c rwlock_class_array. */ + uint m_index; +}; + +/** Instrumentation metadata for a COND. */ +struct 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; + /** Self index in @c cond_class_array. */ + uint m_index; +}; + +/** Instrumentation metadata of a thread. */ +struct PFS_thread_class +{ + /** Thread instrument name. */ + char m_name[PFS_MAX_INFO_NAME_LENGTH]; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** True if this thread instrument is enabled. */ + bool m_enabled; +}; + +/** 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 "<schema_name><0x00><object_name><0x00>" + @see create_table_def_key + */ + char m_hash_key[NAME_LEN + 1 + NAME_LEN + 1]; + /** Length in bytes of @c m_hash_key. */ + uint m_key_length; +}; + +/** Instrumentation metadata for a table share. */ +struct PFS_table_share +{ + /** Internal lock. */ + pfs_lock m_lock; + /** 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; + /** Wait statistics chain. */ + PFS_single_stat_chain m_wait_stat; + /** True if this table instrument is enabled. */ + bool m_enabled; + /** True if this table instrument is timed. */ + bool m_timed; + /** True if this table instrument is aggregated. */ + bool m_aggregated; +}; + +/** + Instrument controlling all tables. + This instrument is used as a default when there is no + entry present in SETUP_OBJECTS. +*/ +extern PFS_instr_class global_table_class; + +/** Instrumentation metadata for a file. */ +struct PFS_file_class : public PFS_instr_class +{ + /** File usage statistics. */ + PFS_file_stat m_file_stat; + /** Self index in @c file_class_array. */ + uint m_index; +}; + +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_hash(); +void cleanup_table_share_hash(); +int init_file_class(uint file_class_sizing); +void cleanup_file_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_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_table_share *find_or_create_table_share(PFS_thread *thread, + 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 table_share_max; +extern ulong table_share_lost; +extern PFS_table_share *table_share_array; + +void reset_instrument_class_waits(); +void reset_file_class_io(); + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_lock.h b/storage/perfschema/pfs_lock.h new file mode 100644 index 00000000000..46d7d33617b --- /dev/null +++ b/storage/perfschema/pfs_lock.h @@ -0,0 +1,173 @@ +/* Copyright (C) 2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef PFS_LOCK_H +#define PFS_LOCK_H + +/** + @file storage/perfschema/pfs_lock.h + Performance schema internal locks (declarations). +*/ + +#include "pfs_atomic.h" + +/** + @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 0 +/** + 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 1 +/** + 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 2 + +/** + 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 state. + @sa PFS_LOCK_FREE + @sa PFS_LOCK_DIRTY + @sa PFS_LOCK_ALLOCATED + */ + volatile int32 m_state; + /** + The record internal version number. + This 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. + */ + volatile uint32 m_version; + + /** Returns true if the record is free. */ + bool is_free(void) + { + /* This is a dirty read */ + return (m_state == PFS_LOCK_FREE); + } + + /** Returns true if the record contains values that can be read. */ + bool is_populated(void) + { + int32 copy= m_state; /* non volatile copy, and dirty read */ + return (copy == 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(void) + { + int32 old_state= PFS_LOCK_FREE; + int32 new_state= PFS_LOCK_DIRTY; + + return (PFS_atomic::cas_32(&m_state, &old_state, new_state)); + } + + /** + 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(void) + { + DBUG_ASSERT(m_state == PFS_LOCK_DIRTY); + PFS_atomic::add_u32(&m_version, 1); + PFS_atomic::store_32(&m_state, PFS_LOCK_ALLOCATED); + } + + /** + Execute a dirty to free transition. + This transition should be executed by the writer that owns the record. + */ + void dirty_to_free(void) + { + DBUG_ASSERT(m_state == PFS_LOCK_DIRTY); + PFS_atomic::store_32(&m_state, PFS_LOCK_FREE); + } + + /** + Execute an allocated to free transition. + This transition should be executed by the writer that owns the record. + */ + void allocated_to_free(void) + { + DBUG_ASSERT(m_state == PFS_LOCK_ALLOCATED); + PFS_atomic::store_32(&m_state, PFS_LOCK_FREE); + } + + /** + Start an optimistic read operation. + @sa end_optimist_lock. + */ + void begin_optimistic_lock(struct pfs_lock *copy) + { + copy->m_version= PFS_atomic::load_u32(&m_version); + copy->m_state= PFS_atomic::load_32(&m_state); + } + + /** + End an optimistic read operation. + @sa begin_optimist_lock. + @return true if the data read is safe to use. + */ + bool end_optimistic_lock(struct pfs_lock *copy) + { + /* + return true if: + - the version + state has not changed + - and there was valid data to look at + */ + return ((copy->m_version == PFS_atomic::load_u32(&m_version)) && + (copy->m_state == PFS_atomic::load_32(&m_state)) && + (copy->m_state == PFS_LOCK_ALLOCATED)); + } +}; + + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_server.cc b/storage/perfschema/pfs_server.cc new file mode 100644 index 00000000000..66b8a7fb22b --- /dev/null +++ b/storage/perfschema/pfs_server.cc @@ -0,0 +1,131 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "mysql_priv.h" +#include "pfs_server.h" +#include "pfs.h" +#include "pfs_global.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_events_waits.h" +#include "pfs_timer.h" + +PFS_global_param pfs_param; + +static void destroy_pfs_thread(void *key); +void cleanup_performance_schema(void); + +struct PSI_bootstrap* +initialize_performance_schema(const PFS_global_param *param) +{ + pfs_initialized= false; + + if (! param->m_enabled) + { + /* + The performance schema is disabled in the startup command line. + All the instrumentation is turned off. + */ + return NULL; + } + + init_timers(); + PFS_atomic::init(); + + if (pthread_key_create(&THR_PFS, destroy_pfs_thread)) + return NULL; + + THR_PFS_initialized= true; + + 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_file_class(param->m_file_class_sizing) || + init_instruments(param) || + init_events_waits_history_long( + param->m_events_waits_history_long_sizing) || + init_file_hash() || + init_table_share_hash()) + { + /* + The performance schema initialization failed. + Free the memory used, and disable the instrumentation. + */ + cleanup_performance_schema(); + return NULL; + } + + pfs_initialized= true; + return &PFS_bootstrap; +} + +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); +} + +void cleanup_performance_schema(void) +{ + cleanup_instruments(); + cleanup_sync_class(); + cleanup_thread_class(); + cleanup_table_share(); + cleanup_file_class(); + cleanup_events_waits_history_long(); + cleanup_table_share_hash(); + cleanup_file_hash(); + PFS_atomic::cleanup(); +} + +void shutdown_performance_schema(void) +{ + pfs_initialized= 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_pthread_setspecific_ptr(THR_PFS, NULL); + pthread_key_delete(THR_PFS); + THR_PFS_initialized= false; + } +} + diff --git a/storage/perfschema/pfs_server.h b/storage/perfschema/pfs_server.h new file mode 100644 index 00000000000..acf483e1f86 --- /dev/null +++ b/storage/perfschema/pfs_server.h @@ -0,0 +1,101 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef PFS_SERVER_H +#define PFS_SERVER_H + +/** + @file storage/perfschema/pfs_server.h + Private interface for the server (declarations). +*/ + +#ifndef PFS_MAX_MUTEX_CLASS + #define PFS_MAX_MUTEX_CLASS 200 +#endif +#ifndef PFS_MAX_MUTEX + #define PFS_MAX_MUTEX 1000 +#endif +#ifndef PFS_MAX_RWLOCK_CLASS + #define PFS_MAX_RWLOCK_CLASS 20 +#endif +#ifndef PFS_MAX_RWLOCK + #define PFS_MAX_RWLOCK 1000 +#endif +#ifndef PFS_MAX_COND_CLASS + #define PFS_MAX_COND_CLASS 80 +#endif +#ifndef PFS_MAX_COND + #define PFS_MAX_COND 1000 +#endif +#ifndef PFS_MAX_THREAD_CLASS + #define PFS_MAX_THREAD_CLASS 50 +#endif +#ifndef PFS_MAX_THREAD + #define PFS_MAX_THREAD 1000 +#endif +#ifndef PFS_MAX_FILE_CLASS + #define PFS_MAX_FILE_CLASS 50 +#endif +#ifndef PFS_MAX_FILE + #define PFS_MAX_FILE 10000 +#endif +#ifndef PFS_MAX_FILE_HANDLE + #define PFS_MAX_FILE_HANDLE 32768 +#endif +#ifndef PFS_MAX_TABLE_SHARE + #define PFS_MAX_TABLE_SHARE 50000 +#endif +#ifndef PFS_MAX_TABLE + #define PFS_MAX_TABLE 100000 +#endif +#ifndef PFS_WAITS_HISTORY_SIZE + #define PFS_WAITS_HISTORY_SIZE 10 +#endif +#ifndef PFS_WAITS_HISTORY_LONG_SIZE + #define PFS_WAITS_HISTORY_LONG_SIZE 10000 +#endif + +struct PFS_global_param +{ + bool m_enabled; + ulong m_mutex_class_sizing; + ulong m_rwlock_class_sizing; + ulong m_cond_class_sizing; + ulong m_thread_class_sizing; + ulong m_table_share_sizing; + ulong m_file_class_sizing; + ulong m_mutex_sizing; + ulong m_rwlock_sizing; + ulong m_cond_sizing; + ulong m_thread_sizing; + ulong m_table_sizing; + ulong m_file_sizing; + ulong m_file_handle_sizing; + ulong m_events_waits_history_sizing; + ulong m_events_waits_history_long_sizing; +}; + +extern PFS_global_param pfs_param; + +struct PSI_bootstrap* +initialize_performance_schema(const PFS_global_param *param); + +void initialize_performance_schema_acl(bool bootstrap); + +void check_performance_schema(); + +void shutdown_performance_schema(); + +#endif diff --git a/storage/perfschema/pfs_stat.h b/storage/perfschema/pfs_stat.h new file mode 100644 index 00000000000..654f292d82c --- /dev/null +++ b/storage/perfschema/pfs_stat.h @@ -0,0 +1,125 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef PFS_STAT_H +#define PFS_STAT_H + +/** + @file storage/perfschema/pfs_stat.h + Statistics (declarations). +*/ + +/** + @addtogroup Performance_schema_buffers + @{ +*/ + +/** Usage statistics chain, for a single value and its aggregates. */ +struct PFS_single_stat_chain +{ + /** + Control flag. + Statistics are aggregated only if the control flag is true. + */ + bool *m_control_flag; + /** Next link in the statistics chain. */ + struct PFS_single_stat_chain *m_parent; + /** Count of values. */ + ulonglong m_count; + /** Sum of values. */ + ulonglong m_sum; + /** Minimum value. */ + ulonglong m_min; + /** Maximum value. */ + ulonglong m_max; +}; + +/** + Reset a single statistic link. + Only the current link is reset, parents are not affected. + @param stat the statistics link to reset +*/ +inline void reset_single_stat_link(PFS_single_stat_chain *stat) +{ + stat->m_count= 0; + stat->m_sum= 0; + stat->m_min= ULONGLONG_MAX; + stat->m_max= 0; +} + +/** + Aggregate a value to a statistic chain. + @param stat the aggregated statistic chain + @param value the value to aggregate +*/ +inline void aggregate_single_stat_chain(PFS_single_stat_chain *stat, + ulonglong value) +{ + do + { + if (*stat->m_control_flag) + { + stat->m_count++; + stat->m_sum+= value; + if (stat->m_min > value) + stat->m_min= value; + if (stat->m_max < value) + stat->m_max= value; + } + stat= stat->m_parent; + } + while (stat); +} + +/** Statistics for COND usage. */ +struct PFS_cond_stat +{ + /** Number of times a condition was signalled. */ + ulonglong m_signal_count; + /** Number of times a condition was broadcasted. */ + ulonglong m_broadcast_count; +}; + +/** Statistics for FILE usage. */ +struct PFS_file_stat +{ + /** Number of current open handles. */ + ulong m_open_count; + /** Count of READ operations. */ + ulonglong m_count_read; + /** Count of WRITE operations. */ + ulonglong m_count_write; + /** Number of bytes read. */ + ulonglong m_read_bytes; + /** Number of bytes written. */ + ulonglong m_write_bytes; +}; + +/** + Reset file statistic. + @param stat the statistics to reset +*/ +inline void reset_file_stat(PFS_file_stat *stat) +{ + stat->m_open_count= 0; + stat->m_count_read= 0; + stat->m_count_write= 0; + stat->m_read_bytes= 0; + stat->m_write_bytes= 0; +} + +/** @} */ +#endif + diff --git a/storage/perfschema/pfs_timer.cc b/storage/perfschema/pfs_timer.cc new file mode 100644 index 00000000000..65883b62c32 --- /dev/null +++ b/storage/perfschema/pfs_timer.cc @@ -0,0 +1,119 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 wait_timer= TIMER_NAME_CYCLE; +MY_TIMER_INFO pfs_timer_info; + +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 */ + +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; + + my_timer_init(&pfs_timer_info); + + 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 (pfs_timer_info.cycles.frequency > 0) + cycle_to_pico= round_to_ulong(pico_frequency/ + (double)pfs_timer_info.cycles.frequency); + else + cycle_to_pico= 0; + + if (pfs_timer_info.nanoseconds.frequency > 0) + nanosec_to_pico= round_to_ulong(pico_frequency/ + (double)pfs_timer_info.nanoseconds.frequency); + else + nanosec_to_pico= 0; + + if (pfs_timer_info.microseconds.frequency > 0) + microsec_to_pico= round_to_ulong(pico_frequency/ + (double)pfs_timer_info.microseconds.frequency); + else + microsec_to_pico= 0; + + if (pfs_timer_info.milliseconds.frequency > 0) + millisec_to_pico= round_to_ulong(pico_frequency/ + (double)pfs_timer_info.milliseconds.frequency); + else + millisec_to_pico= 0; + + if (pfs_timer_info.ticks.frequency > 0) + tick_to_pico= round_to_ulonglong(pico_frequency/ + (double)pfs_timer_info.ticks.frequency); + else + tick_to_pico= 0; +} + +ulonglong get_timer_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; +} + diff --git a/storage/perfschema/pfs_timer.h b/storage/perfschema/pfs_timer.h new file mode 100644 index 00000000000..beba263f45a --- /dev/null +++ b/storage/perfschema/pfs_timer.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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" + +extern enum_timer_name wait_timer; +extern MY_TIMER_INFO pfs_timer_info; + +void init_timers(); + +ulonglong get_timer_value(enum_timer_name timer_name); + +#endif + diff --git a/storage/perfschema/plug.in b/storage/perfschema/plug.in new file mode 100644 index 00000000000..e6539dc1260 --- /dev/null +++ b/storage/perfschema/plug.in @@ -0,0 +1,26 @@ +dnl -*- ksh -*- + +# Copyright (C) 2008-2009 Sun Microsystems, Inc +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +dnl This file is part of the configure scripts used by autoconf. + +MYSQL_STORAGE_ENGINE(perfschema, + perfschema, + [Performance Schema], + [Performance Schema], + [default,max,max-no-ndb]) +MYSQL_PLUGIN_DIRECTORY(perfschema, [storage/perfschema]) +MYSQL_PLUGIN_STATIC(perfschema, [libperfschema.a]) diff --git a/storage/perfschema/table_all_instr.cc b/storage/perfschema/table_all_instr.cc new file mode 100644 index 00000000000..e6cd7897f6f --- /dev/null +++ b/storage/perfschema/table_all_instr.cc @@ -0,0 +1,264 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/table_all_instr.cc + Abstract tables for all instruments (implementation). +*/ + +#include "mysql_priv.h" +#include "table_all_instr.h" +#include "pfs_global.h" + +table_all_instr::table_all_instr(const PFS_engine_table_share *share) + : PFS_readonly_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; + + 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: + for ( ; m_pos.m_index_2 < mutex_max; m_pos.m_index_2++) + { + mutex= &mutex_array[m_pos.m_index_2]; + if (mutex->m_lock.is_populated()) + { + make_mutex_row(mutex); + m_next_pos.set_after(&m_pos); + return 0; + } + } + break; + case pos_all_instr::VIEW_RWLOCK: + for ( ; m_pos.m_index_2 < rwlock_max; m_pos.m_index_2++) + { + rwlock= &rwlock_array[m_pos.m_index_2]; + if (rwlock->m_lock.is_populated()) + { + make_rwlock_row(rwlock); + m_next_pos.set_after(&m_pos); + return 0; + } + } + break; + case pos_all_instr::VIEW_COND: + for ( ; m_pos.m_index_2 < cond_max; m_pos.m_index_2++) + { + cond= &cond_array[m_pos.m_index_2]; + if (cond->m_lock.is_populated()) + { + make_cond_row(cond); + m_next_pos.set_after(&m_pos); + return 0; + } + } + break; + case pos_all_instr::VIEW_FILE: + for ( ; m_pos.m_index_2 < file_max; m_pos.m_index_2++) + { + file= &file_array[m_pos.m_index_2]; + if (file->m_lock.is_populated()) + { + make_file_row(file); + 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; + + set_position(pos); + + switch (m_pos.m_index_1) { + case pos_all_instr::VIEW_MUTEX: + DBUG_ASSERT(m_pos.m_index_2 < mutex_max); + mutex= &mutex_array[m_pos.m_index_2]; + if (mutex->m_lock.is_populated()) + { + make_mutex_row(mutex); + return 0; + } + break; + case pos_all_instr::VIEW_RWLOCK: + DBUG_ASSERT(m_pos.m_index_2 < rwlock_max); + rwlock= &rwlock_array[m_pos.m_index_2]; + if (rwlock->m_lock.is_populated()) + { + make_rwlock_row(rwlock); + return 0; + } + break; + case pos_all_instr::VIEW_COND: + DBUG_ASSERT(m_pos.m_index_2 < cond_max); + cond= &cond_array[m_pos.m_index_2]; + if (cond->m_lock.is_populated()) + { + make_cond_row(cond); + return 0; + } + break; + case pos_all_instr::VIEW_FILE: + DBUG_ASSERT(m_pos.m_index_2 < file_max); + file= &file_array[m_pos.m_index_2]; + if (file->m_lock.is_populated()) + { + make_file_row(file); + return 0; + } + break; + } + + return HA_ERR_RECORD_DELETED; +} + +table_all_instr_class::table_all_instr_class +(const PFS_engine_table_share *share) + : PFS_readonly_table(share, &m_pos), + m_pos(), m_next_pos() +{} + +void table_all_instr_class::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_all_instr_class::rnd_next(void) +{ + PFS_mutex_class *mutex_class; + PFS_rwlock_class *rwlock_class; + PFS_cond_class *cond_class; + PFS_file_class *file_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_all_instr_class::VIEW_MUTEX: + mutex_class= find_mutex_class(m_pos.m_index_2); + if (mutex_class) + { + make_instr_row(mutex_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_all_instr_class::VIEW_RWLOCK: + rwlock_class= find_rwlock_class(m_pos.m_index_2); + if (rwlock_class) + { + make_instr_row(rwlock_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_all_instr_class::VIEW_COND: + cond_class= find_cond_class(m_pos.m_index_2); + if (cond_class) + { + make_instr_row(cond_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_all_instr_class::VIEW_FILE: + file_class= find_file_class(m_pos.m_index_2); + if (file_class) + { + make_instr_row(file_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + } + } + + return HA_ERR_END_OF_FILE; +} + +int table_all_instr_class::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; + + set_position(pos); + switch (m_pos.m_index_1) { + case pos_all_instr_class::VIEW_MUTEX: + mutex_class= find_mutex_class(m_pos.m_index_2); + if (mutex_class) + { + make_instr_row(mutex_class); + return 0; + } + break; + case pos_all_instr_class::VIEW_RWLOCK: + rwlock_class= find_rwlock_class(m_pos.m_index_2); + if (rwlock_class) + { + make_instr_row(rwlock_class); + return 0; + } + break; + case pos_all_instr_class::VIEW_COND: + cond_class= find_cond_class(m_pos.m_index_2); + if (cond_class) + { + make_instr_row(cond_class); + return 0; + } + break; + case pos_all_instr_class::VIEW_FILE: + file_class= find_file_class(m_pos.m_index_2); + if (file_class) + { + make_instr_row(file_class); + 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 00000000000..a8767695602 --- /dev/null +++ b/storage/perfschema/table_all_instr.h @@ -0,0 +1,168 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** Position of a cursor on table_all_instr_class. */ +struct pos_all_instr_class : public PFS_double_index, + public PFS_instrument_view_constants +{ + pos_all_instr_class() + : PFS_double_index(VIEW_MUTEX, 1) + {} + + inline void reset(void) + { + m_index_1= VIEW_MUTEX; + m_index_2= 1; + } + + inline bool has_more_view(void) + { return (m_index_1 <= VIEW_FILE); } + + inline void next_view(void) + { + m_index_1++; + /* Instrument keys start at 1, not 0. */ + m_index_2= 1; + } +}; + +/** + Abstract table, a union of all instrumentations class metadata. + This table is a union of: + - a view on all mutex classes, + - a view on all rwlock classes, + - a view on all cond classes, + - a view on all file classes +*/ +class table_all_instr_class : public PFS_readonly_table +{ +public: + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + table_all_instr_class(const PFS_engine_table_share *share); + +public: + ~table_all_instr_class() + {} + +protected: + /** + Build a row. + @param klass the instrument class + */ + virtual void make_instr_row(PFS_instr_class *klass)= 0; + + /** Current position. */ + pos_all_instr_class m_pos; + /** Next position. */ + pos_all_instr_class m_next_pos; +}; + +/** 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(VIEW_MUTEX, 0) + {} + + inline void reset(void) + { + m_index_1= VIEW_MUTEX; + m_index_2= 0; + } + + inline bool has_more_view(void) + { return (m_index_1 <= VIEW_FILE); } + + 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 +*/ +class table_all_instr : public PFS_readonly_table +{ +public: + 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; + + /** Current position. */ + pos_all_instr m_pos; + /** Next position. */ + pos_all_instr 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 00000000000..3a12578597e --- /dev/null +++ b/storage/perfschema/table_events_waits.cc @@ -0,0 +1,773 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/table_events_waits.cc + Table EVENTS_WAITS_xxx (implementation). +*/ + +#include "mysql_priv.h" +#include "table_events_waits.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" +#include "pfs_events_waits.h" + +THR_LOCK table_events_waits_current::m_table_lock; + +static const TABLE_FIELD_TYPE field_types[]= +{ + { + { C_STRING_WITH_LEN("THREAD_ID") }, + { C_STRING_WITH_LEN("int(11)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("EVENT_ID") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("EVENT_NAME") }, + { C_STRING_WITH_LEN("varchar(128)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("SOURCE") }, + { C_STRING_WITH_LEN("varchar(64)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("TIMER_START") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("TIMER_END") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("TIMER_WAIT") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("SPINS") }, + { C_STRING_WITH_LEN("int(10)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("OBJECT_SCHEMA") }, + { C_STRING_WITH_LEN("varchar(64)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("OBJECT_NAME") }, + { C_STRING_WITH_LEN("varchar(512)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("OBJECT_TYPE") }, + { C_STRING_WITH_LEN("varchar(64)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("OBJECT_INSTANCE_BEGIN") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("NESTING_EVENT_ID") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("OPERATION") }, + { C_STRING_WITH_LEN("varchar(16)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("NUMBER_OF_BYTES") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("FLAGS") }, + { C_STRING_WITH_LEN("int(10)") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_events_waits_current::m_field_def= +{ 16, field_types }; + +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, + 1000, /* records */ + sizeof(pos_events_waits_current), /* ref length */ + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +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, + 1000, /* records */ + sizeof(pos_events_waits_history), /* ref length */ + &m_table_lock, + &table_events_waits_current::m_field_def, + false /* checked */ +}; + +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, + 10000, /* records */ + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + &table_events_waits_current::m_field_def, + false /* checked */ +}; + +table_events_waits_common::table_events_waits_common +(const PFS_engine_table_share *share, void *pos) + : PFS_readonly_table(share, pos), + m_row_exists(false) +{} + +void table_events_waits_common::clear_object_columns() +{ + m_row.m_object_type= NULL; + m_row.m_object_type_length= 0; + m_row.m_object_schema_length= 0; + m_row.m_object_name_length= 0; +} + +/** + Build a row. + @param thread_own_wait True if the memory for the wait + is owned by pfs_thread + @param pfs_thread the thread the cursor is reading + @param wait the wait the cursor is reading +*/ +void table_events_waits_common::make_row(bool thread_own_wait, + PFS_thread *pfs_thread, + PFS_events_waits *wait) +{ + pfs_lock lock; + PFS_thread *safe_thread; + PFS_instr_class *safe_class; + const char *base; + const char *safe_source_file; + + m_row_exists= false; + safe_thread= sanitize_thread(pfs_thread); + if (unlikely(safe_thread == NULL)) + return; + + /* Protect this reader against a thread termination */ + if (thread_own_wait) + safe_thread->m_lock.begin_optimistic_lock(&lock); + + /* + Design choice: + We could have used a pfs_lock in PFS_events_waits here, + to protect the reader from concurrent event generation, + but this leads to too many pfs_lock atomic operations + each time an event is recorded: + - 1 dirty() + 1 allocated() per event start, for EVENTS_WAITS_CURRENT + - 1 dirty() + 1 allocated() per event end, for EVENTS_WAITS_CURRENT + - 1 dirty() + 1 allocated() per copy to EVENTS_WAITS_HISTORY + - 1 dirty() + 1 allocated() per copy to EVENTS_WAITS_HISTORY_LONG + or 8 atomics per recorded event. + The problem is that we record a *lot* of events ... + + Instead, a *dirty* marking is done using m_wait_class. + Using m_wait_class alone does not guarantee anything, it just filters + out most of the bad data. + This is acceptable because this code is garbage-proof, + and won't crash on bad data, only display it, + very rarely (which is accepted). + + If a bad record is displayed, it's a very transient failure: + the next select * from EVENTS_WAITS_CURRENT/_HISTORY/_HISTORY_LONG will + show clean data again. + */ + + m_row.m_thread_internal_id= safe_thread->m_thread_internal_id; + m_row.m_event_id= wait->m_event_id; + m_row.m_timer_state= wait->m_timer_state; + m_row.m_timer_start= wait->m_timer_start; + m_row.m_timer_end= wait->m_timer_end; + m_row.m_object_instance_addr= (intptr) wait->m_object_instance_addr; + + /* + 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_MUTEX: + clear_object_columns(); + safe_class= sanitize_mutex_class((PFS_mutex_class*) wait->m_class); + break; + case WAIT_CLASS_RWLOCK: + clear_object_columns(); + safe_class= sanitize_rwlock_class((PFS_rwlock_class*) wait->m_class); + break; + case WAIT_CLASS_COND: + clear_object_columns(); + safe_class= sanitize_cond_class((PFS_cond_class*) wait->m_class); + break; + case WAIT_CLASS_TABLE: + m_row.m_object_type= "TABLE"; + m_row.m_object_type_length= 5; + memcpy(m_row.m_object_schema, wait->m_schema_name, + wait->m_schema_name_length); + m_row.m_object_schema_length= wait->m_schema_name_length; + memcpy(m_row.m_object_name, wait->m_object_name, + wait->m_object_name_length); + m_row.m_object_name_length= wait->m_object_name_length; + safe_class= &global_table_class; + break; + case WAIT_CLASS_FILE: + m_row.m_object_type= "FILE"; + m_row.m_object_type_length= 4; + m_row.m_object_schema_length= 0; + memcpy(m_row.m_object_name, wait->m_object_name, + wait->m_object_name_length); + m_row.m_object_name_length= wait->m_object_name_length; + safe_class= sanitize_file_class((PFS_file_class*) wait->m_class); + break; + case NO_WAIT_CLASS: + default: + return; + } + if (unlikely(safe_class == NULL)) + return; + m_row.m_name= safe_class->m_name; + m_row.m_name_length= safe_class->m_name_length; + + /* + We are assuming this pointer is sane, + since it comes from __FILE__. + */ + safe_source_file= wait->m_source_file; + if (unlikely(safe_source_file == NULL)) + return; + + base= base_name(wait->m_source_file); + m_row.m_source_length= my_snprintf(m_row.m_source, sizeof(m_row.m_source), + "%s:%d", base, wait->m_source_line); + if (m_row.m_source_length > sizeof(m_row.m_source)) + m_row.m_source_length= sizeof(m_row.m_source); + m_row.m_operation= wait->m_operation; + m_row.m_number_of_bytes= wait->m_number_of_bytes; + m_row.m_flags= 0; + + if (thread_own_wait) + { + if (safe_thread->m_lock.end_optimistic_lock(&lock)) + m_row_exists= true; + } + else + { + /* + For EVENTS_WAITS_HISTORY_LONG (thread_own_wait is false), + the wait record is always valid, because it is not stored + in memory owned by pfs_thread. + Even when the thread terminated, the record is mostly readable, + so this record is displayed. + */ + m_row_exists= true; + } +} + +/** + Operations names map, as displayed in the 'OPERATION' column. + Indexed by enum_operation_type - 1. + Note: enum_operation_type contains a more precise definition, + since more details are needed internally by the instrumentation. + Different similar operations (CLOSE vs STREAMCLOSE) are displayed + with the same name 'close'. +*/ +static const LEX_STRING operation_names_map[]= +{ + /* Mutex operations */ + { C_STRING_WITH_LEN("lock") }, + { C_STRING_WITH_LEN("try_lock") }, + + /* RWLock operations */ + { C_STRING_WITH_LEN("read_lock") }, + { C_STRING_WITH_LEN("write_lock") }, + { C_STRING_WITH_LEN("try_read_lock") }, + { C_STRING_WITH_LEN("try_write_lock") }, + + /* Condition operations */ + { C_STRING_WITH_LEN("wait") }, + { C_STRING_WITH_LEN("timed_wait") }, + + /* File operations */ + { C_STRING_WITH_LEN("create") }, + { C_STRING_WITH_LEN("create") }, /* create tmp */ + { C_STRING_WITH_LEN("open") }, + { C_STRING_WITH_LEN("open") }, /* stream open */ + { C_STRING_WITH_LEN("close") }, + { C_STRING_WITH_LEN("close") }, /* stream close */ + { C_STRING_WITH_LEN("read") }, + { C_STRING_WITH_LEN("write") }, + { C_STRING_WITH_LEN("seek") }, + { C_STRING_WITH_LEN("tell") }, + { C_STRING_WITH_LEN("flush") }, + { C_STRING_WITH_LEN("stat") }, + { C_STRING_WITH_LEN("stat") }, /* fstat */ + { C_STRING_WITH_LEN("chsize") }, + { C_STRING_WITH_LEN("delete") }, + { C_STRING_WITH_LEN("rename") }, + { C_STRING_WITH_LEN("sync") } +}; + + +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_ulong(f, m_row.m_thread_internal_id); + break; + case 1: /* EVENT_ID */ + set_field_ulonglong(f, m_row.m_event_id); + break; + case 2: /* EVENT_NAME */ + set_field_varchar_utf8(f, m_row.m_name, m_row.m_name_length); + break; + case 3: /* SOURCE */ + set_field_varchar_utf8(f, m_row.m_source, m_row.m_source_length); + break; + case 4: /* TIMER_START */ + if ((m_row.m_timer_state == TIMER_STATE_STARTED) || + (m_row.m_timer_state == TIMER_STATE_TIMED)) + set_field_ulonglong(f, m_row.m_timer_start); + else + f->set_null(); + break; + case 5: /* TIMER_END */ + if (m_row.m_timer_state == TIMER_STATE_TIMED) + set_field_ulonglong(f, m_row.m_timer_end); + else + f->set_null(); + break; + case 6: /* TIMER_WAIT */ + if (m_row.m_timer_state == TIMER_STATE_TIMED) + set_field_ulonglong(f, m_row.m_timer_end - m_row.m_timer_start); + else + f->set_null(); + break; + case 7: /* SPINS */ + f->set_null(); + break; + case 8: /* 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 9: /* 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 10: /* OBJECT_TYPE */ + if (m_row.m_object_type) + { + set_field_varchar_utf8(f, m_row.m_object_type, + m_row.m_object_type_length); + } + else + f->set_null(); + break; + case 11: /* OBJECT_INSTANCE */ + set_field_ulonglong(f, m_row.m_object_instance_addr); + break; + case 12: /* NESTING_EVENT_ID */ + f->set_null(); + break; + case 13: /* OPERATION */ + operation= &operation_names_map[(int) m_row.m_operation - 1]; + set_field_varchar_utf8(f, operation->str, operation->length); + break; + case 14: /* NUMBER_OF_BYTES */ + if ((m_row.m_operation == OPERATION_TYPE_FILEREAD) || + (m_row.m_operation == OPERATION_TYPE_FILEWRITE) || + (m_row.m_operation == OPERATION_TYPE_FILECHSIZE)) + set_field_ulonglong(f, m_row.m_number_of_bytes); + else + f->set_null(); + break; + case 15: /* FLAGS */ + set_field_ulong(f, m_row.m_flags); + break; + default: + DBUG_ASSERT(false); + } + } + } + return 0; +} + +PFS_engine_table* table_events_waits_current::create(void) +{ + return new table_events_waits_current(); +} + +table_events_waits_current::table_events_waits_current() + : table_events_waits_common(&m_share, &m_pos), + m_pos(), m_next_pos() +{} + +void table_events_waits_current::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_events_waits_current::rnd_next(void) +{ + PFS_thread *pfs_thread; + PFS_events_waits *wait; + + for (m_pos.set_at(&m_next_pos); + m_pos.m_index_1 < thread_max; + m_pos.next_thread()) + { + pfs_thread= &thread_array[m_pos.m_index_1]; + + if (! pfs_thread->m_lock.is_populated()) + { + /* This thread does not exist */ + continue; + } + + /* + We do not show nested events for now, + this will be revised with TABLE io + */ +#define ONLY_SHOW_ONE_WAIT + +#ifdef ONLY_SHOW_ONE_WAIT + if (m_pos.m_index_2 >= 1) + continue; +#else + if (m_pos.m_index_2 >= pfs_thread->m_wait_locker_count) + continue; +#endif + + wait= &pfs_thread->m_wait_locker_stack[m_pos.m_index_2].m_waits_current; + + if (wait->m_wait_class == NO_WAIT_CLASS) + { + /* + This locker does not exist. + There can not be more lockers in the stack, skip to the next thread + */ + continue; + } + + make_row(true, pfs_thread, wait); + /* Next iteration, look for the next locker in this thread */ + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_events_waits_current::rnd_pos(const void *pos) +{ + PFS_thread *pfs_thread; + PFS_events_waits *wait; + + set_position(pos); + DBUG_ASSERT(m_pos.m_index_1 < thread_max); + pfs_thread= &thread_array[m_pos.m_index_1]; + + if (! pfs_thread->m_lock.is_populated()) + return HA_ERR_RECORD_DELETED; + +#ifdef ONLY_SHOW_CURRENT_WAITS + if (m_pos.m_index_2 >= pfs_thread->m_wait_locker_count) + return HA_ERR_RECORD_DELETED; +#endif + + DBUG_ASSERT(m_pos.m_index_2 < LOCKER_STACK_SIZE); + + wait= &pfs_thread->m_wait_locker_stack[m_pos.m_index_2].m_waits_current; + + if (wait->m_wait_class == NO_WAIT_CLASS) + return HA_ERR_RECORD_DELETED; + + make_row(true, pfs_thread, wait); + return 0; +} + +int table_events_waits_current::delete_all_rows(void) +{ + reset_events_waits_current(); + return 0; +} + +PFS_engine_table* table_events_waits_history::create(void) +{ + return new table_events_waits_history(); +} + +table_events_waits_history::table_events_waits_history() + : table_events_waits_common(&m_share, &m_pos), + m_pos(), m_next_pos() +{} + +void table_events_waits_history::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_events_waits_history::rnd_next(void) +{ + PFS_thread *pfs_thread; + PFS_events_waits *wait; + + if (events_waits_history_per_thread == 0) + return HA_ERR_END_OF_FILE; + + for (m_pos.set_at(&m_next_pos); + m_pos.m_index_1 < thread_max; + m_pos.next_thread()) + { + pfs_thread= &thread_array[m_pos.m_index_1]; + + if (! pfs_thread->m_lock.is_populated()) + { + /* This thread does not exist */ + continue; + } + + if (m_pos.m_index_2 >= events_waits_history_per_thread) + { + /* This thread does not have more (full) history */ + continue; + } + + if ( ! pfs_thread->m_waits_history_full && + (m_pos.m_index_2 >= pfs_thread->m_waits_history_index)) + { + /* This thread does not have more (not full) history */ + continue; + } + + if (pfs_thread->m_waits_history[m_pos.m_index_2].m_wait_class + == NO_WAIT_CLASS) + { + /* + This locker does not exist. + There can not be more lockers in the stack, skip to the next thread + */ + continue; + } + + wait= &pfs_thread->m_waits_history[m_pos.m_index_2]; + + make_row(true, pfs_thread, wait); + /* Next iteration, look for the next history in this thread */ + m_next_pos.set_after(&m_pos); + return 0; + } + + return HA_ERR_END_OF_FILE; +} + +int table_events_waits_history::rnd_pos(const void *pos) +{ + PFS_thread *pfs_thread; + PFS_events_waits *wait; + + DBUG_ASSERT(events_waits_history_per_thread != 0); + set_position(pos); + DBUG_ASSERT(m_pos.m_index_1 < thread_max); + pfs_thread= &thread_array[m_pos.m_index_1]; + + if (! pfs_thread->m_lock.is_populated()) + return HA_ERR_RECORD_DELETED; + + DBUG_ASSERT(m_pos.m_index_2 < events_waits_history_per_thread); + + if ( ! pfs_thread->m_waits_history_full && + (m_pos.m_index_2 >= pfs_thread->m_waits_history_index)) + return HA_ERR_RECORD_DELETED; + + if (pfs_thread->m_waits_history[m_pos.m_index_2].m_wait_class + == NO_WAIT_CLASS) + return HA_ERR_RECORD_DELETED; + + wait= &pfs_thread->m_waits_history[m_pos.m_index_2]; + + make_row(true, pfs_thread, wait); + return 0; +} + +int table_events_waits_history::delete_all_rows(void) +{ + reset_events_waits_history(); + return 0; +} + +PFS_engine_table* table_events_waits_history_long::create(void) +{ + return new table_events_waits_history_long(); +} + +table_events_waits_history_long::table_events_waits_history_long() + : table_events_waits_common(&m_share, &m_pos), + m_pos(0), m_next_pos(0) +{} + +void table_events_waits_history_long::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_events_waits_history_long::rnd_next(void) +{ + PFS_events_waits *wait; + uint limit; + + if (events_waits_history_long_size == 0) + return HA_ERR_END_OF_FILE; + + if (events_waits_history_long_full) + limit= events_waits_history_long_size; + else + limit= events_waits_history_long_index % events_waits_history_long_size; + + for (m_pos.set_at(&m_next_pos); m_pos.m_index < limit; m_pos.next()) + { + wait= &events_waits_history_long_array[m_pos.m_index]; + + if (wait->m_wait_class != NO_WAIT_CLASS) + { + make_row(false, wait->m_thread, wait); + /* Next iteration, look for the next entry */ + m_next_pos.set_after(&m_pos); + return 0; + } + } + + return HA_ERR_END_OF_FILE; +} + +int table_events_waits_history_long::rnd_pos(const void *pos) +{ + PFS_events_waits *wait; + uint limit; + + if (events_waits_history_long_size == 0) + return HA_ERR_RECORD_DELETED; + + set_position(pos); + + if (events_waits_history_long_full) + limit= events_waits_history_long_size; + else + limit= events_waits_history_long_index % events_waits_history_long_size; + + if (m_pos.m_index >= limit) + return HA_ERR_RECORD_DELETED; + + wait= &events_waits_history_long_array[m_pos.m_index]; + + if (wait->m_wait_class == NO_WAIT_CLASS) + return HA_ERR_RECORD_DELETED; + + make_row(false, wait->m_thread, wait); + return 0; +} + +int table_events_waits_history_long::delete_all_rows(void) +{ + reset_events_waits_history_long(); + return 0; +} + diff --git a/storage/perfschema/table_events_waits.h b/storage/perfschema/table_events_waits.h new file mode 100644 index 00000000000..5aa16cc0cab --- /dev/null +++ b/storage/perfschema/table_events_waits.h @@ -0,0 +1,248 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. */ + ulong m_thread_internal_id; + /** Column EVENT_ID. */ + ulonglong m_event_id; + /** Column EVENT_NAME. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** Timer state. */ + enum timer_state m_timer_state; + /** Column TIMER_START. */ + ulonglong m_timer_start; + /** True if TIMER_END is null. */ + bool m_timer_end_null; + /** Column TIMER_END. */ + ulonglong m_timer_end; + /** 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 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_readonly_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(); + + void make_row(bool thread_own_wait, PFS_thread *pfs_thread, + PFS_events_waits *wait); + + /** Current row. */ + row_events_waits m_row; + /** True is 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(); + + 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; + /** + Fields definition. + Also used by table_events_waits_history + and table_events_waits_history_long. + */ + static TABLE_FIELD_DEF m_field_def; + + /** 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(); + + 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; + + /** 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(); + + 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 00000000000..c3d678d374c --- /dev/null +++ b/storage/perfschema/table_events_waits_summary.cc @@ -0,0 +1,696 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/table_events_waits_summary.cc + Table EVENTS_WAITS_SUMMARY_BY_xxx (implementation). +*/ + +#include "mysql_priv.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" + +THR_LOCK table_events_waits_summary_by_thread_by_event_name::m_table_lock; + +static const TABLE_FIELD_TYPE ews_by_thread_by_event_name_field_types[]= +{ + { + { C_STRING_WITH_LEN("THREAD_ID") }, + { C_STRING_WITH_LEN("int(11)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("EVENT_NAME") }, + { C_STRING_WITH_LEN("varchar(128)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("COUNT_STAR") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("SUM_TIMER_WAIT") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("MIN_TIMER_WAIT") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("AVG_TIMER_WAIT") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("MAX_TIMER_WAIT") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_events_waits_summary_by_thread_by_event_name::m_field_def= +{ 7, ews_by_thread_by_event_name_field_types }; + +PFS_engine_table_share +table_events_waits_summary_by_thread_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME") }, + &pfs_truncatable_acl, + &table_events_waits_summary_by_thread_by_event_name::create, + NULL, /* write_row */ + &table_events_waits_summary_by_thread_by_event_name::delete_all_rows, + 1000, /* records */ + sizeof(pos_events_waits_summary_by_thread_by_event_name), + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +PFS_engine_table* +table_events_waits_summary_by_thread_by_event_name::create(void) +{ + return new table_events_waits_summary_by_thread_by_event_name(); +} + +int +table_events_waits_summary_by_thread_by_event_name::delete_all_rows(void) +{ + reset_per_thread_wait_stat(); + return 0; +} + +table_events_waits_summary_by_thread_by_event_name +::table_events_waits_summary_by_thread_by_event_name() + : PFS_readonly_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_events_waits_summary_by_thread_by_event_name::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_events_waits_summary_by_thread_by_event_name::rnd_next(void) +{ + PFS_thread *thread; + PFS_mutex_class *mutex_class; + PFS_rwlock_class *rwlock_class; + PFS_cond_class *cond_class; + PFS_file_class *file_class; + + for (m_pos.set_at(&m_next_pos); + m_pos.has_more_thread(); + m_pos.next_thread()) + { + thread= &thread_array[m_pos.m_index_1]; + if (thread->m_lock.is_populated()) + { + for ( ; m_pos.has_more_view(); m_pos.next_view()) + { + switch (m_pos.m_index_2) { + case pos_events_waits_summary_by_thread_by_event_name::VIEW_MUTEX: + mutex_class= find_mutex_class(m_pos.m_index_3); + if (mutex_class) + { + make_mutex_row(thread, mutex_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_events_waits_summary_by_thread_by_event_name::VIEW_RWLOCK: + rwlock_class= find_rwlock_class(m_pos.m_index_3); + if (rwlock_class) + { + make_rwlock_row(thread, rwlock_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_events_waits_summary_by_thread_by_event_name::VIEW_COND: + cond_class= find_cond_class(m_pos.m_index_3); + if (cond_class) + { + make_cond_row(thread, cond_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_events_waits_summary_by_thread_by_event_name::VIEW_FILE: + file_class= find_file_class(m_pos.m_index_3); + if (file_class) + { + make_file_row(thread, file_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + } + } + } + } + + return HA_ERR_END_OF_FILE; +} + +int +table_events_waits_summary_by_thread_by_event_name::rnd_pos(const void *pos) +{ + PFS_thread *thread; + PFS_mutex_class *mutex_class; + PFS_rwlock_class *rwlock_class; + PFS_cond_class *cond_class; + PFS_file_class *file_class; + + set_position(pos); + DBUG_ASSERT(m_pos.m_index_1 < thread_max); + + thread= &thread_array[m_pos.m_index_1]; + if (! thread->m_lock.is_populated()) + return HA_ERR_RECORD_DELETED; + + switch (m_pos.m_index_2) { + case pos_events_waits_summary_by_thread_by_event_name::VIEW_MUTEX: + mutex_class= find_mutex_class(m_pos.m_index_3); + if (mutex_class) + { + make_mutex_row(thread, mutex_class); + return 0; + } + break; + case pos_events_waits_summary_by_thread_by_event_name::VIEW_RWLOCK: + rwlock_class= find_rwlock_class(m_pos.m_index_3); + if (rwlock_class) + { + make_rwlock_row(thread, rwlock_class); + return 0; + } + break; + case pos_events_waits_summary_by_thread_by_event_name::VIEW_COND: + cond_class= find_cond_class(m_pos.m_index_3); + if (cond_class) + { + make_cond_row(thread, cond_class); + return 0; + } + break; + case pos_events_waits_summary_by_thread_by_event_name::VIEW_FILE: + file_class= find_file_class(m_pos.m_index_3); + if (file_class) + { + make_file_row(thread, file_class); + return 0; + } + break; + } + return HA_ERR_RECORD_DELETED; +} + +void table_events_waits_summary_by_thread_by_event_name +::make_instr_row(PFS_thread *thread, PFS_instr_class *klass, + PFS_single_stat_chain *stat) +{ + pfs_lock 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_name= klass->m_name; + m_row.m_name_length= klass->m_name_length; + + m_row.m_count= stat->m_count; + m_row.m_sum= stat->m_sum; + m_row.m_min= stat->m_min; + m_row.m_max= stat->m_max; + + if (m_row.m_count) + m_row.m_avg= m_row.m_sum / m_row.m_count; + else + { + m_row.m_min= 0; + m_row.m_avg= 0; + } + + if (thread->m_lock.end_optimistic_lock(&lock)) + m_row_exists= true; +} + +void table_events_waits_summary_by_thread_by_event_name +::make_mutex_row(PFS_thread *thread, PFS_mutex_class *klass) +{ + PFS_single_stat_chain *stat; + stat= find_per_thread_mutex_class_wait_stat(thread, klass); + make_instr_row(thread, klass, stat); +} + +void table_events_waits_summary_by_thread_by_event_name +::make_rwlock_row(PFS_thread *thread, PFS_rwlock_class *klass) +{ + PFS_single_stat_chain *stat; + stat= find_per_thread_rwlock_class_wait_stat(thread, klass); + make_instr_row(thread, klass, stat); +} + +void table_events_waits_summary_by_thread_by_event_name +::make_cond_row(PFS_thread *thread, PFS_cond_class *klass) +{ + PFS_single_stat_chain *stat; + stat= find_per_thread_cond_class_wait_stat(thread, klass); + make_instr_row(thread, klass, stat); +} + +void table_events_waits_summary_by_thread_by_event_name +::make_file_row(PFS_thread *thread, PFS_file_class *klass) +{ + PFS_single_stat_chain *stat; + stat= find_per_thread_file_class_wait_stat(thread, klass); + make_instr_row(thread, klass, stat); +} + +int table_events_waits_summary_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_ulong(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: /* COUNT */ + set_field_ulonglong(f, m_row.m_count); + break; + case 3: /* SUM */ + set_field_ulonglong(f, m_row.m_sum); + break; + case 4: /* MIN */ + set_field_ulonglong(f, m_row.m_min); + break; + case 5: /* AVG */ + set_field_ulonglong(f, m_row.m_avg); + break; + case 6: /* MAX */ + set_field_ulonglong(f, m_row.m_max); + break; + default: + DBUG_ASSERT(false); + } + } + } + + return 0; +} + +THR_LOCK table_events_waits_summary_by_event_name::m_table_lock; + +static const TABLE_FIELD_TYPE ews_by_event_name_field_types[]= +{ + { + { C_STRING_WITH_LEN("EVENT_NAME") }, + { C_STRING_WITH_LEN("varchar(128)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("COUNT_STAR") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("SUM_TIMER_WAIT") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("MIN_TIMER_WAIT") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("AVG_TIMER_WAIT") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("MAX_TIMER_WAIT") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_events_waits_summary_by_event_name::m_field_def= +{ 6, ews_by_event_name_field_types }; + +PFS_engine_table_share +table_events_waits_summary_by_event_name::m_share= +{ + { C_STRING_WITH_LEN("EVENTS_WAITS_SUMMARY_BY_EVENT_NAME") }, + &pfs_truncatable_acl, + &table_events_waits_summary_by_event_name::create, + NULL, /* write_row */ + &table_events_waits_summary_by_event_name::delete_all_rows, + 1000, /* records */ + sizeof(pos_all_instr_class), + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +PFS_engine_table* table_events_waits_summary_by_event_name::create(void) +{ + return new table_events_waits_summary_by_event_name(); +} + +int table_events_waits_summary_by_event_name::delete_all_rows(void) +{ + reset_instrument_class_waits(); + return 0; +} + +table_events_waits_summary_by_event_name +::table_events_waits_summary_by_event_name() + : table_all_instr_class(&m_share) +{} + +void table_events_waits_summary_by_event_name +::make_instr_row(PFS_instr_class *klass) +{ + m_row.m_name= klass->m_name; + m_row.m_name_length= klass->m_name_length; + + m_row.m_count= klass->m_wait_stat.m_count; + m_row.m_sum= klass->m_wait_stat.m_sum; + m_row.m_min= klass->m_wait_stat.m_min; + m_row.m_max= klass->m_wait_stat.m_max; + + if (m_row.m_count) + m_row.m_avg= m_row.m_sum / m_row.m_count; + else + { + m_row.m_min= 0; + m_row.m_avg= 0; + } +} + +int table_events_waits_summary_by_event_name +::read_row_values(TABLE *table, unsigned char *, Field **fields, + bool read_all) +{ + Field *f; + + /* Set the null bits */ + 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_name, m_row.m_name_length); + break; + case 1: /* COUNT */ + set_field_ulonglong(f, m_row.m_count); + break; + case 2: /* SUM */ + set_field_ulonglong(f, m_row.m_sum); + break; + case 3: /* MIN */ + set_field_ulonglong(f, m_row.m_min); + break; + case 4: /* AVG */ + set_field_ulonglong(f, m_row.m_avg); + break; + case 5: /* MAX */ + set_field_ulonglong(f, m_row.m_max); + break; + default: + DBUG_ASSERT(false); + } + } + } + + return 0; +} + +THR_LOCK table_events_waits_summary_by_instance::m_table_lock; + +static const TABLE_FIELD_TYPE ews_by_instance_field_types[]= +{ + { + { C_STRING_WITH_LEN("EVENT_NAME") }, + { C_STRING_WITH_LEN("varchar(128)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("OBJECT_INSTANCE_BEGIN") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("COUNT_STAR") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("SUM_TIMER_WAIT") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("MIN_TIMER_WAIT") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("AVG_TIMER_WAIT") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("MAX_TIMER_WAIT") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_events_waits_summary_by_instance::m_field_def= +{ 7, ews_by_instance_field_types }; + +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, + 1000, /* records */ + sizeof(pos_all_instr), + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +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_lock 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; + + m_row.m_count= pfs->m_wait_stat.m_count; + m_row.m_sum= pfs->m_wait_stat.m_sum; + m_row.m_min= pfs->m_wait_stat.m_min; + m_row.m_max= pfs->m_wait_stat.m_max; + + if (m_row.m_count) + m_row.m_avg= m_row.m_sum / m_row.m_count; + else + { + m_row.m_min= 0; + m_row.m_avg= 0; + } + + 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); +} + +/** + 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); +} + +/** + 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); +} + +/** + 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; + + /* + 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); +} + +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_count); + break; + case 3: /* SUM */ + set_field_ulonglong(f, m_row.m_sum); + break; + case 4: /* MIN */ + set_field_ulonglong(f, m_row.m_min); + break; + case 5: /* AVG */ + set_field_ulonglong(f, m_row.m_avg); + break; + case 6: /* MAX */ + set_field_ulonglong(f, m_row.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 00000000000..28c820e5c81 --- /dev/null +++ b/storage/perfschema/table_events_waits_summary.h @@ -0,0 +1,261 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** + A row of table + PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME. +*/ +struct row_events_waits_summary_by_thread_by_event_name +{ + /** Column THREAD_ID. */ + ulong m_thread_internal_id; + /** Column EVENT_NAME. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** 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; +}; + +/** + Position of a cursor on + PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME. +*/ +struct pos_events_waits_summary_by_thread_by_event_name +: public PFS_triple_index, public PFS_instrument_view_constants +{ + pos_events_waits_summary_by_thread_by_event_name() + : PFS_triple_index(0, VIEW_MUTEX, 1) + {} + + inline void reset(void) + { + m_index_1= 0; + m_index_2= VIEW_MUTEX; + m_index_3= 1; + } + + inline bool has_more_thread(void) + { return (m_index_1 < thread_max); } + + inline bool has_more_view(void) + { return (m_index_2 <= VIEW_FILE); } + + inline void next_thread(void) + { + m_index_1++; + m_index_2= VIEW_MUTEX; + m_index_3= 1; + } + + 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_events_waits_summary_by_thread_by_event_name + : public PFS_readonly_table +{ +public: + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + + 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_events_waits_summary_by_thread_by_event_name(); + +public: + ~table_events_waits_summary_by_thread_by_event_name() + {} + +protected: + void make_instr_row(PFS_thread *thread, PFS_instr_class *klass, + PFS_single_stat_chain *stat); + void make_mutex_row(PFS_thread *thread, PFS_mutex_class *klass); + void make_rwlock_row(PFS_thread *thread, PFS_rwlock_class *klass); + void make_cond_row(PFS_thread *thread, PFS_cond_class *klass); + void make_file_row(PFS_thread *thread, PFS_file_class *klass); + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_events_waits_summary_by_thread_by_event_name m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_events_waits_summary_by_thread_by_event_name m_pos; + /** Next position. */ + pos_events_waits_summary_by_thread_by_event_name m_next_pos; +}; + +/** A row of PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_EVENT_NAME. */ +struct row_events_waits_summary_by_event_name +{ + /** Column EVENT_NAME. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** 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; +}; + +/** Table PERFORMANCE_SCHEMA.EVENTS_WAITS_SUMMARY_BY_EVENT_NAME. */ +class table_events_waits_summary_by_event_name : public table_all_instr_class +{ +public: + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + +protected: + virtual void make_instr_row(PFS_instr_class *klass); + + virtual int read_row_values(TABLE *table, + unsigned char *buf, + Field **fields, + bool read_all); + + table_events_waits_summary_by_event_name(); + +public: + ~table_events_waits_summary_by_event_name() + {} + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_events_waits_summary_by_event_name m_row; +}; + +/** 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; + /** 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; +}; + +/** 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); + 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 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; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_events_waits_summary_by_instance m_row; + /** True is the current row exists. */ + bool m_row_exists; +}; + +/** @} */ +#endif diff --git a/storage/perfschema/table_file_instances.cc b/storage/perfschema/table_file_instances.cc new file mode 100644 index 00000000000..b6bc7ecbcf3 --- /dev/null +++ b/storage/perfschema/table_file_instances.cc @@ -0,0 +1,180 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/table_file_instances.cc + Table FILE_INSTANCES (implementation). +*/ + +#include "mysql_priv.h" +#include "pfs_instr.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_file_instances.h" +#include "pfs_global.h" + +THR_LOCK table_file_instances::m_table_lock; + +static const TABLE_FIELD_TYPE field_types[]= +{ + { + { C_STRING_WITH_LEN("FILE_NAME") }, + { C_STRING_WITH_LEN("varchar(512)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("EVENT_NAME") }, + { C_STRING_WITH_LEN("varchar(128)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("OPEN_COUNT") }, + { C_STRING_WITH_LEN("int(10)") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_file_instances::m_field_def= +{ 3, field_types }; + +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 */ + 1000, /* records */ + sizeof(PFS_simple_index), + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +PFS_engine_table* table_file_instances::create(void) +{ + return new table_file_instances(); +} + +table_file_instances::table_file_instances() + : PFS_readonly_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; + + for (m_pos.set_at(&m_next_pos); + m_pos.m_index < file_max; + m_pos.next()) + { + pfs= &file_array[m_pos.m_index]; + if (pfs->m_lock.is_populated()) + { + 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); + DBUG_ASSERT(m_pos.m_index < file_max); + pfs= &file_array[m_pos.m_index]; + + if (! pfs->m_lock.is_populated()) + return HA_ERR_RECORD_DELETED; + + make_row(pfs); + return 0; +} + +void table_file_instances::make_row(PFS_file *pfs) +{ + pfs_lock 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 00000000000..fb5298f37b3 --- /dev/null +++ b/storage/perfschema/table_file_instances.h @@ -0,0 +1,90 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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_readonly_table +{ +public: + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + + 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; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_file_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_file_summary.cc b/storage/perfschema/table_file_summary.cc new file mode 100644 index 00000000000..0db4e08625a --- /dev/null +++ b/storage/perfschema/table_file_summary.cc @@ -0,0 +1,371 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/table_file_summary.cc + Table FILE_SUMMARY_BY_xxx (implementation). +*/ + +#include "mysql_priv.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_file_summary.h" +#include "pfs_global.h" + +THR_LOCK table_file_summary_by_event_name::m_table_lock; + +static const TABLE_FIELD_TYPE fs_by_event_name_field_types[]= +{ + { + { C_STRING_WITH_LEN("EVENT_NAME") }, + { C_STRING_WITH_LEN("varchar(128)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("COUNT_READ") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("COUNT_WRITE") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("SUM_NUMBER_OF_BYTES_READ") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("SUM_NUMBER_OF_BYTES_WRITE") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_file_summary_by_event_name::m_field_def= +{ 5, fs_by_event_name_field_types }; + +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, + 1000, /* records */ + sizeof(PFS_simple_index), + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +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_class_io(); + return 0; +} + +table_file_summary_by_event_name::table_file_summary_by_event_name() + : PFS_readonly_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 klass the file class the cursor is reading +*/ +void table_file_summary_by_event_name::make_row(PFS_file_class *klass) +{ + m_row.m_name= &klass->m_name[0]; + m_row.m_name_length= klass->m_name_length; + m_row.m_file_stat= klass->m_file_stat; +} + +int table_file_summary_by_event_name::read_row_values(TABLE *table, + unsigned char *, + Field **fields, + bool read_all) +{ + Field *f; + + /* Set the null bits */ + DBUG_ASSERT(table->s->null_bytes == 0); + + /* The row always exists for classes */ + + 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: /* COUNT_READ */ + set_field_ulonglong(f, m_row.m_file_stat.m_count_read); + break; + case 2: /* COUNT_WRITE */ + set_field_ulonglong(f, m_row.m_file_stat.m_count_write); + break; + case 3: /* READ_BYTES */ + set_field_ulonglong(f, m_row.m_file_stat.m_read_bytes); + break; + case 4: /* WRITE_BYTES */ + set_field_ulonglong(f, m_row.m_file_stat.m_write_bytes); + break; + default: + DBUG_ASSERT(false); + } + } + } + + return 0; +} + +THR_LOCK table_file_summary_by_instance::m_table_lock; + +static const TABLE_FIELD_TYPE fs_by_instance_field_types[]= +{ + { + { C_STRING_WITH_LEN("FILE_NAME") }, + { C_STRING_WITH_LEN("varchar(512)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("EVENT_NAME") }, + { C_STRING_WITH_LEN("varchar(128)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("COUNT_READ") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("COUNT_WRITE") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("SUM_NUMBER_OF_BYTES_READ") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("SUM_NUMBER_OF_BYTES_WRITE") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_file_summary_by_instance::m_field_def= +{ 6, fs_by_instance_field_types }; + +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, + 1000, /* records */ + sizeof(PFS_simple_index), + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +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; +} + +table_file_summary_by_instance::table_file_summary_by_instance() + : PFS_readonly_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; + + for (m_pos.set_at(&m_next_pos); + m_pos.m_index < file_max; + m_pos.next()) + { + pfs= &file_array[m_pos.m_index]; + if (pfs->m_lock.is_populated()) + { + 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); + DBUG_ASSERT(m_pos.m_index < file_max); + pfs= &file_array[m_pos.m_index]; + + if (! pfs->m_lock.is_populated()) + return HA_ERR_RECORD_DELETED; + + make_row(pfs); + return 0; +} + +/** + Build a row. + @param pfs the file the cursor is reading +*/ +void table_file_summary_by_instance::make_row(PFS_file *pfs) +{ + pfs_lock 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_name= safe_class->m_name; + m_row.m_name_length= safe_class->m_name_length; + m_row.m_file_stat= pfs->m_file_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: /* FILENAME */ + set_field_varchar_utf8(f, m_row.m_filename, m_row.m_filename_length); + break; + case 1: /* NAME */ + set_field_varchar_utf8(f, m_row.m_name, m_row.m_name_length); + break; + case 2: /* COUNT_READ */ + set_field_ulonglong(f, m_row.m_file_stat.m_count_read); + break; + case 3: /* COUNT_WRITE */ + set_field_ulonglong(f, m_row.m_file_stat.m_count_write); + break; + case 4: /* READ_BYTES */ + set_field_ulonglong(f, m_row.m_file_stat.m_read_bytes); + break; + case 5: /* WRITE_BYTES */ + set_field_ulonglong(f, m_row.m_file_stat.m_write_bytes); + break; + default: + DBUG_ASSERT(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_file_summary.h b/storage/perfschema/table_file_summary.h new file mode 100644 index 00000000000..e962292a725 --- /dev/null +++ b/storage/perfschema/table_file_summary.h @@ -0,0 +1,151 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef TABLE_FILE_SUMMARY_H +#define TABLE_FILE_SUMMARY_H + +/** + @file storage/perfschema/table_file_summary.h + Table FILE_SUMMARY_BY_xxx (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" +#include "pfs_instr_class.h" +#include "pfs_instr.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. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** + Columns COUNT_READ, COUNT_WRITE, + SUM_NUMBER_OF_BYTES_READ, SUM_NUMBER_OF_BYTES_WRITE. + */ + PFS_file_stat m_file_stat; +}; + +/** Table PERFORMANCE_SCHEMA.FILE_SUMMARY_BY_EVENT_NAME. */ +class table_file_summary_by_event_name : public PFS_readonly_table +{ +public: + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + void make_row(PFS_file_class *klass); + + 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: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_file_summary_by_event_name m_row; + /** Current position. */ + PFS_simple_index m_pos; + /** Next position. */ + PFS_simple_index m_next_pos; +}; + +/** 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. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** + Columns COUNT_READ, COUNT_WRITE, + SUM_NUMBER_OF_BYTES_READ, SUM_NUMBER_OF_BYTES_WRITE. + */ + PFS_file_stat m_file_stat; +}; + +/** Table PERFORMANCE_SCHEMA.FILE_UMMARY_BY_INSTANCE. */ +class table_file_summary_by_instance : public PFS_readonly_table +{ +public: + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int delete_all_rows(); + + virtual int rnd_next(); + virtual int rnd_pos(const void *pos); + virtual void reset_position(void); + +protected: + void make_row(PFS_file *pfs); + + 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: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_file_summary_by_instance 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_performance_timers.cc b/storage/perfschema/table_performance_timers.cc new file mode 100644 index 00000000000..ab4e72b89fa --- /dev/null +++ b/storage/perfschema/table_performance_timers.cc @@ -0,0 +1,187 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/table_performance_timers.cc + Table PERFORMANCE_TIMERS (implementation). +*/ + +#include "mysql_priv.h" +#include "table_performance_timers.h" +#include "pfs_timer.h" +#include "pfs_global.h" + +THR_LOCK table_performance_timers::m_table_lock; + +static const TABLE_FIELD_TYPE field_types[]= +{ + { + { C_STRING_WITH_LEN("TIMER_NAME") }, + { C_STRING_WITH_LEN("enum(\'CYCLE\',\'NANOSECOND\',\'MICROSECOND\'," + "\'MILLISECOND\',\'TICK\')") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("TIMER_FREQUENCY") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("TIMER_RESOLUTION") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("TIMER_OVERHEAD") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_performance_timers::m_field_def= +{ 4, field_types }; + +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 */ + COUNT_TIMER_NAME, /* records */ + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +PFS_engine_table* table_performance_timers::create(void) +{ + return new table_performance_timers(); +} + +table_performance_timers::table_performance_timers() + : PFS_readonly_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= pfs_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= pfs_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= pfs_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= pfs_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= pfs_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 00000000000..0818a0af2fe --- /dev/null +++ b/storage/perfschema/table_performance_timers.h @@ -0,0 +1,86 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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_readonly_table +{ +public: + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + + 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; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** 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_processlist.cc b/storage/perfschema/table_processlist.cc new file mode 100644 index 00000000000..d0707b9df60 --- /dev/null +++ b/storage/perfschema/table_processlist.cc @@ -0,0 +1,176 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/table_processlist.cc + Table PROCESSLIST (implementation). +*/ + +#include "mysql_priv.h" +#include "table_processlist.h" +#include "pfs_instr_class.h" +#include "pfs_instr.h" + +THR_LOCK table_processlist::m_table_lock; + +static const TABLE_FIELD_TYPE field_types[]= +{ + { + { C_STRING_WITH_LEN("THREAD_ID") }, + { C_STRING_WITH_LEN("int(11)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("ID") }, + { C_STRING_WITH_LEN("int(11)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("NAME") }, + { C_STRING_WITH_LEN("varchar(64)") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_processlist::m_field_def= +{ 3, field_types }; + +PFS_engine_table_share +table_processlist::m_share= +{ + { C_STRING_WITH_LEN("PROCESSLIST") }, + &pfs_readonly_acl, + &table_processlist::create, + NULL, /* write_row */ + NULL, /* delete_all_rows */ + 1000, /* records */ + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +PFS_engine_table* table_processlist::create(void) +{ + return new table_processlist(); +} + +table_processlist::table_processlist() + : PFS_readonly_table(&m_share, &m_pos), + m_row_exists(false), m_pos(0), m_next_pos(0) +{} + +void table_processlist::reset_position(void) +{ + m_pos.m_index= 0; + m_next_pos.m_index= 0; +} + +int table_processlist::rnd_next(void) +{ + PFS_thread *pfs; + + for (m_pos.set_at(&m_next_pos); + m_pos.m_index < thread_max; + m_pos.next()) + { + pfs= &thread_array[m_pos.m_index]; + if (pfs->m_lock.is_populated()) + { + make_row(pfs); + m_next_pos.set_after(&m_pos); + return 0; + } + } + + return HA_ERR_END_OF_FILE; +} + +int table_processlist::rnd_pos(const void *pos) +{ + PFS_thread *pfs; + + set_position(pos); + DBUG_ASSERT(m_pos.m_index < thread_max); + pfs= &thread_array[m_pos.m_index]; + if (pfs->m_lock.is_populated()) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_processlist::make_row(PFS_thread *pfs) +{ + pfs_lock lock; + 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_thread_id= pfs->m_thread_id; + m_row.m_name= safe_class->m_name; + m_row.m_name_length= safe_class->m_name_length; + + if (pfs->m_lock.end_optimistic_lock(&lock)) + m_row_exists= true; +} + +int table_processlist::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_ulong(f, m_row.m_thread_internal_id); + break; + case 1: /* ID */ + set_field_ulong(f, m_row.m_thread_id); + break; + case 2: /* NAME */ + set_field_varchar_utf8(f, m_row.m_name, m_row.m_name_length); + break; + default: + DBUG_ASSERT(false); + } + } + } + return 0; +} + diff --git a/storage/perfschema/table_processlist.h b/storage/perfschema/table_processlist.h new file mode 100644 index 00000000000..2c6d5160f41 --- /dev/null +++ b/storage/perfschema/table_processlist.h @@ -0,0 +1,91 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef TABLE_PROCESSIST_H +#define TABLE_PROCESSIST_H + +/** + @file storage/perfschema/table_processlist.h + Table PROCESSLIST (declarations). +*/ + +#include "pfs_column_types.h" +#include "pfs_engine_table.h" + +struct PFS_thread; + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.PROCESSLIST. */ +struct row_processlist +{ + /** Column THREAD_ID. */ + ulong m_thread_internal_id; + /** Column ID. */ + ulong m_thread_id; + /** Column NAME. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; +}; + +/** Table PERFORMANCE_SCHEMA.PROCESSLIST. */ +class table_processlist : public PFS_readonly_table +{ +public: + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + + 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_processlist(); + +public: + ~table_processlist() + {} + +private: + void make_row(PFS_thread *pfs); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_processlist m_row; + /** 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_consumers.cc b/storage/perfschema/table_setup_consumers.cc new file mode 100644 index 00000000000..f23dca8a675 --- /dev/null +++ b/storage/perfschema/table_setup_consumers.cc @@ -0,0 +1,211 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/table_setup_consumers.cc + Table SETUP_CONSUMERS (implementation). +*/ + +#include "mysql_priv.h" +#include "table_setup_consumers.h" +#include "pfs_instr.h" +#include "pfs_events_waits.h" + +#define COUNT_SETUP_CONSUMERS 8 +static row_setup_consumers all_setup_consumers_data[COUNT_SETUP_CONSUMERS]= +{ + { + { C_STRING_WITH_LEN("events_waits_current") }, + &flag_events_waits_current + }, + { + { C_STRING_WITH_LEN("events_waits_history") }, + &flag_events_waits_history + }, + { + { C_STRING_WITH_LEN("events_waits_history_long") }, + &flag_events_waits_history_long + }, + { + { C_STRING_WITH_LEN("events_waits_summary_by_thread_by_event_name") }, + &flag_events_waits_summary_by_thread_by_event_name + }, + { + { C_STRING_WITH_LEN("events_waits_summary_by_event_name") }, + &flag_events_waits_summary_by_event_name + }, + { + { C_STRING_WITH_LEN("events_waits_summary_by_instance") }, + &flag_events_waits_summary_by_instance + }, + { + { C_STRING_WITH_LEN("file_summary_by_event_name") }, + &flag_file_summary_by_event_name + }, + { + { C_STRING_WITH_LEN("file_summary_by_instance") }, + &flag_file_summary_by_instance + } +}; + +THR_LOCK table_setup_consumers::m_table_lock; + +static const TABLE_FIELD_TYPE field_types[]= +{ + { + { C_STRING_WITH_LEN("NAME") }, + { C_STRING_WITH_LEN("varchar(64)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("ENABLED") }, + { C_STRING_WITH_LEN("enum(\'YES\',\'NO\')") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_setup_consumers::m_field_def= +{ 2, field_types }; + +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 */ + COUNT_SETUP_CONSUMERS, /* records */ + sizeof(PFS_simple_index), /* ref length */ + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +PFS_engine_table* table_setup_consumers::create(void) +{ + return new table_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, 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 *, + 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 */ + my_error(ER_WRONG_PERFSCHEMA_USAGE, MYF(0)); + 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); + } + } + } + + return 0; +} + + diff --git a/storage/perfschema/table_setup_consumers.h b/storage/perfschema/table_setup_consumers.h new file mode 100644 index 00000000000..f54f69fcef5 --- /dev/null +++ b/storage/perfschema/table_setup_consumers.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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; +}; + +/** 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(); + + 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, + unsigned char *new_buf, + Field **fields); + + table_setup_consumers(); + +public: + ~table_setup_consumers() + {} + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** 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 00000000000..6756e780d78 --- /dev/null +++ b/storage/perfschema/table_setup_instruments.cc @@ -0,0 +1,276 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/table_setup_instruments.cc + Table SETUP_INSTRUMENTS (implementation). +*/ + +#include "mysql_priv.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_setup_instruments.h" +#include "pfs_global.h" + +THR_LOCK table_setup_instruments::m_table_lock; + +static const TABLE_FIELD_TYPE field_types[]= +{ + { + { C_STRING_WITH_LEN("NAME") }, + { C_STRING_WITH_LEN("varchar(128)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("ENABLED") }, + { C_STRING_WITH_LEN("enum(\'YES\',\'NO\')") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("TIMED") }, + { C_STRING_WITH_LEN("enum(\'YES\',\'NO\')") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_setup_instruments::m_field_def= +{ 3, field_types }; + +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 */ + 1000, /* records */ + sizeof(pos_setup_instruments), + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +PFS_engine_table* table_setup_instruments::create(void) +{ + return new table_setup_instruments(); +} + +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_mutex_class *mutex_class; + PFS_rwlock_class *rwlock_class; + PFS_cond_class *cond_class; + PFS_file_class *file_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_setup_instruments::VIEW_MUTEX: + mutex_class= find_mutex_class(m_pos.m_index_2); + if (mutex_class) + { + make_row(mutex_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_setup_instruments::VIEW_RWLOCK: + rwlock_class= find_rwlock_class(m_pos.m_index_2); + if (rwlock_class) + { + make_row(rwlock_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_setup_instruments::VIEW_COND: + cond_class= find_cond_class(m_pos.m_index_2); + if (cond_class) + { + make_row(cond_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + case pos_setup_instruments::VIEW_THREAD: + /* Reserved for WL#4674, PERFORMANCE_SCHEMA Setup For Actors. */ + break; + case pos_setup_instruments::VIEW_FILE: + file_class= find_file_class(m_pos.m_index_2); + if (file_class) + { + make_row(file_class); + m_next_pos.set_after(&m_pos); + return 0; + } + break; + } + } + + return HA_ERR_END_OF_FILE; +} + +int table_setup_instruments::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; + + set_position(pos); + + switch (m_pos.m_index_1) { + case pos_setup_instruments::VIEW_MUTEX: + mutex_class= find_mutex_class(m_pos.m_index_2); + if (mutex_class) + { + make_row(mutex_class); + return 0; + } + break; + case pos_setup_instruments::VIEW_RWLOCK: + rwlock_class= find_rwlock_class(m_pos.m_index_2); + if (rwlock_class) + { + make_row(rwlock_class); + return 0; + } + break; + case pos_setup_instruments::VIEW_COND: + cond_class= find_cond_class(m_pos.m_index_2); + if (cond_class) + { + make_row(cond_class); + return 0; + } + break; + case pos_setup_instruments::VIEW_THREAD: + /* Reserved for WL#4674, PERFORMANCE_SCHEMA Setup For Actors. */ + break; + case pos_setup_instruments::VIEW_FILE: + file_class= find_file_class(m_pos.m_index_2); + if (file_class) + { + make_row(file_class); + return 0; + } + break; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_setup_instruments::make_row(PFS_instr_class *klass) +{ + m_row.m_name= &klass->m_name[0]; + m_row.m_name_length= klass->m_name_length; + m_row.m_enabled_ptr= &klass->m_enabled; + m_row.m_timed_ptr= &klass->m_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_name, m_row.m_name_length); + break; + case 1: /* ENABLED */ + set_field_enum(f, (*m_row.m_enabled_ptr) ? ENUM_YES : ENUM_NO); + break; + case 2: /* TIMED */ + if (m_row.m_timed_ptr) + set_field_enum(f, (*m_row.m_timed_ptr) ? ENUM_YES : ENUM_NO); + else + set_field_enum(f, ENUM_NO); + break; + default: + DBUG_ASSERT(false); + } + } + } + + return 0; +} + +int table_setup_instruments::update_row_values(TABLE *table, + const unsigned char *, + 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 */ + my_error(ER_WRONG_PERFSCHEMA_USAGE, MYF(0)); + 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; + case 2: /* TIMED */ + if (m_row.m_timed_ptr) + { + value= (enum_yes_no) get_field_enum(f); + *m_row.m_timed_ptr= (value == ENUM_YES) ? true : false; + } + break; + default: + DBUG_ASSERT(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_setup_instruments.h b/storage/perfschema/table_setup_instruments.h new file mode 100644 index 00000000000..549fe4fa5f9 --- /dev/null +++ b/storage/perfschema/table_setup_instruments.h @@ -0,0 +1,121 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 +{ + /** Column NAME. */ + const char *m_name; + /** Length in bytes of @c m_name. */ + uint m_name_length; + /** Column ENABLED. */ + bool *m_enabled_ptr; + /** Column TIMED. */ + bool *m_timed_ptr; +}; + +/** Position of a cursor on PERFORMANCE_SCHEMA.SETUP_INSTRUMENTS. */ +struct pos_setup_instruments : public PFS_double_index +{ + static const uint VIEW_MUTEX= 1; + static const uint VIEW_RWLOCK= 2; + static const uint VIEW_COND= 3; + /** Reverved for WL#4674, PERFORMANCE_SCHEMA Setup For Actors. */ + static const uint VIEW_THREAD= 4; + static const uint VIEW_FILE= 5; + + pos_setup_instruments() + : PFS_double_index(VIEW_MUTEX, 1) + {} + + inline void reset(void) + { + m_index_1= VIEW_MUTEX; + m_index_2= 1; + } + + inline bool has_more_view(void) + { return (m_index_1 <= VIEW_FILE); } + + 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(); + + 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, + unsigned char *new_buf, + Field **fields); + + table_setup_instruments(); + +public: + ~table_setup_instruments() + {} + +private: + void make_row(PFS_instr_class *klass); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** 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 00000000000..53a80782da0 --- /dev/null +++ b/storage/perfschema/table_setup_objects.cc @@ -0,0 +1,280 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/table_setup_objects.cc + Table SETUP_OBJECTS (implementation). +*/ + +#include "mysql_priv.h" +#include "pfs_instr_class.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_setup_objects.h" +#include "pfs_global.h" + +THR_LOCK table_setup_objects::m_table_lock; + +static const TABLE_FIELD_TYPE field_types[]= +{ + { + { C_STRING_WITH_LEN("OBJECT_TYPE") }, + { C_STRING_WITH_LEN("varchar(64)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("OBJECT_SCHEMA") }, + { C_STRING_WITH_LEN("varchar(64)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("OBJECT_NAME") }, + { C_STRING_WITH_LEN("varchar(64)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("ENABLED") }, + { C_STRING_WITH_LEN("enum(\'YES\',\'NO\')") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("TIMED") }, + { C_STRING_WITH_LEN("enum(\'YES\',\'NO\')") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("AGGREGATED") }, + { C_STRING_WITH_LEN("enum(\'YES\',\'NO\')") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_setup_objects::m_field_def= +{ 6, field_types }; + +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, + 1000, /* records */ + sizeof(pos_setup_objects), + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +PFS_engine_table* table_setup_objects::create(void) +{ + return new table_setup_objects(); +} + +int table_setup_objects::write_row(TABLE *table, unsigned char *buf, + Field **fields) +{ + /* Not implemented */ + return HA_ERR_WRONG_COMMAND; +} + +int table_setup_objects::delete_all_rows(void) +{ + /* Not implemented */ + return HA_ERR_WRONG_COMMAND; +} + +table_setup_objects::table_setup_objects() + : PFS_engine_table(&m_share, &m_pos), + m_row_exists(false), m_pos(), m_next_pos() +{} + +void table_setup_objects::reset_position(void) +{ + m_pos.reset(); + m_next_pos.reset(); +} + +int table_setup_objects::rnd_next(void) +{ + PFS_table_share *table_share; + + 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_setup_objects::VIEW_TABLE: + for ( ; m_pos.m_index_2 < table_share_max; m_pos.m_index_2++) + { + table_share= &table_share_array[m_pos.m_index_2]; + if (table_share->m_lock.is_populated()) + { + make_row(table_share); + m_next_pos.set_after(&m_pos); + return 0; + } + } + break; + case pos_setup_objects::VIEW_EVENT: + case pos_setup_objects::VIEW_PROCEDURE: + case pos_setup_objects::VIEW_FUNCTION: + default: + break; + } + } + + return HA_ERR_END_OF_FILE; +} + +int table_setup_objects::rnd_pos(const void *pos) +{ + PFS_table_share *share; + + set_position(pos); + + switch (m_pos.m_index_1) { + case pos_setup_objects::VIEW_TABLE: + DBUG_ASSERT(m_pos.m_index_2 < table_share_max); + share= &table_share_array[m_pos.m_index_2]; + if (share->m_lock.is_populated()) + { + make_row(share); + return 0; + } + break; + case pos_setup_objects::VIEW_EVENT: + case pos_setup_objects::VIEW_PROCEDURE: + case pos_setup_objects::VIEW_FUNCTION: + default: + break; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_setup_objects::make_row(PFS_table_share *share) +{ + pfs_lock lock; + + m_row_exists= false; + if (share == NULL) + return; + + share->m_lock.begin_optimistic_lock(&lock); + + m_row.m_schema_name= &share->m_schema_name[0]; + m_row.m_schema_name_length= share->m_schema_name_length; + m_row.m_object_name= &share->m_table_name[0]; + m_row.m_object_name_length= share->m_table_name_length; + m_row.m_enabled_ptr= &share->m_enabled; + m_row.m_timed_ptr= &share->m_timed; + m_row.m_aggregated_ptr= &share->m_aggregated; + + if (share->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_varchar_utf8(f, "TABLE", 5); + break; + case 1: /* OBJECT_SCHEMA */ + set_field_varchar_utf8(f, m_row.m_schema_name, + m_row.m_schema_name_length); + break; + case 2: /* OBJECT_NAME */ + set_field_varchar_utf8(f, m_row.m_object_name, + m_row.m_object_name_length); + 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; + case 5: /* AGGREGATED */ + set_field_enum(f, (*m_row.m_aggregated_ptr) ? ENUM_YES : ENUM_NO); + break; + default: + DBUG_ASSERT(false); + } + } + } + + return 0; +} + +int table_setup_objects::update_row_values(TABLE *table, + const unsigned char *, + 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: /* OBJECT_TYPE */ + case 1: /* OBJECT_SCHEMA */ + case 2: /* OBJECT_NAME */ + my_error(ER_WRONG_PERFSCHEMA_USAGE, MYF(0)); + return HA_ERR_WRONG_COMMAND; + case 3: /* ENABLED */ + value= (enum_yes_no) get_field_enum(f); + *m_row.m_enabled_ptr= (value == ENUM_YES) ? true : false; + break; + case 4: /* TIMED */ + value= (enum_yes_no) get_field_enum(f); + *m_row.m_timed_ptr= (value == ENUM_YES) ? true : false; + break; + case 5: /* AGGREGATED */ + value= (enum_yes_no) get_field_enum(f); + *m_row.m_aggregated_ptr= (value == ENUM_YES) ? true : false; + break; + default: + DBUG_ASSERT(false); + } + } + } + + return 0; +} + diff --git a/storage/perfschema/table_setup_objects.h b/storage/perfschema/table_setup_objects.h new file mode 100644 index 00000000000..994e12016a6 --- /dev/null +++ b/storage/perfschema/table_setup_objects.h @@ -0,0 +1,125 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef TABLE_SETUP_OBJECTS_H +#define TABLE_SETUP_OBJECTS_H + +/** + @file storage/perfschema/table_setup_objects.h + Table SETUP_OBJECTS (declarations). +*/ + +#include "pfs_instr_class.h" +#include "pfs_engine_table.h" + +/** + @addtogroup Performance_schema_tables + @{ +*/ + +/** A row of PERFORMANCE_SCHEMA.SETUP_OBJECTS. */ +struct row_setup_objects +{ + /** Column SCHEMA_NAME. */ + const char *m_schema_name; + /** Length in bytes of @c m_schema_name. */ + uint m_schema_name_length; + /** Column OBJECT_NAME. */ + const char *m_object_name; + /** 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; + /** Column AGGREGATED. */ + bool *m_aggregated_ptr; +}; + +/** Position of a cursor on PERFORMANCE_SCHEMA.SETUP_OBJECTS. */ +struct pos_setup_objects : public PFS_double_index, + public PFS_object_view_constants +{ + pos_setup_objects() + : PFS_double_index(VIEW_TABLE, 0) + {} + + inline void reset(void) + { + m_index_1= VIEW_TABLE; + m_index_2= 0; + } + + inline bool has_more_view(void) + { return (m_index_1 <= VIEW_FUNCTION); } + + inline void next_view(void) + { + m_index_1++; + m_index_2= 0; + } +}; + +/** Table PERFORMANCE_SCHEMA.SETUP_OBJECTS. */ +class table_setup_objects : public PFS_engine_table +{ +public: + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + static int write_row(TABLE *table, unsigned char *buf, Field **fields); + static int delete_all_rows(); + + 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, + unsigned char *new_buf, + Field **fields); + + table_setup_objects(); + +public: + ~table_setup_objects() + {} + +private: + void make_row(PFS_table_share *share); + + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_setup_objects m_row; + /** True is the current row exists. */ + bool m_row_exists; + /** Current position. */ + pos_setup_objects m_pos; + /** Next position. */ + pos_setup_objects 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 00000000000..2d57adb3a85 --- /dev/null +++ b/storage/perfschema/table_setup_timers.cc @@ -0,0 +1,183 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/table_setup_timers.cc + Table SETUP_TIMERS (implementation). +*/ + +#include "mysql_priv.h" +#include "table_setup_timers.h" +#include "pfs_column_values.h" +#include "pfs_timer.h" + +#define COUNT_SETUP_TIMERS 1 +static row_setup_timers all_setup_timers_data[COUNT_SETUP_TIMERS]= +{ + { + { C_STRING_WITH_LEN("wait") }, + &wait_timer + } +}; + +THR_LOCK table_setup_timers::m_table_lock; + +static const TABLE_FIELD_TYPE field_types[]= +{ + { + { C_STRING_WITH_LEN("NAME") }, + { C_STRING_WITH_LEN("varchar(64)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("TIMER_NAME") }, + { C_STRING_WITH_LEN("enum(\'CYCLE\',\'NANOSECOND\',\'MICROSECOND\'," + "\'MILLISECOND\',\'TICK\')") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_setup_timers::m_field_def= +{ 2, field_types }; + +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 */ + COUNT_SETUP_TIMERS, + sizeof(PFS_simple_index), + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +PFS_engine_table* table_setup_timers::create(void) +{ + return new table_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, 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 *, + 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 */ + my_error(ER_WRONG_PERFSCHEMA_USAGE, MYF(0)); + 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 00000000000..96af76ae05c --- /dev/null +++ b/storage/perfschema/table_setup_timers.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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(); + + 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, + unsigned char *new_buf, + Field **fields); + + table_setup_timers(); + +public: + ~table_setup_timers() + {} + +private: + /** Table share lock. */ + static THR_LOCK m_table_lock; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** 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_sync_instances.cc b/storage/perfschema/table_sync_instances.cc new file mode 100644 index 00000000000..a783b5b817b --- /dev/null +++ b/storage/perfschema/table_sync_instances.cc @@ -0,0 +1,507 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file storage/perfschema/table_sync_instances.cc + Table MUTEX_INSTANCES, RWLOCK_INSTANCES + and COND_INSTANCES (implementation). +*/ + +#include "mysql_priv.h" +#include "pfs_instr.h" +#include "pfs_column_types.h" +#include "pfs_column_values.h" +#include "table_sync_instances.h" +#include "pfs_global.h" + +THR_LOCK table_mutex_instances::m_table_lock; + +static const TABLE_FIELD_TYPE mutex_field_types[]= +{ + { + { C_STRING_WITH_LEN("NAME") }, + { C_STRING_WITH_LEN("varchar(128)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("OBJECT_INSTANCE_BEGIN") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("LOCKED_BY_THREAD_ID") }, + { C_STRING_WITH_LEN("int(11)") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_mutex_instances::m_field_def= +{ 3, mutex_field_types }; + +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 */ + 1000, /* records */ + sizeof(PFS_simple_index), + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +PFS_engine_table* table_mutex_instances::create(void) +{ + return new table_mutex_instances(); +} + +table_mutex_instances::table_mutex_instances() + : PFS_readonly_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; + + for (m_pos.set_at(&m_next_pos); m_pos.m_index < mutex_max; m_pos.next()) + { + pfs= &mutex_array[m_pos.m_index]; + if (pfs->m_lock.is_populated()) + { + 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); + DBUG_ASSERT(m_pos.m_index < mutex_max); + pfs= &mutex_array[m_pos.m_index]; + if (pfs->m_lock.is_populated()) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_mutex_instances::make_row(PFS_mutex *pfs) +{ + pfs_lock 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_ulong(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; + +static const TABLE_FIELD_TYPE rwlock_field_types[]= +{ + { + { C_STRING_WITH_LEN("NAME") }, + { C_STRING_WITH_LEN("varchar(128)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("OBJECT_INSTANCE_BEGIN") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("WRITE_LOCKED_BY_THREAD_ID") }, + { C_STRING_WITH_LEN("int(11)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("READ_LOCKED_BY_COUNT") }, + { C_STRING_WITH_LEN("int(10)") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_rwlock_instances::m_field_def= +{ 4, rwlock_field_types }; + +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 */ + 1000, /* records */ + sizeof(PFS_simple_index), + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +PFS_engine_table* table_rwlock_instances::create(void) +{ + return new table_rwlock_instances(); +} + +table_rwlock_instances::table_rwlock_instances() + : PFS_readonly_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; + + for (m_pos.set_at(&m_next_pos); m_pos.m_index < rwlock_max; m_pos.next()) + { + pfs= &rwlock_array[m_pos.m_index]; + if (pfs->m_lock.is_populated()) + { + 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); + DBUG_ASSERT(m_pos.m_index < rwlock_max); + pfs= &rwlock_array[m_pos.m_index]; + if (pfs->m_lock.is_populated()) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_rwlock_instances::make_row(PFS_rwlock *pfs) +{ + pfs_lock 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_ulong(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; + +static const TABLE_FIELD_TYPE cond_field_types[]= +{ + { + { C_STRING_WITH_LEN("NAME") }, + { C_STRING_WITH_LEN("varchar(128)") }, + { NULL, 0} + }, + { + { C_STRING_WITH_LEN("OBJECT_INSTANCE_BEGIN") }, + { C_STRING_WITH_LEN("bigint(20)") }, + { NULL, 0} + } +}; + +TABLE_FIELD_DEF +table_cond_instances::m_field_def= +{ 2, cond_field_types }; + +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 */ + 1000, /* records */ + sizeof(PFS_simple_index), + &m_table_lock, + &m_field_def, + false /* checked */ +}; + +PFS_engine_table* table_cond_instances::create(void) +{ + return new table_cond_instances(); +} + +table_cond_instances::table_cond_instances() + : PFS_readonly_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; + + for (m_pos.set_at(&m_next_pos); m_pos.m_index < cond_max; m_pos.next()) + { + pfs= &cond_array[m_pos.m_index]; + if (pfs->m_lock.is_populated()) + { + 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); + DBUG_ASSERT(m_pos.m_index < cond_max); + pfs= &cond_array[m_pos.m_index]; + if (pfs->m_lock.is_populated()) + { + make_row(pfs); + return 0; + } + + return HA_ERR_RECORD_DELETED; +} + +void table_cond_instances::make_row(PFS_cond *pfs) +{ + pfs_lock 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 00000000000..a8a9cdaa071 --- /dev/null +++ b/storage/perfschema/table_sync_instances.h @@ -0,0 +1,206 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. */ + ulong m_locked_by_thread_id; +}; + +/** Table PERFORMANCE_SCHEMA.MUTEX_INSTANCES. */ +class table_mutex_instances : public PFS_readonly_table +{ +public: + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + + 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; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_mutex_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; +}; + +/** 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. */ + ulong 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_readonly_table +{ +public: + /** Table share */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + + 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; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_rwlock_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; +}; + +/** 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_readonly_table +{ +public: + /** Table share. */ + static PFS_engine_table_share m_share; + static PFS_engine_table* create(); + + 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; + /** Fields definition. */ + static TABLE_FIELD_DEF m_field_def; + + /** Current row. */ + row_cond_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/unittest/CMakeLists.txt b/storage/perfschema/unittest/CMakeLists.txt new file mode 100644 index 00000000000..01f3fbb8c98 --- /dev/null +++ b/storage/perfschema/unittest/CMakeLists.txt @@ -0,0 +1,39 @@ +# Copyright (C) 2009 Sun Microsystems, Inc +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/include/mysql + ${CMAKE_SOURCE_DIR}/regex + ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_SOURCE_DIR}/extra/yassl/include + ${CMAKE_SOURCE_DIR}/unittest/mytap + ${CMAKE_SOURCE_DIR}/storage/perfschema) + +ADD_DEFINITIONS(-DMYSQL_SERVER) + +LINK_LIBRARIES(perfschema mytap mysys dbug strings) + +ADD_EXECUTABLE(pfs_instr_class-t pfs_instr_class-t.cc) + +ADD_EXECUTABLE(pfs_instr_class-oom-t pfs_instr_class-oom-t.cc) + +ADD_EXECUTABLE(pfs_instr-t pfs_instr-t.cc) + +ADD_EXECUTABLE(pfs_instr-oom-t pfs_instr-oom-t.cc) + +ADD_EXECUTABLE(pfs_timer-t pfs_timer-t.cc) + +ADD_EXECUTABLE(pfs-t pfs-t.cc) + diff --git a/storage/perfschema/unittest/Makefile.am b/storage/perfschema/unittest/Makefile.am new file mode 100644 index 00000000000..7d82753c9e5 --- /dev/null +++ b/storage/perfschema/unittest/Makefile.am @@ -0,0 +1,58 @@ +# Copyright (C) 2008-2009 Sun Microsystems, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +INCLUDES = -I$(top_builddir)/include \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/include/mysql \ + -I$(top_srcdir)/regex \ + -I$(top_srcdir)/unittest/mytap \ + -I$(top_srcdir)/sql \ + -I$(top_srcdir)/storage/perfschema + +DEFS = -DMYSQL_SERVER @DEFS@ + +TEST_CODE = $(top_builddir)/unittest/mytap/libmytap.a + +$(TEST_CODE) : + (cd $(top_builddir)/unittest/mytap; $(MAKE)) + +PROD_CODE = $(top_builddir)/storage/perfschema/libperfschema.a \ + $(top_builddir)/mysys/libmysys.a \ + $(top_builddir)/dbug/libdbug.a \ + $(top_builddir)/strings/libmystrings.a + +noinst_PROGRAMS = pfs_instr_class-t pfs_instr_class-oom-t \ + pfs_instr-t pfs_instr-oom-t pfs_timer-t pfs-t + +pfs_t_SOURCES = pfs-t.cc stub_print_error.h +pfs_t_LDADD = $(TEST_CODE) $(PROD_CODE) + +pfs_instr_class_t_SOURCES = pfs_instr_class-t.cc +pfs_instr_class_t_LDADD = $(TEST_CODE) $(PROD_CODE) + +pfs_instr_class_oom_t_SOURCES = pfs_instr_class-oom-t.cc stub_pfs_global.h +pfs_instr_class_oom_t_LDADD = $(TEST_CODE) $(PROD_CODE) + +pfs_instr_t_SOURCES = pfs_instr-t.cc +pfs_instr_t_LDADD = $(TEST_CODE) $(PROD_CODE) + +pfs_instr_oom_t_SOURCES = pfs_instr-oom-t.cc stub_pfs_global.h +pfs_instr_oom_t_LDADD = $(TEST_CODE) $(PROD_CODE) + +pfs_timer_t_SOURCES = pfs_timer-t.cc +pfs_timer_t_LDADD = $(TEST_CODE) $(PROD_CODE) + +EXTRA_DIST = conf.txt CMakeLists.txt + diff --git a/storage/perfschema/unittest/conf.txt b/storage/perfschema/unittest/conf.txt new file mode 100644 index 00000000000..a3bee9c3be5 --- /dev/null +++ b/storage/perfschema/unittest/conf.txt @@ -0,0 +1,420 @@ +# Copyright (C) 2009 Sun Microsystems, Inc +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 00000000000..fcbaf4aeb8f --- /dev/null +++ b/storage/perfschema/unittest/pfs-t.cc @@ -0,0 +1,1201 @@ +/* Copyright (C) 2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <mysql_priv.h> +#include <pfs_server.h> +#include <pfs_instr_class.h> +#include <pfs_instr.h> +#include <pfs_global.h> +#include <tap.h> + +#include "stub_print_error.h" + +/* 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) +{ + uint i; + PFS_file *pfs; + uint len= strlen(name); + + for (i= 0; i < file_max; i++) + { + pfs= & file_array[i]; + if (pfs->m_lock.is_populated()) + { + if ((len == pfs->m_filename_length) && + (strncmp(name, pfs->m_filename, pfs->m_filename_length) == 0)) + return pfs; + } + } + + return NULL; +} + +/* tests */ + +void test_bootstrap() +{ + void *psi; + void *psi_2; + PSI_bootstrap *boot; + PFS_global_param param; + + diag("test_bootstrap"); + + 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_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_events_waits_history_sizing= 0; + param.m_events_waits_history_long_sizing= 0; + + 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"); + + shutdown_performance_schema(); +} + +/* + Not a test, helper for testing pfs.cc +*/ +PSI * load_perfschema() +{ + void *psi; + PSI_bootstrap *boot; + PFS_global_param 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_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_events_waits_history_sizing= 10; + param.m_events_waits_history_long_sizing= 10; + + /* test_bootstrap() covered this, assuming it just works */ + boot= initialize_performance_schema(& param); + psi= boot->get_interface(PSI_VERSION_1); + + return (PSI*) psi; +} + +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_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"); + + /* + 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"); + + shutdown_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_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_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; + PSI_mutex *mutex_A1; + PSI_rwlock *rwlock_A1; + PSI_cond *cond_A1; + PFS_file *file_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"); + + /* Pretend thread T-1 is running, and disabled */ + /* ------------------------------------------- */ + + psi->set_thread(thread_1); + setup_thread(thread_1, false); + + /* disabled M-A + disabled T-1: no instrumentation */ + + mutex_class_A->m_enabled= false; + mutex_A1= psi->init_mutex(mutex_key_A, NULL); + ok(mutex_A1 == NULL, "not instrumented"); + + /* enabled M-A + disabled T-1: no instrumentation */ + + mutex_class_A->m_enabled= true; + mutex_A1= psi->init_mutex(mutex_key_A, NULL); + ok(mutex_A1 == NULL, "not 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, "not instrumented"); + mutex_A1= psi->init_mutex(99, NULL); + ok(mutex_A1 == NULL, "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, "not instrumented"); + + /* enabled RW-A + disabled T-1: no instrumentation */ + + rwlock_class_A->m_enabled= true; + rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL); + ok(rwlock_A1 == NULL, "not 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, "not instrumented"); + rwlock_A1= psi->init_rwlock(99, NULL); + ok(rwlock_A1 == NULL, "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, "not instrumented"); + + /* enabled C-A + disabled T-1: no instrumentation */ + + cond_class_A->m_enabled= true; + cond_A1= psi->init_cond(cond_key_A, NULL); + ok(cond_A1 == NULL, "not 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, "not instrumented"); + cond_A1= psi->init_cond(99, NULL); + ok(cond_A1 == NULL, "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, "not instrumented"); + psi->create_file(99, "foo", (File) 12); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "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, "not 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, "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, "not instrumented"); + mutex_A1= psi->init_mutex(99, NULL); + ok(mutex_A1 == NULL, "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, "not 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, "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, "not instrumented"); + rwlock_A1= psi->init_rwlock(99, NULL); + ok(rwlock_A1 == NULL, "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, "not 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, "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, "not instrumented"); + cond_A1= psi->init_cond(99, NULL); + ok(cond_A1 == NULL, "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, "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, "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, "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, "instrumented"); + + /* 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, "not instrumented"); + psi->create_file(99, "foo", (File) 12); + file_A1= lookup_file_by_name("foo"); + ok(file_A1 == NULL, "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, "not instrumented"); + + /* enabled M-A + unknown thread: no instrumentation */ + + mutex_class_A->m_enabled= true; + mutex_A1= psi->init_mutex(mutex_key_A, NULL); + ok(mutex_A1 == NULL, "not instrumented"); + + /* broken key + unknown thread: no instrumentation */ + + mutex_class_A->m_enabled= true; + mutex_A1= psi->init_mutex(0, NULL); + ok(mutex_A1 == NULL, "not instrumented"); + mutex_A1= psi->init_mutex(99, NULL); + ok(mutex_A1 == NULL, "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, "not instrumented"); + + /* enabled RW-A + unknown thread: no instrumentation */ + + rwlock_class_A->m_enabled= true; + rwlock_A1= psi->init_rwlock(rwlock_key_A, NULL); + ok(rwlock_A1 == NULL, "not instrumented"); + + /* broken key + unknown thread: no instrumentation */ + + rwlock_class_A->m_enabled= true; + rwlock_A1= psi->init_rwlock(0, NULL); + ok(rwlock_A1 == NULL, "not instrumented"); + rwlock_A1= psi->init_rwlock(99, NULL); + ok(rwlock_A1 == NULL, "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, "not instrumented"); + + /* enabled C-A + unknown thread: no instrumentation */ + + cond_class_A->m_enabled= true; + cond_A1= psi->init_cond(cond_key_A, NULL); + ok(cond_A1 == NULL, "not instrumented"); + + /* broken key + unknown thread: no instrumentation */ + + cond_class_A->m_enabled= true; + cond_A1= psi->init_cond(0, NULL); + ok(cond_A1 == NULL, "not instrumented"); + cond_A1= psi->init_cond(99, NULL); + ok(cond_A1 == NULL, "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, "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, "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"); + + shutdown_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_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_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; + PSI_mutex *mutex_A1; + PSI_rwlock *rwlock_A1; + PSI_cond *cond_A1; + PSI_file *file_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"); + + /* 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"); + + PSI_mutex_locker *mutex_locker; + PSI_rwlock_locker *rwlock_locker; + PSI_cond_locker *cond_locker; + PSI_file_locker *file_locker; + + /* 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; + + mutex_locker= psi->get_thread_mutex_locker(mutex_A1, PSI_MUTEX_LOCK); + ok(mutex_locker == NULL, "no locker"); + rwlock_locker= psi->get_thread_rwlock_locker(rwlock_A1, PSI_RWLOCK_READLOCK); + ok(rwlock_locker == NULL, "no locker"); + cond_locker= psi->get_thread_cond_locker(cond_A1, mutex_A1, PSI_COND_WAIT); + ok(cond_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_name_locker(file_key_A, PSI_FILE_OPEN, "xxx", NULL); + ok(file_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_stream_locker(file_A1, PSI_FILE_READ); + ok(file_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_descriptor_locker((File) 12, PSI_FILE_READ); + ok(file_locker == NULL, "no locker"); + + /* Pretend the consumer is disabled */ + /* -------------------------------- */ + + setup_thread(thread_1, true); + flag_events_waits_current= 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; + + mutex_locker= psi->get_thread_mutex_locker(mutex_A1, PSI_MUTEX_LOCK); + ok(mutex_locker == NULL, "no locker"); + rwlock_locker= psi->get_thread_rwlock_locker(rwlock_A1, PSI_RWLOCK_READLOCK); + ok(rwlock_locker == NULL, "no locker"); + cond_locker= psi->get_thread_cond_locker(cond_A1, mutex_A1, PSI_COND_WAIT); + ok(cond_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_name_locker(file_key_A, PSI_FILE_OPEN, "xxx", NULL); + ok(file_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_stream_locker(file_A1, PSI_FILE_READ); + ok(file_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_descriptor_locker((File) 12, PSI_FILE_READ); + ok(file_locker == NULL, "no locker"); + + /* Pretend the instrument is disabled */ + /* ---------------------------------- */ + + setup_thread(thread_1, 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; + + mutex_locker= psi->get_thread_mutex_locker(mutex_A1, PSI_MUTEX_LOCK); + ok(mutex_locker == NULL, "no locker"); + rwlock_locker= psi->get_thread_rwlock_locker(rwlock_A1, PSI_RWLOCK_READLOCK); + ok(rwlock_locker == NULL, "no locker"); + cond_locker= psi->get_thread_cond_locker(cond_A1, mutex_A1, PSI_COND_WAIT); + ok(cond_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_name_locker(file_key_A, PSI_FILE_OPEN, "xxx", NULL); + ok(file_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_stream_locker(file_A1, PSI_FILE_READ); + ok(file_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_descriptor_locker((File) 12, PSI_FILE_READ); + ok(file_locker == NULL, "no locker"); + + /* Pretend everything is enabled */ + /* ----------------------------- */ + + setup_thread(thread_1, true); + 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; + + mutex_locker= psi->get_thread_mutex_locker(mutex_A1, PSI_MUTEX_LOCK); + ok(mutex_locker != NULL, "locker"); + psi->start_mutex_wait(mutex_locker, __FILE__, __LINE__); + psi->end_mutex_wait(mutex_locker, 0); + rwlock_locker= psi->get_thread_rwlock_locker(rwlock_A1, PSI_RWLOCK_READLOCK); + ok(rwlock_locker != NULL, "locker"); + psi->start_rwlock_rdwait(rwlock_locker, __FILE__, __LINE__); + psi->end_rwlock_rdwait(rwlock_locker, 0); + cond_locker= psi->get_thread_cond_locker(cond_A1, mutex_A1, PSI_COND_WAIT); + ok(cond_locker != NULL, "locker"); + psi->start_cond_wait(cond_locker, __FILE__, __LINE__); + psi->end_cond_wait(cond_locker, 0); + file_locker= psi->get_thread_file_name_locker(file_key_A, PSI_FILE_OPEN, "xxx", NULL); + ok(file_locker != NULL, "locker"); + psi->start_file_open_wait(file_locker, __FILE__, __LINE__); + psi->end_file_open_wait(file_locker); + file_locker= psi->get_thread_file_stream_locker(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) 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); + + /* 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; + + mutex_locker= psi->get_thread_mutex_locker(mutex_A1, PSI_MUTEX_LOCK); + ok(mutex_locker == NULL, "no locker"); + rwlock_locker= psi->get_thread_rwlock_locker(rwlock_A1, PSI_RWLOCK_READLOCK); + ok(rwlock_locker == NULL, "no locker"); + cond_locker= psi->get_thread_cond_locker(cond_A1, mutex_A1, PSI_COND_WAIT); + ok(cond_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_name_locker(file_key_A, PSI_FILE_OPEN, "xxx", NULL); + ok(file_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_stream_locker(file_A1, PSI_FILE_READ); + ok(file_locker == NULL, "no locker"); + file_locker= psi->get_thread_file_descriptor_locker((File) 12, PSI_FILE_READ); + ok(file_locker == NULL, "no locker"); + + shutdown_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_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_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) 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) 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) 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) 12, PSI_FILE_WRITE); + ok(file_locker == NULL, "no locker, no leak"); + + shutdown_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} + }; + + shutdown_performance_schema(); +#endif +} + +void do_all_tests() +{ + test_bootstrap(); + test_bad_registration(); + test_init_disabled(); + test_locker_disabled(); + test_file_instrumentation_leak(); +} + +int main(int, char **) +{ + plan(153); + MY_INIT("pfs-t"); + do_all_tests(); + return 0; +} + 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 00000000000..d7810eedb04 --- /dev/null +++ b/storage/perfschema/unittest/pfs_instr-oom-t.cc @@ -0,0 +1,210 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <mysql_priv.h> +#include <pfs_instr.h> +#include <pfs_stat.h> +#include <pfs_global.h> +#include <tap.h> + +#include "stub_pfs_global.h" + +void test_oom() +{ + int rc; + PFS_global_param param; + + stub_alloc_always_fails= true; + + param.m_enabled= true; + param.m_mutex_class_sizing= 10; + 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_mutex_sizing= 1000; + 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_events_waits_history_sizing= 0; + param.m_events_waits_history_long_sizing= 0; + + rc= init_instruments(& param); + ok(rc == 1, "oom (mutex)"); + + param.m_enabled= true; + param.m_mutex_class_sizing= 0; + param.m_rwlock_class_sizing= 10; + 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_mutex_sizing= 0; + param.m_rwlock_sizing= 1000; + 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_events_waits_history_sizing= 0; + param.m_events_waits_history_long_sizing= 0; + + rc= init_instruments(& param); + ok(rc == 1, "oom (rwlock)"); + + param.m_enabled= true; + param.m_mutex_class_sizing= 0; + param.m_rwlock_class_sizing= 0; + param.m_cond_class_sizing= 10; + param.m_thread_class_sizing= 0; + param.m_table_share_sizing= 0; + param.m_file_class_sizing= 0; + param.m_mutex_sizing= 0; + param.m_rwlock_sizing= 0; + param.m_cond_sizing= 1000; + param.m_thread_sizing= 0; + param.m_table_sizing= 0; + param.m_file_sizing= 0; + param.m_file_handle_sizing= 0; + param.m_events_waits_history_sizing= 0; + param.m_events_waits_history_long_sizing= 0; + + rc= init_instruments(& param); + ok(rc == 1, "oom (cond)"); + + 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= 10; + 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= 1000; + param.m_file_handle_sizing= 1000; + param.m_events_waits_history_sizing= 0; + param.m_events_waits_history_long_sizing= 0; + + rc= init_instruments(& param); + ok(rc == 1, "oom (file)"); + + 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= 10; + param.m_file_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= 1000; + param.m_file_sizing= 0; + param.m_file_handle_sizing= 0; + param.m_events_waits_history_sizing= 0; + param.m_events_waits_history_long_sizing= 0; + + rc= init_instruments(& param); + ok(rc == 1, "oom (table)"); + + 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_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_events_waits_history_sizing= 0; + param.m_events_waits_history_long_sizing= 0; + + rc= init_instruments(& param); + ok(rc == 1, "oom (thread)"); + + stub_alloc_always_fails= false; + + 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_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_events_waits_history_sizing= 10; + param.m_events_waits_history_long_sizing= 0; + + stub_alloc_fails_after_count= 2; + rc= init_instruments(& param); + ok(rc == 1, "oom (thread history sizing)"); + + param.m_enabled= true; + param.m_mutex_class_sizing= 50; + param.m_rwlock_class_sizing= 50; + param.m_cond_class_sizing= 50; + param.m_thread_class_sizing= 10; + param.m_table_share_sizing= 0; + param.m_file_class_sizing= 50; + 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_events_waits_history_sizing= 0; + param.m_events_waits_history_long_sizing= 0; + + stub_alloc_fails_after_count= 2; + rc= init_instruments(& param); + ok(rc == 1, "oom (per thread wait)"); + + cleanup_instruments(); +} + +void do_all_tests() +{ + test_oom(); +} + +int main(int, char **) +{ + plan(8); + MY_INIT("pfs_instr-oom-t"); + do_all_tests(); + return 0; +} + diff --git a/storage/perfschema/unittest/pfs_instr-t.cc b/storage/perfschema/unittest/pfs_instr-t.cc new file mode 100644 index 00000000000..f85de601579 --- /dev/null +++ b/storage/perfschema/unittest/pfs_instr-t.cc @@ -0,0 +1,411 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <mysql_priv.h> +#include <pfs_instr.h> +#include <pfs_stat.h> +#include <pfs_global.h> +#include <tap.h> + +void test_no_instruments() +{ + int rc; + PFS_global_param 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_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_events_waits_history_sizing= 0; + param.m_events_waits_history_long_sizing= 0; + + 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_mutex *mutex; + PFS_rwlock *rwlock; + PFS_cond *cond; + PFS_thread *thread; + PFS_file *file; + PFS_table *table; + PFS_global_param 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_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_events_waits_history_sizing= 0; + param.m_events_waits_history_long_sizing= 0; + + rc= init_instruments(& param); + ok(rc == 0, "no instances init"); + + mutex= create_mutex(& dummy_mutex_class, NULL); + ok(mutex == NULL, "no mutex"); + ok(mutex_lost == 1, "lost 1"); + mutex= create_mutex(& dummy_mutex_class, NULL); + ok(mutex == NULL, "no mutex"); + ok(mutex_lost == 2, "lost 2"); + + rwlock= create_rwlock(& dummy_rwlock_class, NULL); + ok(rwlock == NULL, "no rwlock"); + ok(rwlock_lost == 1, "lost 1"); + rwlock= create_rwlock(& dummy_rwlock_class, NULL); + ok(rwlock == NULL, "no rwlock"); + ok(rwlock_lost == 2, "lost 2"); + + cond= create_cond(& dummy_cond_class, NULL); + ok(cond == NULL, "no cond"); + ok(cond_lost == 1, "lost 1"); + cond= create_cond(& dummy_cond_class, NULL); + ok(cond == NULL, "no cond"); + ok(cond_lost == 2, "lost 2"); + + thread= create_thread(& dummy_thread_class, NULL, 0); + ok(thread == NULL, "no thread"); + ok(thread_lost == 1, "lost 1"); + thread= create_thread(& dummy_thread_class, NULL, 0); + ok(thread == NULL, "no thread"); + ok(thread_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); + ok(file == NULL, "no file"); + ok(file_lost == 1, "lost 1"); + file= find_or_create_file(& fake_thread, & dummy_file_class, "dummy", 5); + ok(file == NULL, "no file"); + ok(file_lost == 2, "lost 2"); + + init_file_hash(); + + file= find_or_create_file(& fake_thread, & dummy_file_class, "dummy", 5); + ok(file == NULL, "no file"); + ok(file_lost == 3, "lost 3"); + file= find_or_create_file(& fake_thread, & dummy_file_class, "dummy", 5); + ok(file == NULL, "no file"); + ok(file_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); + ok(file == NULL, "no file"); + ok(file_lost == 5, "lost 5"); + + table= create_table(& dummy_table_share, NULL); + ok(table == NULL, "no table"); + ok(table_lost == 1, "lost 1"); + table= create_table(& dummy_table_share, NULL); + ok(table == NULL, "no table"); + ok(table_lost == 2, "lost 2"); + + /* No result to test, just make sure it does not crash */ + reset_events_waits_by_instance(); + reset_per_thread_wait_stat(); + + 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_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_table *table_1; + PFS_table *table_2; + PFS_global_param 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_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_events_waits_history_sizing= 10; + param.m_events_waits_history_long_sizing= 10000; + + rc= init_instruments(& param); + ok(rc == 0, "instances init"); + + mutex_1= create_mutex(& dummy_mutex_class, NULL); + ok(mutex_1 != NULL, "mutex"); + ok(mutex_lost == 0, "not lost"); + mutex_2= create_mutex(& dummy_mutex_class, NULL); + ok(mutex_2 != NULL, "mutex"); + ok(mutex_lost == 0, "not lost"); + mutex_2= create_mutex(& dummy_mutex_class, NULL); + ok(mutex_2 == NULL, "no mutex"); + ok(mutex_lost == 1, "lost 1"); + destroy_mutex(mutex_1); + mutex_2= create_mutex(& dummy_mutex_class, NULL); + ok(mutex_2 != NULL, "mutex"); + ok(mutex_lost == 1, "no new loss"); + + rwlock_1= create_rwlock(& dummy_rwlock_class, NULL); + ok(rwlock_1 != NULL, "rwlock"); + ok(rwlock_lost == 0, "not lost"); + rwlock_2= create_rwlock(& dummy_rwlock_class, NULL); + ok(rwlock_2 != NULL, "rwlock"); + ok(rwlock_lost == 0, "not lost"); + rwlock_2= create_rwlock(& dummy_rwlock_class, NULL); + ok(rwlock_2 == NULL, "no rwlock"); + ok(rwlock_lost == 1, "lost 1"); + destroy_rwlock(rwlock_1); + rwlock_2= create_rwlock(& dummy_rwlock_class, NULL); + ok(rwlock_2 != NULL, "rwlock"); + ok(rwlock_lost == 1, "no new loss"); + + cond_1= create_cond(& dummy_cond_class, NULL); + ok(cond_1 != NULL, "cond"); + ok(cond_lost == 0, "not lost"); + cond_2= create_cond(& dummy_cond_class, NULL); + ok(cond_2 != NULL, "cond"); + ok(cond_lost == 0, "not lost"); + cond_2= create_cond(& dummy_cond_class, NULL); + ok(cond_2 == NULL, "no cond"); + ok(cond_lost == 1, "lost 1"); + destroy_cond(cond_1); + cond_2= create_cond(& dummy_cond_class, NULL); + ok(cond_2 != NULL, "cond"); + ok(cond_lost == 1, "no new loss"); + + thread_1= create_thread(& dummy_thread_class, NULL, 0); + ok(thread_1 != NULL, "thread"); + ok(thread_lost == 0, "not lost"); + thread_2= create_thread(& dummy_thread_class, NULL, 0); + ok(thread_2 != NULL, "thread"); + ok(thread_lost == 0, "not lost"); + thread_2= create_thread(& dummy_thread_class, NULL, 0); + ok(thread_2 == NULL, "no thread"); + ok(thread_lost == 1, "lost 1"); + destroy_thread(thread_1); + thread_2= create_thread(& dummy_thread_class, NULL, 0); + ok(thread_2 != NULL, "thread"); + ok(thread_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); + ok(file_1 == NULL, "no file"); + ok(file_lost == 1, "lost 1"); + file_1= find_or_create_file(& fake_thread, & dummy_file_class, "dummy", 5); + ok(file_1 == NULL, "no file"); + ok(file_lost == 2, "lost 2"); + + init_file_hash(); + file_lost= 0; + + file_1= find_or_create_file(& fake_thread, & dummy_file_class, "dummy_A", 7); + ok(file_1 != NULL, "file"); + ok(file_1->m_file_stat.m_open_count == 1, "open count 1"); + ok(file_lost == 0, "not lost"); + file_2= find_or_create_file(& fake_thread, & dummy_file_class, "dummy_A", 7); + ok(file_1 == file_2, "same file"); + ok(file_1->m_file_stat.m_open_count == 2, "open count 2"); + ok(file_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); + ok(file_2 != NULL, "file"); + ok(file_lost == 0, "not lost"); + file_2= find_or_create_file(& fake_thread, & dummy_file_class, "dummy_C", 7); + ok(file_2 == NULL, "no file"); + ok(file_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); + ok(file_2 == NULL, "no file"); + ok(file_lost == 2, "lost"); + + table_1= create_table(& dummy_table_share, NULL); + ok(table_1 != NULL, "table"); + ok(table_lost == 0, "not lost"); + table_2= create_table(& dummy_table_share, NULL); + ok(table_2 != NULL, "table"); + ok(table_lost == 0, "not lost"); + table_2= create_table(& dummy_table_share, NULL); + ok(table_2 == NULL, "no table"); + ok(table_lost == 1, "lost 1"); + destroy_table(table_1); + table_2= create_table(& dummy_table_share, NULL); + ok(table_2 != NULL, "table"); + ok(table_lost == 1, "no new loss"); + + //TODO: test that cleanup works + reset_events_waits_by_instance(); + reset_per_thread_wait_stat(); + + cleanup_file_hash(); + cleanup_instruments(); +} + +void test_per_thread_wait() +{ + 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_thread *thread; + PFS_single_stat_chain *base; + PFS_single_stat_chain *stat; + PFS_global_param param; + + + /* Per mutex info waits should be at [0..9] */ + mutex_class_max= 10; + /* Per rwlock info waits should be at [10..29] */ + rwlock_class_max= 20; + /* Per cond info waits should be at [30..69] */ + cond_class_max= 40; + /* Per file info waits should be at [70..149] */ + file_class_max= 80; + /* Per table info waits should be at [150..309] */ + table_share_max= 160; + + param.m_enabled= true; + param.m_mutex_class_sizing= mutex_class_max; + param.m_rwlock_class_sizing= rwlock_class_max; + param.m_cond_class_sizing= cond_class_max; + param.m_thread_class_sizing= 2; + param.m_table_share_sizing= table_share_max; + param.m_file_class_sizing= file_class_max; + param.m_mutex_sizing= 0; + param.m_rwlock_sizing= 0; + param.m_cond_sizing= 0; + param.m_thread_sizing= 2; + param.m_table_sizing= 0; + param.m_file_sizing= 0; + param.m_file_handle_sizing= 0; + param.m_events_waits_history_sizing= 10; + param.m_events_waits_history_long_sizing= 10000; + + rc= init_instruments(& param); + ok(rc == 0, "instances init"); + + thread= create_thread(& dummy_thread_class, NULL, 0); + ok(thread != NULL, "thread"); + ok(thread_lost == 0, "not lost"); + + base= & thread->m_instr_class_wait_stats[0]; + + dummy_mutex_class.m_index= 0; + stat= find_per_thread_mutex_class_wait_stat(thread, & dummy_mutex_class); + ok(base + 0 == stat, "fist mutex info slot at 0"); + dummy_mutex_class.m_index= mutex_class_max - 1; + stat= find_per_thread_mutex_class_wait_stat(thread, & dummy_mutex_class); + ok(base + 9 == stat, "last mutex info slot at 9"); + + dummy_rwlock_class.m_index= 0; + stat= find_per_thread_rwlock_class_wait_stat(thread, & dummy_rwlock_class); + ok(base + 10 == stat, "fist rwlock info slot at 10"); + dummy_rwlock_class.m_index= rwlock_class_max - 1; + stat= find_per_thread_rwlock_class_wait_stat(thread, & dummy_rwlock_class); + ok(base + 29 == stat, "last rwlock info slot at 29"); + + dummy_cond_class.m_index= 0; + stat= find_per_thread_cond_class_wait_stat(thread, & dummy_cond_class); + ok(base + 30 == stat, "fist cond info slot at 30"); + dummy_cond_class.m_index= cond_class_max - 1; + stat= find_per_thread_cond_class_wait_stat(thread, & dummy_cond_class); + ok(base + 69 == stat, "last cond info slot at 69"); + + dummy_file_class.m_index= 0; + stat= find_per_thread_file_class_wait_stat(thread, & dummy_file_class); + ok(base + 70 == stat, "fist file info slot at 70"); + dummy_file_class.m_index= file_class_max - 1; + stat= find_per_thread_file_class_wait_stat(thread, & dummy_file_class); + ok(base + 149 == stat, "last file info slot at 149"); + + cleanup_instruments(); +} + +void do_all_tests() +{ + test_no_instruments(); + test_no_instances(); + test_with_instances(); + test_per_thread_wait(); +} + +int main(int, char **) +{ + plan(102); + MY_INIT("pfs_instr-t"); + do_all_tests(); + return 0; +} + 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 00000000000..53100571323 --- /dev/null +++ b/storage/perfschema/unittest/pfs_instr_class-oom-t.cc @@ -0,0 +1,58 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <mysql_priv.h> +#include <pfs_instr_class.h> +#include <pfs_global.h> +#include <tap.h> + +#include "stub_pfs_global.h" + +void test_oom() +{ + int rc; + + 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_table_share(1000); + ok(rc == 1, "oom (cond)"); + + cleanup_sync_class(); + cleanup_thread_class(); + cleanup_file_class(); + cleanup_table_share(); +} + +void do_all_tests() +{ + test_oom(); +} + +int main(int, char **) +{ + plan(6); + MY_INIT("pfs_instr_info-oom-t"); + do_all_tests(); + return 0; +} + 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 00000000000..0a1e0c2d0b1 --- /dev/null +++ b/storage/perfschema/unittest/pfs_instr_class-t.cc @@ -0,0 +1,570 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <mysql_priv.h> +#include <pfs_instr_class.h> +#include <pfs_instr.h> +#include <pfs_global.h> +#include <tap.h> + +void test_no_registration() +{ + int rc; + PFS_sync_key key; + PFS_thread_key thread_key; + PFS_file_key file_key; + PFS_mutex_class *mutex; + PFS_rwlock_class *rwlock; + PFS_cond_class *cond; + PFS_thread_class *thread; + PFS_file_class *file; + 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_table_share(0); + ok(rc == 0, "zero init (table)"); + + 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"); + + PFS_thread fake_thread; + fake_thread.m_table_share_hash_pins= NULL; + + table= find_or_create_table_share(& fake_thread, "foo_db", 6, "foo_table", 9); + ok(table == NULL, "not created"); + table= find_or_create_table_share(& fake_thread, "bar_db", 6, "bar_table", 9); + ok(table == NULL, "not created"); + table= find_or_create_table_share(& fake_thread, "foo_db", 6, "foo_table", 9); + ok(table == NULL, "not created"); + + 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"); + + cleanup_sync_class(); + cleanup_thread_class(); + cleanup_file_class(); + cleanup_table_share(); +} + +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_table_registration() +{ + 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, "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, "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, "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, "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, "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, "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, "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, "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, "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(); +} + +void set_wait_stat(PFS_single_stat_chain *stat) +{ + stat->m_count= 12; + stat->m_min= 5; + stat->m_max= 120; + stat->m_sum= 999; +} + +bool is_empty_stat(PFS_single_stat_chain *stat) +{ + 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; +} + +void test_instruments_reset() +{ + int rc; + PFS_sync_key key; + PFS_file_key file_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; + + 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)"); + + 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"); + + 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"); + + set_wait_stat(& mutex_1->m_wait_stat); + set_wait_stat(& mutex_2->m_wait_stat); + set_wait_stat(& mutex_3->m_wait_stat); + set_wait_stat(& rwlock_1->m_wait_stat); + set_wait_stat(& rwlock_2->m_wait_stat); + set_wait_stat(& rwlock_3->m_wait_stat); + set_wait_stat(& cond_1->m_wait_stat); + set_wait_stat(& cond_2->m_wait_stat); + set_wait_stat(& cond_3->m_wait_stat); + set_wait_stat(& file_1->m_wait_stat); + set_wait_stat(& file_2->m_wait_stat); + set_wait_stat(& file_3->m_wait_stat); + + ok(! is_empty_stat(& mutex_1->m_wait_stat), "mutex_1 stat is populated"); + ok(! is_empty_stat(& mutex_2->m_wait_stat), "mutex_2 stat is populated"); + ok(! is_empty_stat(& mutex_3->m_wait_stat), "mutex_3 stat is populated"); + ok(! is_empty_stat(& rwlock_1->m_wait_stat), "rwlock_1 stat is populated"); + ok(! is_empty_stat(& rwlock_2->m_wait_stat), "rwlock_2 stat is populated"); + ok(! is_empty_stat(& rwlock_3->m_wait_stat), "rwlock_3 stat is populated"); + ok(! is_empty_stat(& cond_1->m_wait_stat), "cond_1 stat is populated"); + ok(! is_empty_stat(& cond_2->m_wait_stat), "cond_2 stat is populated"); + ok(! is_empty_stat(& cond_3->m_wait_stat), "cond_3 stat is populated"); + ok(! is_empty_stat(& file_1->m_wait_stat), "file_1 stat is populated"); + ok(! is_empty_stat(& file_2->m_wait_stat), "file_2 stat is populated"); + ok(! is_empty_stat(& file_3->m_wait_stat), "file_3 stat is populated"); + + reset_instrument_class_waits(); + + ok(is_empty_stat(& mutex_1->m_wait_stat), "mutex_1 stat is cleared"); + ok(is_empty_stat(& mutex_2->m_wait_stat), "mutex_2 stat is cleared"); + ok(is_empty_stat(& mutex_3->m_wait_stat), "mutex_3 stat is cleared"); + ok(is_empty_stat(& rwlock_1->m_wait_stat), "rwlock_1 stat is cleared"); + ok(is_empty_stat(& rwlock_2->m_wait_stat), "rwlock_2 stat is cleared"); + ok(is_empty_stat(& rwlock_3->m_wait_stat), "rwlock_3 stat is cleared"); + ok(is_empty_stat(& cond_1->m_wait_stat), "cond_1 stat is cleared"); + ok(is_empty_stat(& cond_2->m_wait_stat), "cond_2 stat is cleared"); + ok(is_empty_stat(& cond_3->m_wait_stat), "cond_3 stat is cleared"); + ok(is_empty_stat(& file_1->m_wait_stat), "file_1 stat is cleared"); + ok(is_empty_stat(& file_2->m_wait_stat), "file_2 stat is cleared"); + ok(is_empty_stat(& file_3->m_wait_stat), "file_3 stat is cleared"); + + cleanup_sync_class(); + cleanup_file_class(); +} + +void do_all_tests() +{ + test_no_registration(); + test_mutex_registration(); + test_rwlock_registration(); + test_cond_registration(); + test_thread_registration(); + test_file_registration(); + test_table_registration(); + test_instruments_reset(); +} + +int main(int, char **) +{ + plan(196); + MY_INIT("pfs_instr_info-t"); + do_all_tests(); + return 0; +} + diff --git a/storage/perfschema/unittest/pfs_timer-t.cc b/storage/perfschema/unittest/pfs_timer-t.cc new file mode 100644 index 00000000000..8fa29306982 --- /dev/null +++ b/storage/perfschema/unittest/pfs_timer-t.cc @@ -0,0 +1,116 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <mysql_priv.h> +#include <pfs_timer.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; + + init_timers(); + + t1_a= get_timer_value(TIMER_NAME_CYCLE); + /* Wait 5 seconds */ + my_sleep(5000000); + t1_b= get_timer_value(TIMER_NAME_CYCLE); + + t2_a= get_timer_value(TIMER_NAME_NANOSEC); + my_sleep(5000000); + t2_b= get_timer_value(TIMER_NAME_NANOSEC); + + t3_a= get_timer_value(TIMER_NAME_MICROSEC); + my_sleep(5000000); + t3_b= get_timer_value(TIMER_NAME_MICROSEC); + + t4_a= get_timer_value(TIMER_NAME_MILLISEC); + my_sleep(5000000); + t4_b= get_timer_value(TIMER_NAME_MILLISEC); + + t5_a= get_timer_value(TIMER_NAME_TICK); + my_sleep(5000000); + t5_b= get_timer_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(); + return 0; +} + diff --git a/storage/perfschema/unittest/stub_pfs_global.h b/storage/perfschema/unittest/stub_pfs_global.h new file mode 100644 index 00000000000..85088061d3f --- /dev/null +++ b/storage/perfschema/unittest/stub_pfs_global.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> +#include <my_sys.h> +#include <pfs_global.h> + +bool pfs_initialized= false; + +bool stub_alloc_always_fails= true; +int stub_alloc_fails_after_count= 0; + +void *pfs_malloc(size_t, myf) +{ + static char garbage[100]; + + if (stub_alloc_always_fails) + return NULL; + + if (--stub_alloc_fails_after_count <= 0) + return NULL; + + return garbage; +} + +void pfs_free(void *) +{ +} + diff --git a/storage/perfschema/unittest/stub_print_error.h b/storage/perfschema/unittest/stub_print_error.h new file mode 100644 index 00000000000..12dabb46ceb --- /dev/null +++ b/storage/perfschema/unittest/stub_print_error.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2008-2009 Sun Microsystems, Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> +#include <my_sys.h> +#include <pfs_global.h> + +bool pfs_initialized= false; + +void *pfs_malloc(size_t size, myf flags) +{ + void *ptr= malloc(size); + if (ptr && (flags & MY_ZEROFILL)) + memset(ptr, 0, size); + return ptr; +} + +void pfs_free(void *ptr) +{ + if (ptr != NULL) + free(ptr); +} + +void pfs_print_error(const char *format, ...) +{ + /* Do not pollute the unit test output with annoying messages. */ +} + |