diff options
-rw-r--r-- | cmake/abi_check.cmake | 11 | ||||
-rw-r--r-- | cmake/do_abi_check.cmake | 8 | ||||
-rw-r--r-- | include/my_sys.h | 10 | ||||
-rw-r--r-- | include/mysql/plugin.h | 2 | ||||
-rw-r--r-- | include/mysql/plugin_audit.h.pp | 2 | ||||
-rw-r--r-- | include/mysql/plugin_auth.h.pp | 2 | ||||
-rw-r--r-- | include/mysql/plugin_ftparser.h.pp | 2 | ||||
-rw-r--r-- | include/mysql/service_debug_sync.h | 354 | ||||
-rw-r--r-- | include/mysql/services.h | 2 | ||||
-rw-r--r-- | include/service_versions.h | 1 | ||||
-rw-r--r-- | libservices/CMakeLists.txt | 3 | ||||
-rw-r--r-- | libservices/HOWTO | 35 | ||||
-rw-r--r-- | libservices/debug_sync_service.c | 18 | ||||
-rw-r--r-- | mysql-test/r/plugin.result | 6 | ||||
-rw-r--r-- | mysys/my_static.c | 8 | ||||
-rw-r--r-- | sql/debug_sync.cc | 355 | ||||
-rw-r--r-- | sql/debug_sync.h | 14 | ||||
-rw-r--r-- | sql/slave.cc | 6 | ||||
-rw-r--r-- | sql/sql_parse.cc | 2 | ||||
-rw-r--r-- | sql/sql_plugin.cc | 4 | ||||
-rw-r--r-- | sql/sql_plugin_services.h | 1 | ||||
-rw-r--r-- | sql/sql_repl.cc | 2 |
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[]= |