summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergei Golubchik <sergii@pisem.net>2012-03-28 19:26:00 +0200
committerSergei Golubchik <sergii@pisem.net>2012-03-28 19:26:00 +0200
commit0d5adca0de0a51b1f0bd49045fc4062eac7d1d25 (patch)
tree3cb7a294c5feebb813cf73b248c53d026aa11602
parent20e706689df1eb87c696304797e9d6184c0a75bb (diff)
downloadmariadb-git-0d5adca0de0a51b1f0bd49045fc4062eac7d1d25.tar.gz
debug_sync is now a service, available to dynamically loaded plugins.
new make target - abi_update libservices/HOWTO: remove references to Makefile.am small tweaks
-rw-r--r--cmake/abi_check.cmake11
-rw-r--r--cmake/do_abi_check.cmake8
-rw-r--r--include/my_sys.h10
-rw-r--r--include/mysql/plugin.h2
-rw-r--r--include/mysql/plugin_audit.h.pp2
-rw-r--r--include/mysql/plugin_auth.h.pp2
-rw-r--r--include/mysql/plugin_ftparser.h.pp2
-rw-r--r--include/mysql/service_debug_sync.h354
-rw-r--r--include/mysql/services.h2
-rw-r--r--include/service_versions.h1
-rw-r--r--libservices/CMakeLists.txt3
-rw-r--r--libservices/HOWTO35
-rw-r--r--libservices/debug_sync_service.c18
-rw-r--r--mysql-test/r/plugin.result6
-rw-r--r--mysys/my_static.c8
-rw-r--r--sql/debug_sync.cc355
-rw-r--r--sql/debug_sync.h14
-rw-r--r--sql/slave.cc6
-rw-r--r--sql/sql_parse.cc2
-rw-r--r--sql/sql_plugin.cc4
-rw-r--r--sql/sql_plugin_services.h1
-rw-r--r--sql/sql_repl.cc2
22 files changed, 439 insertions, 409 deletions
diff --git a/cmake/abi_check.cmake b/cmake/abi_check.cmake
index 9948f526b7a..cca595c5635 100644
--- a/cmake/abi_check.cmake
+++ b/cmake/abi_check.cmake
@@ -55,6 +55,17 @@ IF(CMAKE_COMPILER_IS_GNUCC AND RUN_ABI_CHECK)
VERBATIM
)
+ ADD_CUSTOM_TARGET(abi_update
+ COMMAND ${CMAKE_COMMAND}
+ -DCOMPILER=${COMPILER}
+ -DABI_UPDATE=1
+ -DSOURCE_DIR=${CMAKE_SOURCE_DIR}
+ -DBINARY_DIR=${CMAKE_BINARY_DIR}
+ "-DABI_HEADERS=${API_PREPROCESSOR_HEADER}"
+ -P ${CMAKE_SOURCE_DIR}/cmake/do_abi_check.cmake
+ VERBATIM
+ )
+
ADD_CUSTOM_TARGET(abi_check_all
COMMAND ${CMAKE_COMMAND}
-DCOMPILER=${COMPILER}
diff --git a/cmake/do_abi_check.cmake b/cmake/do_abi_check.cmake
index e42f11abde2..c831aaf8b52 100644
--- a/cmake/do_abi_check.cmake
+++ b/cmake/do_abi_check.cmake
@@ -75,8 +75,12 @@ FOREACH(file ${ABI_HEADERS})
EXECUTE_PROCESS(
COMMAND diff -w ${file}.pp ${abi_check_out} RESULT_VARIABLE result)
IF(NOT ${result} EQUAL 0)
- MESSAGE(FATAL_ERROR
- "ABI check found difference between ${file}.pp and ${abi_check_out}")
+ IF(ABI_UPDATE)
+ EXECUTE_PROCESS(COMMAND mv -v ${abi_check_out} ${file}.pp)
+ ELSE(ABI_UPDATE)
+ MESSAGE(FATAL_ERROR
+ "ABI check found difference between ${file}.pp and ${abi_check_out}")
+ ENDIF(ABI_UPDATE)
ENDIF()
FILE(REMOVE ${abi_check_out})
ENDFOREACH()
diff --git a/include/my_sys.h b/include/my_sys.h
index 3fcaea95f7b..a02c390fe4b 100644
--- a/include/my_sys.h
+++ b/include/my_sys.h
@@ -157,16 +157,6 @@ extern char *my_strndup(const char *from, size_t length, myf MyFlags);
extern int sf_leaking_memory; /* set to 1 to disable memleak detection */
-#if defined(ENABLED_DEBUG_SYNC)
-extern void (*debug_sync_C_callback_ptr)(const char *, size_t);
-#define DEBUG_SYNC_C(_sync_point_name_) do { \
- if (debug_sync_C_callback_ptr != NULL) \
- (*debug_sync_C_callback_ptr)(STRING_WITH_LEN(_sync_point_name_)); } \
- while(0)
-#else
-#define DEBUG_SYNC_C(_sync_point_name_)
-#endif /* defined(ENABLED_DEBUG_SYNC) */
-
#ifdef HAVE_LARGE_PAGES
extern uint my_get_large_page_size(void);
extern uchar * my_large_malloc(size_t size, myf my_flags);
diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h
index 8fb0ef77eef..b73f2d75193 100644
--- a/include/mysql/plugin.h
+++ b/include/mysql/plugin.h
@@ -72,7 +72,7 @@ typedef struct st_mysql_xid MYSQL_XID;
#define MYSQL_PLUGIN_INTERFACE_VERSION 0x0103
/* MariaDB plugin interface version */
-#define MARIA_PLUGIN_INTERFACE_VERSION 0x0102
+#define MARIA_PLUGIN_INTERFACE_VERSION 0x0103
/*
The allowable types of plugins
diff --git a/include/mysql/plugin_audit.h.pp b/include/mysql/plugin_audit.h.pp
index cdd9324a5f7..b987f690592 100644
--- a/include/mysql/plugin_audit.h.pp
+++ b/include/mysql/plugin_audit.h.pp
@@ -80,6 +80,8 @@ void thd_progress_next_stage(void* thd);
void thd_progress_end(void* thd);
const char *set_thd_proc_info(void*, const char * info, const char *func,
const char *file, unsigned int line);
+#include <mysql/service_debug_sync.h>
+extern void (*debug_sync_C_callback_ptr)(void*, const char *, size_t);
struct st_mysql_xid {
long formatID;
long gtrid_length;
diff --git a/include/mysql/plugin_auth.h.pp b/include/mysql/plugin_auth.h.pp
index e06494746dd..113aaf62d19 100644
--- a/include/mysql/plugin_auth.h.pp
+++ b/include/mysql/plugin_auth.h.pp
@@ -80,6 +80,8 @@ void thd_progress_next_stage(void* thd);
void thd_progress_end(void* thd);
const char *set_thd_proc_info(void*, const char * info, const char *func,
const char *file, unsigned int line);
+#include <mysql/service_debug_sync.h>
+extern void (*debug_sync_C_callback_ptr)(void*, const char *, size_t);
struct st_mysql_xid {
long formatID;
long gtrid_length;
diff --git a/include/mysql/plugin_ftparser.h.pp b/include/mysql/plugin_ftparser.h.pp
index a990c62e8e9..6011e7f7519 100644
--- a/include/mysql/plugin_ftparser.h.pp
+++ b/include/mysql/plugin_ftparser.h.pp
@@ -80,6 +80,8 @@ void thd_progress_next_stage(void* thd);
void thd_progress_end(void* thd);
const char *set_thd_proc_info(void*, const char * info, const char *func,
const char *file, unsigned int line);
+#include <mysql/service_debug_sync.h>
+extern void (*debug_sync_C_callback_ptr)(void*, const char *, size_t);
struct st_mysql_xid {
long formatID;
long gtrid_length;
diff --git a/include/mysql/service_debug_sync.h b/include/mysql/service_debug_sync.h
new file mode 100644
index 00000000000..f7bab99ac97
--- /dev/null
+++ b/include/mysql/service_debug_sync.h
@@ -0,0 +1,354 @@
+#ifndef MYSQL_SERVICE_DEBUG_SYNC_INCLUDED
+/* Copyright (c) 2009, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2012, Monty Program Ab
+
+ 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 */
+
+/**
+ @file
+ == Debug Sync Facility ==
+
+ The Debug Sync Facility allows placement of synchronization points in
+ the server code by using the DEBUG_SYNC macro:
+
+ open_tables(...)
+
+ DEBUG_SYNC(thd, "after_open_tables");
+
+ lock_tables(...)
+
+ When activated, a sync point can
+
+ - Emit a signal and/or
+ - Wait for a signal
+
+ Nomenclature:
+
+ - signal: A value of a global variable that persists
+ until overwritten by a new signal. The global
+ variable can also be seen as a "signal post"
+ or "flag mast". Then the signal is what is
+ attached to the "signal post" or "flag mast".
+
+ - emit a signal: Assign the value (the signal) to the global
+ variable ("set a flag") and broadcast a
+ global condition to wake those waiting for
+ a signal.
+
+ - wait for a signal: Loop over waiting for the global condition until
+ the global value matches the wait-for signal.
+
+ By default, all sync points are inactive. They do nothing (except to
+ burn a couple of CPU cycles for checking if they are active).
+
+ A sync point becomes active when an action is requested for it.
+ To do so, put a line like this in the test case file:
+
+ SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
+
+ This activates the sync point 'after_open_tables'. It requests it to
+ emit the signal 'opened' and wait for another thread to emit the signal
+ 'flushed' when the thread's execution runs through the sync point.
+
+ For every sync point there can be one action per thread only. Every
+ thread can request multiple actions, but only one per sync point. In
+ other words, a thread can activate multiple sync points.
+
+ Here is an example how to activate and use the sync points:
+
+ --connection conn1
+ SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
+ send INSERT INTO t1 VALUES(1);
+ --connection conn2
+ SET DEBUG_SYNC= 'now WAIT_FOR opened';
+ SET DEBUG_SYNC= 'after_abort_locks SIGNAL flushed';
+ FLUSH TABLE t1;
+
+ When conn1 runs through the INSERT statement, it hits the sync point
+ 'after_open_tables'. It notices that it is active and executes its
+ action. It emits the signal 'opened' and waits for another thread to
+ emit the signal 'flushed'.
+
+ conn2 waits immediately at the special sync point 'now' for another
+ thread to emit the 'opened' signal.
+
+ A signal remains in effect until it is overwritten. If conn1 signals
+ 'opened' before conn2 reaches 'now', conn2 will still find the 'opened'
+ signal. It does not wait in this case.
+
+ When conn2 reaches 'after_abort_locks', it signals 'flushed', which lets
+ conn1 awake.
+
+ Normally the activation of a sync point is cleared when it has been
+ executed. Sometimes it is necessary to keep the sync point active for
+ another execution. You can add an execute count to the action:
+
+ SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 3';
+
+ This sets the signal point's activation counter to 3. Each execution
+ decrements the counter. After the third execution the sync point
+ becomes inactive.
+
+ One of the primary goals of this facility is to eliminate sleeps from
+ the test suite. In most cases it should be possible to rewrite test
+ cases so that they do not need to sleep. (But this facility cannot
+ synchronize multiple processes.) However, to support test development,
+ and as a last resort, sync point waiting times out. There is a default
+ timeout, but it can be overridden:
+
+ SET DEBUG_SYNC= 'name WAIT_FOR sig TIMEOUT 10 EXECUTE 2';
+
+ TIMEOUT 0 is special: If the signal is not present, the wait times out
+ immediately.
+
+ When a wait timed out (even on TIMEOUT 0), a warning is generated so
+ that it shows up in the test result.
+
+ You can throw an error message and kill the query when a synchronization
+ point is hit a certain number of times:
+
+ SET DEBUG_SYNC= 'name HIT_LIMIT 3';
+
+ Or combine it with signal and/or wait:
+
+ SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 2 HIT_LIMIT 3';
+
+ Here the first two hits emit the signal, the third hit returns the error
+ message and kills the query.
+
+ For cases where you are not sure that an action is taken and thus
+ cleared in any case, you can force to clear (deactivate) a sync point:
+
+ SET DEBUG_SYNC= 'name CLEAR';
+
+ If you want to clear all actions and clear the global signal, use:
+
+ SET DEBUG_SYNC= 'RESET';
+
+ This is the only way to reset the global signal to an empty string.
+
+ For testing of the facility itself you can execute a sync point just
+ as if it had been hit:
+
+ SET DEBUG_SYNC= 'name TEST';
+
+
+ === Formal Syntax ===
+
+ The string to "assign" to the DEBUG_SYNC variable can contain:
+
+ {RESET |
+ <sync point name> TEST |
+ <sync point name> CLEAR |
+ <sync point name> {{SIGNAL <signal name> |
+ WAIT_FOR <signal name> [TIMEOUT <seconds>]}
+ [EXECUTE <count>] &| HIT_LIMIT <count>}
+
+ Here '&|' means 'and/or'. This means that one of the sections
+ separated by '&|' must be present or both of them.
+
+
+ === Activation/Deactivation ===
+
+ The facility is an optional part of the MySQL server.
+ It is enabled in a debug server by default.
+
+ ./configure --enable-debug-sync
+
+ The Debug Sync Facility, when compiled in, is disabled by default. It
+ can be enabled by a mysqld command line option:
+
+ --debug-sync-timeout[=default_wait_timeout_value_in_seconds]
+
+ 'default_wait_timeout_value_in_seconds' is the default timeout for the
+ WAIT_FOR action. If set to zero, the facility stays disabled.
+
+ The facility is enabled by default in the test suite, but can be
+ disabled with:
+
+ mysql-test-run.pl ... --debug-sync-timeout=0 ...
+
+ Likewise the default wait timeout can be set:
+
+ mysql-test-run.pl ... --debug-sync-timeout=10 ...
+
+ The command line option influences the readable value of the system
+ variable 'debug_sync'.
+
+ * If the facility is not compiled in, the system variable does not exist.
+
+ * If --debug-sync-timeout=0 the value of the variable reads as "OFF".
+
+ * Otherwise the value reads as "ON - current signal: " followed by the
+ current signal string, which can be empty.
+
+ The readable variable value is the same, regardless if read as global
+ or session value.
+
+ Setting the 'debug-sync' system variable requires 'SUPER' privilege.
+ You can never read back the string that you assigned to the variable,
+ unless you assign the value that the variable does already have. But
+ that would give a parse error. A syntactically correct string is
+ parsed into a debug sync action and stored apart from the variable value.
+
+
+ === Implementation ===
+
+ Pseudo code for a sync point:
+
+ #define DEBUG_SYNC(thd, sync_point_name)
+ if (unlikely(opt_debug_sync_timeout))
+ debug_sync(thd, STRING_WITH_LEN(sync_point_name))
+
+ The sync point performs a binary search in a sorted array of actions
+ for this thread.
+
+ The SET DEBUG_SYNC statement adds a requested action to the array or
+ overwrites an existing action for the same sync point. When it adds a
+ new action, the array is sorted again.
+
+
+ === A typical synchronization pattern ===
+
+ There are quite a few places in MySQL, where we use a synchronization
+ pattern like this:
+
+ mysql_mutex_lock(&mutex);
+ thd->enter_cond(&condition_variable, &mutex, new_message);
+ #if defined(ENABLE_DEBUG_SYNC)
+ if (!thd->killed && !end_of_wait_condition)
+ DEBUG_SYNC(thd, "sync_point_name");
+ #endif
+ while (!thd->killed && !end_of_wait_condition)
+ mysql_cond_wait(&condition_variable, &mutex);
+ thd->exit_cond(old_message);
+
+ Here some explanations:
+
+ thd->enter_cond() is used to register the condition variable and the
+ mutex in thd->mysys_var. This is done to allow the thread to be
+ interrupted (killed) from its sleep. Another thread can find the
+ condition variable to signal and mutex to use for synchronization in
+ this thread's THD::mysys_var.
+
+ thd->enter_cond() requires the mutex to be acquired in advance.
+
+ thd->exit_cond() unregisters the condition variable and mutex and
+ releases the mutex.
+
+ If you want to have a Debug Sync point with the wait, please place it
+ behind enter_cond(). Only then you can safely decide, if the wait will
+ be taken. Also you will have THD::proc_info correct when the sync
+ point emits a signal. DEBUG_SYNC sets its own proc_info, but restores
+ the previous one before releasing its internal mutex. As soon as
+ another thread sees the signal, it does also see the proc_info from
+ before entering the sync point. In this case it will be "new_message",
+ which is associated with the wait that is to be synchronized.
+
+ In the example above, the wait condition is repeated before the sync
+ point. This is done to skip the sync point, if no wait takes place.
+ The sync point is before the loop (not inside the loop) to have it hit
+ once only. It is possible that the condition variable is signaled
+ multiple times without the wait condition to be true.
+
+ A bit off-topic: At some places, the loop is taken around the whole
+ synchronization pattern:
+
+ while (!thd->killed && !end_of_wait_condition)
+ {
+ mysql_mutex_lock(&mutex);
+ thd->enter_cond(&condition_variable, &mutex, new_message);
+ if (!thd->killed [&& !end_of_wait_condition])
+ {
+ [DEBUG_SYNC(thd, "sync_point_name");]
+ mysql_cond_wait(&condition_variable, &mutex);
+ }
+ thd->exit_cond(old_message);
+ }
+
+ Note that it is important to repeat the test for thd->killed after
+ enter_cond(). Otherwise the killing thread may kill this thread after
+ it tested thd->killed in the loop condition and before it registered
+ the condition variable and mutex in enter_cond(). In this case, the
+ killing thread does not know that this thread is going to wait on a
+ condition variable. It would just set THD::killed. But if we would not
+ test it again, we would go asleep though we are killed. If the killing
+ thread would kill us when we are after the second test, but still
+ before sleeping, we hold the mutex, which is registered in mysys_var.
+ The killing thread would try to acquire the mutex before signaling
+ the condition variable. Since the mutex is only released implicitly in
+ mysql_cond_wait(), the signaling happens at the right place. We
+ have a safe synchronization.
+
+ === Co-work with the DBUG facility ===
+
+ When running the MySQL test suite with the --debug-dbug command line
+ option, the Debug Sync Facility writes trace messages to the DBUG
+ trace. The following shell commands proved very useful in extracting
+ relevant information:
+
+ egrep 'query:|debug_sync_exec:' mysql-test/var/log/mysqld.1.trace
+
+ It shows all executed SQL statements and all actions executed by
+ synchronization points.
+
+ Sometimes it is also useful to see, which synchronization points have
+ been run through (hit) with or without executing actions. Then add
+ "|debug_sync_point:" to the egrep pattern.
+
+ === Further reading ===
+
+ For a discussion of other methods to synchronize threads see
+ http://forge.mysql.com/wiki/MySQL_Internals_Test_Synchronization
+
+ For complete syntax tests, functional tests, and examples see the test
+ case debug_sync.test.
+
+ See also http://forge.mysql.com/worklog/task.php?id=4259
+*/
+
+#ifndef MYSQL_ABI_CHECK
+#include <stdlib.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef MYSQL_DYNAMIC_PLUGIN
+extern void (*debug_sync_service)(MYSQL_THD, const char *, size_t);
+#else
+#define debug_sync_service debug_sync_C_callback_ptr
+extern void (*debug_sync_C_callback_ptr)(MYSQL_THD, const char *, size_t);
+#endif
+
+#ifdef ENABLED_DEBUG_SYNC
+#define DEBUG_SYNC(thd, name) \
+ do { \
+ if (debug_sync_service) \
+ debug_sync_service(thd, name, sizeof(name)-1); \
+ } while(0)
+#else
+#define DEBUG_SYNC(thd,name) do { } while(0)
+#endif
+
+/* compatibility macro */
+#define DEBUG_SYNC_C(name) DEBUG_SYNC(NULL, name)
+
+#ifdef __cplusplus
+}
+#endif
+
+#define MYSQL_SERVICE_DEBUG_SYNC_INCLUDED
+#endif
diff --git a/include/mysql/services.h b/include/mysql/services.h
index dd0fa58db89..8eb506e1c37 100644
--- a/include/mysql/services.h
+++ b/include/mysql/services.h
@@ -23,7 +23,7 @@ extern "C" {
#include <mysql/service_thd_wait.h>
#include <mysql/service_thread_scheduler.h>
#include <mysql/service_progress_report.h>
-
+#include <mysql/service_debug_sync.h>
#ifdef __cplusplus
}
diff --git a/include/service_versions.h b/include/service_versions.h
index ee50d4856e9..436941643ec 100644
--- a/include/service_versions.h
+++ b/include/service_versions.h
@@ -24,3 +24,4 @@
#define VERSION_thd_wait 0x0100
#define VERSION_my_thread_scheduler 0x0100
#define VERSION_progress_report 0x0100
+#define VERSION_debug_sync 0x1000
diff --git a/libservices/CMakeLists.txt b/libservices/CMakeLists.txt
index ee6a7c73abe..eb8ff7ffe09 100644
--- a/libservices/CMakeLists.txt
+++ b/libservices/CMakeLists.txt
@@ -20,7 +20,8 @@ SET(MYSQLSERVICES_SOURCES
thd_alloc_service.c
thd_wait_service.c
my_thread_scheduler_service.c
- progress_report_service.c)
+ progress_report_service.c
+ debug_sync_service.c)
ADD_CONVENIENCE_LIBRARY(mysqlservices ${MYSQLSERVICES_SOURCES})
INSTALL(TARGETS mysqlservices DESTINATION ${INSTALL_LIBDIR} COMPONENT Development)
diff --git a/libservices/HOWTO b/libservices/HOWTO
index 7edafa89268..7dcfb6d9583 100644
--- a/libservices/HOWTO
+++ b/libservices/HOWTO
@@ -34,19 +34,19 @@ into a service "foo" you need to
#endif
extern struct foo_service_st {
- int (*foo_func1_type)(...); /* fix the prototype as appropriate */
- void (*foo_func2_type)(...); /* fix the prototype as appropriate */
+ int (*foo_func1_ptr)(...); /* fix the prototype as appropriate */
+ void (*foo_func2_ptr)(...); /* fix the prototype as appropriate */
} *foo_service;
#ifdef MYSQL_DYNAMIC_PLUGIN
- #define foo_func1(...) foo_service->foo_func1_type(...)
- #define foo_func2(...) foo_service->foo_func2_type(...)
+ #define foo_func1(...) foo_service->foo_func1_ptr(...)
+ #define foo_func2(...) foo_service->foo_func2_ptr(...)
#else
- int foo_func1_type(...); /* fix the prototype as appropriate */
- void foo_func2_type(...); /* fix the prototype as appropriate */
+ int foo_func1(...); /* fix the prototype as appropriate */
+ void foo_func2(...); /* fix the prototype as appropriate */
#endif
@@ -64,27 +64,26 @@ include them in it, e.g. if you use size_t - #include <stdlib.h>
it should also declare all the accompanying data structures, as necessary
(e.g. thd_alloc_service declares MYSQL_LEX_STRING).
-3. add the new file to include/Makefile.am (pkginclude_HEADERS)
-4. add the new file to include/mysql/services.h
-5. increase the minor plugin ABI version in include/mysql/plugin.h
- (MYSQL_PLUGIN_INTERFACE_VERSION = MYSQL_PLUGIN_INTERFACE_VERSION+1)
-6. add the version of your service to include/service_versions.h:
+3. add the new file to include/mysql/services.h
+4. increase the minor plugin ABI version in include/mysql/plugin.h
+ (MARIA_PLUGIN_INTERFACE_VERSION = MARIA_PLUGIN_INTERFACE_VERSION+1)
+5. add the version of your service to include/service_versions.h:
==================================================================
#define VERSION_foo 0x0100
==================================================================
-7. create a new file libservices/foo_service.h using the following template:
+6. create a new file libservices/foo_service.h using the following template:
==================================================================
/* GPL header */
#include <service_versions.h>
SERVICE_VERSION *foo_service= (void*)VERSION_foo;
==================================================================
-8. add the new file to libservices/CMakeLists.txt (MYSQLSERVICES_SOURCES)
-9. add the new file to libservices/Makefile.am (libmysqlservices_a_SOURCES)
-10. and finally, register your service for dynamic linking in
- sql/sql_plugin_services.h
-10.1 fill in the service structure:
+7. add the new file to libservices/CMakeLists.txt (MYSQLSERVICES_SOURCES)
+8. Add all new files to repository (bzr add)
+9. and finally, register your service for dynamic linking in
+ sql/sql_plugin_services.h as follows:
+9.1 fill in the service structure:
==================================================================
static struct foo_service_st foo_handler = {
foo_func1,
@@ -92,7 +91,7 @@ it should also declare all the accompanying data structures, as necessary
}
==================================================================
-10.2 and add it to the list of services
+9.2 and add it to the list of services
==================================================================
{ "foo_service", VERSION_foo, &foo_handler }
diff --git a/libservices/debug_sync_service.c b/libservices/debug_sync_service.c
new file mode 100644
index 00000000000..8c7f109e95a
--- /dev/null
+++ b/libservices/debug_sync_service.c
@@ -0,0 +1,18 @@
+/* Copyright (c) 2012, Monty Program Ab
+
+ 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 <service_versions.h>
+SERVICE_VERSION *debug_sync_service= (void*)VERSION_debug_sync;
diff --git a/mysql-test/r/plugin.result b/mysql-test/r/plugin.result
index 2efe7117a26..b5addf16147 100644
--- a/mysql-test/r/plugin.result
+++ b/mysql-test/r/plugin.result
@@ -15,7 +15,7 @@ PLUGIN_STATUS ACTIVE
PLUGIN_TYPE STORAGE ENGINE
PLUGIN_TYPE_VERSION #
PLUGIN_LIBRARY ha_example.so
-PLUGIN_LIBRARY_VERSION 1.2
+PLUGIN_LIBRARY_VERSION 1.3
PLUGIN_AUTHOR Brian Aker, MySQL AB
PLUGIN_DESCRIPTION Example storage engine
PLUGIN_LICENSE GPL
@@ -28,7 +28,7 @@ PLUGIN_STATUS ACTIVE
PLUGIN_TYPE DAEMON
PLUGIN_TYPE_VERSION #
PLUGIN_LIBRARY ha_example.so
-PLUGIN_LIBRARY_VERSION 1.2
+PLUGIN_LIBRARY_VERSION 1.3
PLUGIN_AUTHOR Sergei Golubchik
PLUGIN_DESCRIPTION Unusable Daemon
PLUGIN_LICENSE GPL
@@ -57,7 +57,7 @@ PLUGIN_STATUS DELETED
PLUGIN_TYPE STORAGE ENGINE
PLUGIN_TYPE_VERSION #
PLUGIN_LIBRARY ha_example.so
-PLUGIN_LIBRARY_VERSION 1.2
+PLUGIN_LIBRARY_VERSION 1.3
PLUGIN_AUTHOR Brian Aker, MySQL AB
PLUGIN_DESCRIPTION Example storage engine
PLUGIN_LICENSE GPL
diff --git a/mysys/my_static.c b/mysys/my_static.c
index 73af96d87d2..bc2d8beac83 100644
--- a/mysys/my_static.c
+++ b/mysys/my_static.c
@@ -90,13 +90,7 @@ static const char *proc_info_dummy(void *a __attribute__((unused)),
/* this is to be able to call set_thd_proc_info from the C code */
const char *(*proc_info_hook)(void *, const char *, const char *, const char *,
const unsigned int)= proc_info_dummy;
-#if defined(ENABLED_DEBUG_SYNC)
-/**
- Global pointer to be set if callback function is defined
- (e.g. in mysqld). See sql/debug_sync.cc.
-*/
-void (*debug_sync_C_callback_ptr)(const char *, size_t);
-#endif /* defined(ENABLED_DEBUG_SYNC) */
+void (*debug_sync_C_callback_ptr)(MYSQL_THD, const char *, size_t)= 0;
/* How to disable options */
my_bool my_disable_locking=0;
diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc
index a789763dd25..4097d7fe6e1 100644
--- a/sql/debug_sync.cc
+++ b/sql/debug_sync.cc
@@ -13,307 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-/**
- == Debug Sync Facility ==
-
- The Debug Sync Facility allows placement of synchronization points in
- the server code by using the DEBUG_SYNC macro:
-
- open_tables(...)
-
- DEBUG_SYNC(thd, "after_open_tables");
-
- lock_tables(...)
-
- When activated, a sync point can
-
- - Emit a signal and/or
- - Wait for a signal
-
- Nomenclature:
-
- - signal: A value of a global variable that persists
- until overwritten by a new signal. The global
- variable can also be seen as a "signal post"
- or "flag mast". Then the signal is what is
- attached to the "signal post" or "flag mast".
-
- - emit a signal: Assign the value (the signal) to the global
- variable ("set a flag") and broadcast a
- global condition to wake those waiting for
- a signal.
-
- - wait for a signal: Loop over waiting for the global condition until
- the global value matches the wait-for signal.
-
- By default, all sync points are inactive. They do nothing (except to
- burn a couple of CPU cycles for checking if they are active).
-
- A sync point becomes active when an action is requested for it.
- To do so, put a line like this in the test case file:
-
- SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
-
- This activates the sync point 'after_open_tables'. It requests it to
- emit the signal 'opened' and wait for another thread to emit the signal
- 'flushed' when the thread's execution runs through the sync point.
-
- For every sync point there can be one action per thread only. Every
- thread can request multiple actions, but only one per sync point. In
- other words, a thread can activate multiple sync points.
-
- Here is an example how to activate and use the sync points:
-
- --connection conn1
- SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
- send INSERT INTO t1 VALUES(1);
- --connection conn2
- SET DEBUG_SYNC= 'now WAIT_FOR opened';
- SET DEBUG_SYNC= 'after_abort_locks SIGNAL flushed';
- FLUSH TABLE t1;
-
- When conn1 runs through the INSERT statement, it hits the sync point
- 'after_open_tables'. It notices that it is active and executes its
- action. It emits the signal 'opened' and waits for another thread to
- emit the signal 'flushed'.
-
- conn2 waits immediately at the special sync point 'now' for another
- thread to emit the 'opened' signal.
-
- A signal remains in effect until it is overwritten. If conn1 signals
- 'opened' before conn2 reaches 'now', conn2 will still find the 'opened'
- signal. It does not wait in this case.
-
- When conn2 reaches 'after_abort_locks', it signals 'flushed', which lets
- conn1 awake.
-
- Normally the activation of a sync point is cleared when it has been
- executed. Sometimes it is necessary to keep the sync point active for
- another execution. You can add an execute count to the action:
-
- SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 3';
-
- This sets the signal point's activation counter to 3. Each execution
- decrements the counter. After the third execution the sync point
- becomes inactive.
-
- One of the primary goals of this facility is to eliminate sleeps from
- the test suite. In most cases it should be possible to rewrite test
- cases so that they do not need to sleep. (But this facility cannot
- synchronize multiple processes.) However, to support test development,
- and as a last resort, sync point waiting times out. There is a default
- timeout, but it can be overridden:
-
- SET DEBUG_SYNC= 'name WAIT_FOR sig TIMEOUT 10 EXECUTE 2';
-
- TIMEOUT 0 is special: If the signal is not present, the wait times out
- immediately.
-
- When a wait timed out (even on TIMEOUT 0), a warning is generated so
- that it shows up in the test result.
-
- You can throw an error message and kill the query when a synchronization
- point is hit a certain number of times:
-
- SET DEBUG_SYNC= 'name HIT_LIMIT 3';
-
- Or combine it with signal and/or wait:
-
- SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 2 HIT_LIMIT 3';
-
- Here the first two hits emit the signal, the third hit returns the error
- message and kills the query.
-
- For cases where you are not sure that an action is taken and thus
- cleared in any case, you can force to clear (deactivate) a sync point:
-
- SET DEBUG_SYNC= 'name CLEAR';
-
- If you want to clear all actions and clear the global signal, use:
-
- SET DEBUG_SYNC= 'RESET';
-
- This is the only way to reset the global signal to an empty string.
-
- For testing of the facility itself you can execute a sync point just
- as if it had been hit:
-
- SET DEBUG_SYNC= 'name TEST';
-
-
- === Formal Syntax ===
-
- The string to "assign" to the DEBUG_SYNC variable can contain:
-
- {RESET |
- <sync point name> TEST |
- <sync point name> CLEAR |
- <sync point name> {{SIGNAL <signal name> |
- WAIT_FOR <signal name> [TIMEOUT <seconds>]}
- [EXECUTE <count>] &| HIT_LIMIT <count>}
-
- Here '&|' means 'and/or'. This means that one of the sections
- separated by '&|' must be present or both of them.
-
-
- === Activation/Deactivation ===
-
- The facility is an optional part of the MySQL server.
- It is enabled in a debug server by default.
-
- ./configure --enable-debug-sync
-
- The Debug Sync Facility, when compiled in, is disabled by default. It
- can be enabled by a mysqld command line option:
-
- --debug-sync-timeout[=default_wait_timeout_value_in_seconds]
-
- 'default_wait_timeout_value_in_seconds' is the default timeout for the
- WAIT_FOR action. If set to zero, the facility stays disabled.
-
- The facility is enabled by default in the test suite, but can be
- disabled with:
-
- mysql-test-run.pl ... --debug-sync-timeout=0 ...
-
- Likewise the default wait timeout can be set:
-
- mysql-test-run.pl ... --debug-sync-timeout=10 ...
-
- The command line option influences the readable value of the system
- variable 'debug_sync'.
-
- * If the facility is not compiled in, the system variable does not exist.
-
- * If --debug-sync-timeout=0 the value of the variable reads as "OFF".
-
- * Otherwise the value reads as "ON - current signal: " followed by the
- current signal string, which can be empty.
-
- The readable variable value is the same, regardless if read as global
- or session value.
-
- Setting the 'debug-sync' system variable requires 'SUPER' privilege.
- You can never read back the string that you assigned to the variable,
- unless you assign the value that the variable does already have. But
- that would give a parse error. A syntactically correct string is
- parsed into a debug sync action and stored apart from the variable value.
-
-
- === Implementation ===
-
- Pseudo code for a sync point:
-
- #define DEBUG_SYNC(thd, sync_point_name)
- if (unlikely(opt_debug_sync_timeout))
- debug_sync(thd, STRING_WITH_LEN(sync_point_name))
-
- The sync point performs a binary search in a sorted array of actions
- for this thread.
-
- The SET DEBUG_SYNC statement adds a requested action to the array or
- overwrites an existing action for the same sync point. When it adds a
- new action, the array is sorted again.
-
-
- === A typical synchronization pattern ===
-
- There are quite a few places in MySQL, where we use a synchronization
- pattern like this:
-
- mysql_mutex_lock(&mutex);
- thd->enter_cond(&condition_variable, &mutex, new_message);
- #if defined(ENABLE_DEBUG_SYNC)
- if (!thd->killed && !end_of_wait_condition)
- DEBUG_SYNC(thd, "sync_point_name");
- #endif
- while (!thd->killed && !end_of_wait_condition)
- mysql_cond_wait(&condition_variable, &mutex);
- thd->exit_cond(old_message);
-
- Here some explanations:
-
- thd->enter_cond() is used to register the condition variable and the
- mutex in thd->mysys_var. This is done to allow the thread to be
- interrupted (killed) from its sleep. Another thread can find the
- condition variable to signal and mutex to use for synchronization in
- this thread's THD::mysys_var.
-
- thd->enter_cond() requires the mutex to be acquired in advance.
-
- thd->exit_cond() unregisters the condition variable and mutex and
- releases the mutex.
-
- If you want to have a Debug Sync point with the wait, please place it
- behind enter_cond(). Only then you can safely decide, if the wait will
- be taken. Also you will have THD::proc_info correct when the sync
- point emits a signal. DEBUG_SYNC sets its own proc_info, but restores
- the previous one before releasing its internal mutex. As soon as
- another thread sees the signal, it does also see the proc_info from
- before entering the sync point. In this case it will be "new_message",
- which is associated with the wait that is to be synchronized.
-
- In the example above, the wait condition is repeated before the sync
- point. This is done to skip the sync point, if no wait takes place.
- The sync point is before the loop (not inside the loop) to have it hit
- once only. It is possible that the condition variable is signaled
- multiple times without the wait condition to be true.
-
- A bit off-topic: At some places, the loop is taken around the whole
- synchronization pattern:
-
- while (!thd->killed && !end_of_wait_condition)
- {
- mysql_mutex_lock(&mutex);
- thd->enter_cond(&condition_variable, &mutex, new_message);
- if (!thd->killed [&& !end_of_wait_condition])
- {
- [DEBUG_SYNC(thd, "sync_point_name");]
- mysql_cond_wait(&condition_variable, &mutex);
- }
- thd->exit_cond(old_message);
- }
-
- Note that it is important to repeat the test for thd->killed after
- enter_cond(). Otherwise the killing thread may kill this thread after
- it tested thd->killed in the loop condition and before it registered
- the condition variable and mutex in enter_cond(). In this case, the
- killing thread does not know that this thread is going to wait on a
- condition variable. It would just set THD::killed. But if we would not
- test it again, we would go asleep though we are killed. If the killing
- thread would kill us when we are after the second test, but still
- before sleeping, we hold the mutex, which is registered in mysys_var.
- The killing thread would try to acquire the mutex before signaling
- the condition variable. Since the mutex is only released implicitly in
- mysql_cond_wait(), the signaling happens at the right place. We
- have a safe synchronization.
-
- === Co-work with the DBUG facility ===
-
- When running the MySQL test suite with the --debug-dbug command line
- option, the Debug Sync Facility writes trace messages to the DBUG
- trace. The following shell commands proved very useful in extracting
- relevant information:
-
- egrep 'query:|debug_sync_exec:' mysql-test/var/log/mysqld.1.trace
-
- It shows all executed SQL statements and all actions executed by
- synchronization points.
-
- Sometimes it is also useful to see, which synchronization points have
- been run through (hit) with or without executing actions. Then add
- "|debug_sync_point:" to the egrep pattern.
-
- === Further reading ===
-
- For a discussion of other methods to synchronize threads see
- http://forge.mysql.com/wiki/MySQL_Internals_Test_Synchronization
-
- For complete syntax tests, functional tests, and examples see the test
- case debug_sync.test.
-
- See also worklog entry WL#4259 - Test Synchronization Facility
-*/
+/* see include/mysql/service_debug_sync.h for debug sync documentation */
#include "debug_sync.h"
@@ -382,57 +82,16 @@ struct st_debug_sync_globals
};
static st_debug_sync_globals debug_sync_global; /* All globals in one object */
-/**
- Callback pointer for C files.
-*/
-extern "C" void (*debug_sync_C_callback_ptr)(const char *, size_t);
+extern uint opt_debug_sync_timeout;
/**
Callbacks from C files.
*/
C_MODE_START
-static void debug_sync_C_callback(const char *, size_t);
+static void debug_sync(THD *thd, const char *sync_point_name, size_t name_len);
static int debug_sync_qsort_cmp(const void *, const void *);
C_MODE_END
-/**
- Callback for debug sync, to be used by C files. See thr_lock.c for example.
-
- @description
-
- We cannot place a sync point directly in C files (like those in mysys or
- certain storage engines written mostly in C like MyISAM or Maria). Because
- they are C code and do not include sql_priv.h. So they do not know the
- macro DEBUG_SYNC(thd, sync_point_name). The macro needs a 'thd' argument.
- Hence it cannot be used in files outside of the sql/ directory.
-
- The workaround is to call back simple functions like this one from
- non-sql/ files.
-
- We want to allow modules like thr_lock to be used without sql/ and
- especially without Debug Sync. So we cannot just do a simple call
- of the callback function. Instead we provide a global pointer in
- the other file, which is to be set to the callback by Debug Sync.
- If the pointer is not set, no call back will be done. If Debug
- Sync sets the pointer to a callback function like this one, it will
- be called. That way thr_lock.c does not have an undefined reference
- to Debug Sync and can be used without it. Debug Sync, in contrast,
- has an undefined reference to that pointer and thus requires
- thr_lock to be linked too. But this is not a problem as it is part
- of the MySQL server anyway.
-
- @note
- The callback pointer in C files is set only if debug sync is
- initialized. And this is done only if opt_debug_sync_timeout is set.
-*/
-
-static void debug_sync_C_callback(const char *sync_point_name,
- size_t name_len)
-{
- if (unlikely(opt_debug_sync_timeout))
- debug_sync(current_thd, sync_point_name, name_len);
-}
-
#ifdef HAVE_PSI_INTERFACE
static PSI_mutex_key key_debug_sync_globals_ds_mutex;
@@ -495,7 +154,7 @@ int debug_sync_init(void)
DBUG_RETURN(rc); /* purecov: inspected */
/* Set the call back pointer in C files. */
- debug_sync_C_callback_ptr= debug_sync_C_callback;
+ debug_sync_C_callback_ptr= debug_sync;
}
DBUG_RETURN(0);
@@ -1857,12 +1516,14 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
@param[in] name_len length of sync point name
*/
-void debug_sync(THD *thd, const char *sync_point_name, size_t name_len)
+static void debug_sync(THD *thd, const char *sync_point_name, size_t name_len)
{
+ if (!thd)
+ thd= current_thd;
+
st_debug_sync_control *ds_control= thd->debug_sync_control;
st_debug_sync_action *action;
DBUG_ENTER("debug_sync");
- DBUG_ASSERT(thd);
DBUG_ASSERT(sync_point_name);
DBUG_ASSERT(name_len);
DBUG_ASSERT(ds_control);
diff --git a/sql/debug_sync.h b/sql/debug_sync.h
index 50881f491fe..4d29d6e7508 100644
--- a/sql/debug_sync.h
+++ b/sql/debug_sync.h
@@ -32,15 +32,6 @@ class THD;
#if defined(ENABLED_DEBUG_SYNC)
-/* Macro to be put in the code at synchronization points. */
-#define DEBUG_SYNC(_thd_, _sync_point_name_) \
- do { if (unlikely(opt_debug_sync_timeout)) \
- debug_sync(_thd_, STRING_WITH_LEN(_sync_point_name_)); \
- } while (0)
-
-/* Command line option --debug-sync-timeout. See mysqld.cc. */
-extern MYSQL_PLUGIN_IMPORT uint opt_debug_sync_timeout;
-
/* Default WAIT_FOR timeout if command line option is given without argument. */
#define DEBUG_SYNC_DEFAULT_WAIT_TIMEOUT 300
@@ -49,13 +40,8 @@ extern int debug_sync_init(void);
extern void debug_sync_end(void);
extern void debug_sync_init_thread(THD *thd);
extern void debug_sync_end_thread(THD *thd);
-extern void debug_sync(THD *thd, const char *sync_point_name, size_t name_len);
extern bool debug_sync_set_action(THD *thd, const char *action_str, size_t len);
-#else /* defined(ENABLED_DEBUG_SYNC) */
-
-#define DEBUG_SYNC(_thd_, _sync_point_name_) /* disabled DEBUG_SYNC */
-
#endif /* defined(ENABLED_DEBUG_SYNC) */
#endif /* DEBUG_SYNC_INCLUDED */
diff --git a/sql/slave.cc b/sql/slave.cc
index cf550ac22e3..ac7c9cd8c9e 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1339,7 +1339,7 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
const char act[]=
"now "
"wait_for signal.get_unix_timestamp";
- DBUG_ASSERT(opt_debug_sync_timeout > 0);
+ DBUG_ASSERT(debug_sync_service);
DBUG_ASSERT(!debug_sync_set_action(current_thd,
STRING_WITH_LEN(act)));
};);
@@ -1389,7 +1389,7 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
const char act[]=
"now "
"wait_for signal.get_server_id";
- DBUG_ASSERT(opt_debug_sync_timeout > 0);
+ DBUG_ASSERT(debug_sync_service);
DBUG_ASSERT(!debug_sync_set_action(current_thd,
STRING_WITH_LEN(act)));
};);
@@ -3034,7 +3034,7 @@ connected:
const char act[]=
"now "
"wait_for signal.io_thread_let_running";
- DBUG_ASSERT(opt_debug_sync_timeout > 0);
+ DBUG_ASSERT(debug_sync_service);
DBUG_ASSERT(!debug_sync_set_action(thd,
STRING_WITH_LEN(act)));
};);
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 90f6f0264a7..70258629197 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2934,7 +2934,7 @@ end_with_restore_list:
const char act2[]=
"now "
"signal signal.continued";
- DBUG_ASSERT(opt_debug_sync_timeout > 0);
+ DBUG_ASSERT(debug_sync_service);
DBUG_ASSERT(!debug_sync_set_action(thd,
STRING_WITH_LEN(act1)));
DBUG_ASSERT(!debug_sync_set_action(thd,
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 5f196e59c6f..067690ea06f 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -1520,6 +1520,10 @@ int plugin_init(int *argc, char **argv, int flags)
goto err;
}
+ /* prepare debug_sync service */
+ DBUG_ASSERT(strcmp(list_of_services[5].name, "debug_sync_service") == 0);
+ list_of_services[5].service= *(void**)&debug_sync_C_callback_ptr;
+
mysql_mutex_lock(&LOCK_plugin);
initialized= 1;
diff --git a/sql/sql_plugin_services.h b/sql/sql_plugin_services.h
index e6302f6d345..c779547059d 100644
--- a/sql/sql_plugin_services.h
+++ b/sql/sql_plugin_services.h
@@ -61,5 +61,6 @@ static struct st_service_ref list_of_services[]=
{ "thd_wait_service", VERSION_thd_wait, &thd_wait_handler },
{ "my_thread_scheduler_service", VERSION_my_thread_scheduler, &my_thread_scheduler_handler },
{ "progress_report_service", VERSION_progress_report, &progress_report_handler },
+ { "debug_sync_service", VERSION_debug_sync, 0 } // updated in plugin_init()
};
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 199afb30344..b5c852f9b29 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -904,7 +904,7 @@ impossible position";
const char act[]=
"now "
"wait_for signal.continue";
- DBUG_ASSERT(opt_debug_sync_timeout > 0);
+ DBUG_ASSERT(debug_sync_service);
DBUG_ASSERT(!debug_sync_set_action(thd,
STRING_WITH_LEN(act)));
const char act2[]=