diff options
author | Sergei Golubchik <sergii@pisem.net> | 2012-03-28 19:26:00 +0200 |
---|---|---|
committer | Sergei Golubchik <sergii@pisem.net> | 2012-03-28 19:26:00 +0200 |
commit | 0d5adca0de0a51b1f0bd49045fc4062eac7d1d25 (patch) | |
tree | 3cb7a294c5feebb813cf73b248c53d026aa11602 /include | |
parent | 20e706689df1eb87c696304797e9d6184c0a75bb (diff) | |
download | mariadb-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
Diffstat (limited to 'include')
-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 |
8 files changed, 363 insertions, 12 deletions
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 |