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 /sql | |
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 'sql')
-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 |
7 files changed, 18 insertions, 366 deletions
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[]= |