summaryrefslogtreecommitdiff
path: root/storage
diff options
context:
space:
mode:
authorVladislav Vaintroub <vvaintroub@mysql.com>2010-01-14 16:07:27 +0100
committerVladislav Vaintroub <vvaintroub@mysql.com>2010-01-14 16:07:27 +0100
commit6b6f0ca461f625267738ba646a8ff869fec93801 (patch)
tree79a9c5721e01d1c680979c0a50f5be8a965fa7cf /storage
parent905f48b6f367478be955e0f45de0996bda297ffc (diff)
parenta6165accb3279fe64b672fa0eeaa30ef12cf6557 (diff)
downloadmariadb-git-6b6f0ca461f625267738ba646a8ff869fec93801.tar.gz
merge
Diffstat (limited to 'storage')
-rw-r--r--storage/perfschema/CMakeLists.txt77
-rw-r--r--storage/perfschema/Makefile.am76
-rw-r--r--storage/perfschema/ha_perfschema.cc382
-rw-r--r--storage/perfschema/ha_perfschema.h159
-rw-r--r--storage/perfschema/pfs.cc2053
-rw-r--r--storage/perfschema/pfs.h34
-rw-r--r--storage/perfschema/pfs_atomic.cc78
-rw-r--r--storage/perfschema/pfs_atomic.h148
-rw-r--r--storage/perfschema/pfs_check.cc62
-rw-r--r--storage/perfschema/pfs_column_types.h119
-rw-r--r--storage/perfschema/pfs_column_values.cc42
-rw-r--r--storage/perfschema/pfs_column_values.h34
-rw-r--r--storage/perfschema/pfs_engine_table.cc717
-rw-r--r--storage/perfschema/pfs_engine_table.h336
-rw-r--r--storage/perfschema/pfs_events_waits.cc197
-rw-r--r--storage/perfschema/pfs_events_waits.h185
-rw-r--r--storage/perfschema/pfs_global.cc66
-rw-r--r--storage/perfschema/pfs_global.h59
-rw-r--r--storage/perfschema/pfs_instr.cc962
-rw-r--r--storage/perfschema/pfs_instr.h266
-rw-r--r--storage/perfschema/pfs_instr_class.cc878
-rw-r--r--storage/perfschema/pfs_instr_class.h251
-rw-r--r--storage/perfschema/pfs_lock.h173
-rw-r--r--storage/perfschema/pfs_server.cc131
-rw-r--r--storage/perfschema/pfs_server.h101
-rw-r--r--storage/perfschema/pfs_stat.h125
-rw-r--r--storage/perfschema/pfs_timer.cc119
-rw-r--r--storage/perfschema/pfs_timer.h34
-rw-r--r--storage/perfschema/plug.in26
-rw-r--r--storage/perfschema/table_all_instr.cc264
-rw-r--r--storage/perfschema/table_all_instr.h168
-rw-r--r--storage/perfschema/table_events_waits.cc773
-rw-r--r--storage/perfschema/table_events_waits.h248
-rw-r--r--storage/perfschema/table_events_waits_summary.cc696
-rw-r--r--storage/perfschema/table_events_waits_summary.h261
-rw-r--r--storage/perfschema/table_file_instances.cc180
-rw-r--r--storage/perfschema/table_file_instances.h90
-rw-r--r--storage/perfschema/table_file_summary.cc371
-rw-r--r--storage/perfschema/table_file_summary.h151
-rw-r--r--storage/perfschema/table_performance_timers.cc187
-rw-r--r--storage/perfschema/table_performance_timers.h86
-rw-r--r--storage/perfschema/table_processlist.cc176
-rw-r--r--storage/perfschema/table_processlist.h91
-rw-r--r--storage/perfschema/table_setup_consumers.cc211
-rw-r--r--storage/perfschema/table_setup_consumers.h85
-rw-r--r--storage/perfschema/table_setup_instruments.cc276
-rw-r--r--storage/perfschema/table_setup_instruments.h121
-rw-r--r--storage/perfschema/table_setup_objects.cc280
-rw-r--r--storage/perfschema/table_setup_objects.h125
-rw-r--r--storage/perfschema/table_setup_timers.cc183
-rw-r--r--storage/perfschema/table_setup_timers.h85
-rw-r--r--storage/perfschema/table_sync_instances.cc507
-rw-r--r--storage/perfschema/table_sync_instances.h206
-rw-r--r--storage/perfschema/unittest/CMakeLists.txt39
-rw-r--r--storage/perfschema/unittest/Makefile.am58
-rw-r--r--storage/perfschema/unittest/conf.txt420
-rw-r--r--storage/perfschema/unittest/pfs-t.cc1201
-rw-r--r--storage/perfschema/unittest/pfs_instr-oom-t.cc210
-rw-r--r--storage/perfschema/unittest/pfs_instr-t.cc411
-rw-r--r--storage/perfschema/unittest/pfs_instr_class-oom-t.cc58
-rw-r--r--storage/perfschema/unittest/pfs_instr_class-t.cc570
-rw-r--r--storage/perfschema/unittest/pfs_timer-t.cc116
-rw-r--r--storage/perfschema/unittest/stub_pfs_global.h41
-rw-r--r--storage/perfschema/unittest/stub_print_error.h40
64 files changed, 16875 insertions, 0 deletions
diff --git a/storage/perfschema/CMakeLists.txt b/storage/perfschema/CMakeLists.txt
new file mode 100644
index 00000000000..985673561fe
--- /dev/null
+++ b/storage/perfschema/CMakeLists.txt
@@ -0,0 +1,77 @@
+# 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}
+ ${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_ADD_PLUGIN(PERFSCHEMA ${PERFSCHEMA_SOURCES} STORAGE_ENGINE DEFAULT STATIC_ONLY)
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..4190094b52b
--- /dev/null
+++ b/storage/perfschema/pfs_engine_table.cc
@@ -0,0 +1,717 @@
+/* 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,
+ 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. */
+}
+