1
/* Copyright (C) 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc.
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
== Debug Sync Facility ==
19
The Debug Sync Facility allows placement of synchronization points in
20
the server code by using the DEBUG_SYNC macro:
24
DEBUG_SYNC(thd, "after_open_tables");
28
When activated, a sync point can
30
- Emit a signal and/or
35
- signal: A value of a global variable that persists
36
until overwritten by a new signal. The global
37
variable can also be seen as a "signal post"
38
or "flag mast". Then the signal is what is
39
attached to the "signal post" or "flag mast".
41
- emit a signal: Assign the value (the signal) to the global
42
variable ("set a flag") and broadcast a
43
global condition to wake those waiting for
46
- wait for a signal: Loop over waiting for the global condition until
47
the global value matches the wait-for signal.
49
By default, all sync points are inactive. They do nothing (except to
50
burn a couple of CPU cycles for checking if they are active).
52
A sync point becomes active when an action is requested for it.
53
To do so, put a line like this in the test case file:
55
SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
57
This activates the sync point 'after_open_tables'. It requests it to
58
emit the signal 'opened' and wait for another thread to emit the signal
59
'flushed' when the thread's execution runs through the sync point.
61
For every sync point there can be one action per thread only. Every
62
thread can request multiple actions, but only one per sync point. In
63
other words, a thread can activate multiple sync points.
65
Here is an example how to activate and use the sync points:
68
SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
69
send INSERT INTO t1 VALUES(1);
71
SET DEBUG_SYNC= 'now WAIT_FOR opened';
72
SET DEBUG_SYNC= 'after_abort_locks SIGNAL flushed';
75
When conn1 runs through the INSERT statement, it hits the sync point
76
'after_open_tables'. It notices that it is active and executes its
77
action. It emits the signal 'opened' and waits for another thread to
78
emit the signal 'flushed'.
80
conn2 waits immediately at the special sync point 'now' for another
81
thread to emit the 'opened' signal.
83
A signal remains in effect until it is overwritten. If conn1 signals
84
'opened' before conn2 reaches 'now', conn2 will still find the 'opened'
85
signal. It does not wait in this case.
87
When conn2 reaches 'after_abort_locks', it signals 'flushed', which lets
90
Normally the activation of a sync point is cleared when it has been
91
executed. Sometimes it is necessary to keep the sync point active for
92
another execution. You can add an execute count to the action:
94
SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 3';
96
This sets the signal point's activation counter to 3. Each execution
97
decrements the counter. After the third execution the sync point
100
One of the primary goals of this facility is to eliminate sleeps from
101
the test suite. In most cases it should be possible to rewrite test
102
cases so that they do not need to sleep. (But this facility cannot
103
synchronize multiple processes.) However, to support test development,
104
and as a last resort, sync point waiting times out. There is a default
105
timeout, but it can be overridden:
107
SET DEBUG_SYNC= 'name WAIT_FOR sig TIMEOUT 10 EXECUTE 2';
109
TIMEOUT 0 is special: If the signal is not present, the wait times out
112
When a wait timed out (even on TIMEOUT 0), a warning is generated so
113
that it shows up in the test result.
115
You can throw an error message and kill the query when a synchronization
116
point is hit a certain number of times:
118
SET DEBUG_SYNC= 'name HIT_LIMIT 3';
120
Or combine it with signal and/or wait:
122
SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 2 HIT_LIMIT 3';
124
Here the first two hits emit the signal, the third hit returns the error
125
message and kills the query.
127
For cases where you are not sure that an action is taken and thus
128
cleared in any case, you can force to clear (deactivate) a sync point:
130
SET DEBUG_SYNC= 'name CLEAR';
132
If you want to clear all actions and clear the global signal, use:
134
SET DEBUG_SYNC= 'RESET';
136
This is the only way to reset the global signal to an empty string.
138
For testing of the facility itself you can execute a sync point just
139
as if it had been hit:
141
SET DEBUG_SYNC= 'name TEST';
144
=== Formal Syntax ===
146
The string to "assign" to the DEBUG_SYNC variable can contain:
149
<sync point name> TEST |
150
<sync point name> CLEAR |
151
<sync point name> {{SIGNAL <signal name> |
152
WAIT_FOR <signal name> [TIMEOUT <seconds>]}
153
[EXECUTE <count>] &| HIT_LIMIT <count>}
155
Here '&|' means 'and/or'. This means that one of the sections
156
separated by '&|' must be present or both of them.
159
=== Activation/Deactivation ===
161
The facility is an optional part of the MySQL server.
162
It is enabled in a debug server by default.
164
./configure --enable-debug-sync
166
The Debug Sync Facility, when compiled in, is disabled by default. It
167
can be enabled by a mysqld command line option:
169
--debug-sync-timeout[=default_wait_timeout_value_in_seconds]
171
'default_wait_timeout_value_in_seconds' is the default timeout for the
172
WAIT_FOR action. If set to zero, the facility stays disabled.
174
The facility is enabled by default in the test suite, but can be
177
mysql-test-run.pl ... --debug-sync-timeout=0 ...
179
Likewise the default wait timeout can be set:
181
mysql-test-run.pl ... --debug-sync-timeout=10 ...
183
The command line option influences the readable value of the system
184
variable 'debug_sync'.
186
* If the facility is not compiled in, the system variable does not exist.
188
* If --debug-sync-timeout=0 the value of the variable reads as "OFF".
190
* Otherwise the value reads as "ON - current signal: " followed by the
191
current signal string, which can be empty.
193
The readable variable value is the same, regardless if read as global
196
Setting the 'debug-sync' system variable requires 'SUPER' privilege.
197
You can never read back the string that you assigned to the variable,
198
unless you assign the value that the variable does already have. But
199
that would give a parse error. A syntactically correct string is
200
parsed into a debug sync action and stored apart from the variable value.
203
=== Implementation ===
205
Pseudo code for a sync point:
207
#define DEBUG_SYNC(thd, sync_point_name)
208
if (unlikely(opt_debug_sync_timeout))
209
debug_sync(thd, STRING_WITH_LEN(sync_point_name))
211
The sync point performs a binary search in a sorted array of actions
214
The SET DEBUG_SYNC statement adds a requested action to the array or
215
overwrites an existing action for the same sync point. When it adds a
216
new action, the array is sorted again.
219
=== A typical synchronization pattern ===
221
There are quite a few places in MySQL, where we use a synchronization
224
pthread_mutex_lock(&mutex);
225
thd->enter_cond(&condition_variable, &mutex, new_message);
226
#if defined(ENABLE_DEBUG_SYNC)
227
if (!thd->killed && !end_of_wait_condition)
228
DEBUG_SYNC(thd, "sync_point_name");
230
while (!thd->killed && !end_of_wait_condition)
231
pthread_cond_wait(&condition_variable, &mutex);
232
thd->exit_cond(old_message);
234
Here some explanations:
236
thd->enter_cond() is used to register the condition variable and the
237
mutex in thd->mysys_var. This is done to allow the thread to be
238
interrupted (killed) from its sleep. Another thread can find the
239
condition variable to signal and mutex to use for synchronization in
240
this thread's THD::mysys_var.
242
thd->enter_cond() requires the mutex to be acquired in advance.
244
thd->exit_cond() unregisters the condition variable and mutex and
247
If you want to have a Debug Sync point with the wait, please place it
248
behind enter_cond(). Only then you can safely decide, if the wait will
249
be taken. Also you will have THD::proc_info correct when the sync
250
point emits a signal. DEBUG_SYNC sets its own proc_info, but restores
251
the previous one before releasing its internal mutex. As soon as
252
another thread sees the signal, it does also see the proc_info from
253
before entering the sync point. In this case it will be "new_message",
254
which is associated with the wait that is to be synchronized.
256
In the example above, the wait condition is repeated before the sync
257
point. This is done to skip the sync point, if no wait takes place.
258
The sync point is before the loop (not inside the loop) to have it hit
259
once only. It is possible that the condition variable is signaled
260
multiple times without the wait condition to be true.
262
A bit off-topic: At some places, the loop is taken around the whole
263
synchronization pattern:
265
while (!thd->killed && !end_of_wait_condition)
267
pthread_mutex_lock(&mutex);
268
thd->enter_cond(&condition_variable, &mutex, new_message);
269
if (!thd->killed [&& !end_of_wait_condition])
271
[DEBUG_SYNC(thd, "sync_point_name");]
272
pthread_cond_wait(&condition_variable, &mutex);
274
thd->exit_cond(old_message);
277
Note that it is important to repeat the test for thd->killed after
278
enter_cond(). Otherwise the killing thread may kill this thread after
279
it tested thd->killed in the loop condition and before it registered
280
the condition variable and mutex in enter_cond(). In this case, the
281
killing thread does not know that this thread is going to wait on a
282
condition variable. It would just set THD::killed. But if we would not
283
test it again, we would go asleep though we are killed. If the killing
284
thread would kill us when we are after the second test, but still
285
before sleeping, we hold the mutex, which is registered in mysys_var.
286
The killing thread would try to acquire the mutex before signaling
287
the condition variable. Since the mutex is only released implicitly in
288
pthread_cond_wait(), the signaling happens at the right place. We
289
have a safe synchronization.
291
=== Co-work with the DBUG facility ===
293
When running the MySQL test suite with the --debug command line
294
option, the Debug Sync Facility writes trace messages to the DBUG
295
trace. The following shell commands proved very useful in extracting
296
relevant information:
298
egrep 'query:|debug_sync_exec:' mysql-test/var/log/mysqld.1.trace
300
It shows all executed SQL statements and all actions executed by
301
synchronization points.
303
Sometimes it is also useful to see, which synchronization points have
304
been run through (hit) with or without executing actions. Then add
305
"|debug_sync_point:" to the egrep pattern.
307
=== Further reading ===
309
For a discussion of other methods to synchronize threads see
310
http://forge.mysql.com/wiki/MySQL_Internals_Test_Synchronization
312
For complete syntax tests, functional tests, and examples see the test
313
case debug_sync.test.
315
See also worklog entry WL#4259 - Test Synchronization Facility
318
#include "debug_sync.h"
320
#if defined(ENABLED_DEBUG_SYNC)
323
Due to weaknesses in our include files, we need to include
324
mysql_priv.h here. To have THD declared, we need to include
325
sql_class.h. This includes log_event.h, which in turn requires
326
declarations from mysql_priv.h (e.g. OPTION_AUTO_IS_NULL).
327
mysql_priv.h includes almost everything, so is sufficient here.
329
#include "mysql_priv.h"
332
Action to perform at a synchronization point.
333
NOTE: This structure is moved around in memory by realloc(), qsort(),
334
and memmove(). Do not add objects with non-trivial constuctors
335
or destructors, which might prevent moving of this structure
336
with these functions.
338
struct st_debug_sync_action
340
ulong activation_count; /* max(hit_limit, execute) */
341
ulong hit_limit; /* hits before kill query */
342
ulong execute; /* executes before self-clear */
343
ulong timeout; /* wait_for timeout */
344
String signal; /* signal to emit */
345
String wait_for; /* signal to wait for */
346
String sync_point; /* sync point name */
347
bool need_sort; /* if new action, array needs sort */
350
/* Debug sync control. Referenced by THD. */
351
struct st_debug_sync_control
353
st_debug_sync_action *ds_action; /* array of actions */
354
uint ds_active; /* # active actions */
355
uint ds_allocated; /* # allocated actions */
356
ulonglong dsp_hits; /* statistics */
357
ulonglong dsp_executed; /* statistics */
358
ulonglong dsp_max_active; /* statistics */
360
thd->proc_info points at unsynchronized memory.
361
It must not go away as long as the thread exists.
363
char ds_proc_info[80]; /* proc_info string */
368
Definitions for the debug sync facility.
369
1. Global string variable to hold a "signal" ("signal post", "flag mast").
370
2. Global condition variable for signaling and waiting.
371
3. Global mutex to synchronize access to the above.
373
struct st_debug_sync_globals
375
String ds_signal; /* signal variable */
376
pthread_cond_t ds_cond; /* condition variable */
377
pthread_mutex_t ds_mutex; /* mutex variable */
378
ulonglong dsp_hits; /* statistics */
379
ulonglong dsp_executed; /* statistics */
380
ulonglong dsp_max_active; /* statistics */
382
static st_debug_sync_globals debug_sync_global; /* All globals in one object */
385
Callback pointer for C files.
387
extern "C" void (*debug_sync_C_callback_ptr)(const char *, size_t);
391
Callback for debug sync, to be used by C files. See thr_lock.c for example.
395
We cannot place a sync point directly in C files (like those in mysys or
396
certain storage engines written mostly in C like MyISAM or Maria). Because
397
they are C code and do not include mysql_priv.h. So they do not know the
398
macro DEBUG_SYNC(thd, sync_point_name). The macro needs a 'thd' argument.
399
Hence it cannot be used in files outside of the sql/ directory.
401
The workaround is to call back simple functions like this one from
404
We want to allow modules like thr_lock to be used without sql/ and
405
especially without Debug Sync. So we cannot just do a simple call
406
of the callback function. Instead we provide a global pointer in
407
the other file, which is to be set to the callback by Debug Sync.
408
If the pointer is not set, no call back will be done. If Debug
409
Sync sets the pointer to a callback function like this one, it will
410
be called. That way thr_lock.c does not have an undefined reference
411
to Debug Sync and can be used without it. Debug Sync, in contrast,
412
has an undefined reference to that pointer and thus requires
413
thr_lock to be linked too. But this is not a problem as it is part
414
of the MySQL server anyway.
417
The callback pointer in C files is set only if debug sync is
418
initialized. And this is done only if opt_debug_sync_timeout is set.
421
static void debug_sync_C_callback(const char *sync_point_name,
424
if (unlikely(opt_debug_sync_timeout))
425
debug_sync(current_thd, sync_point_name, name_len);
430
Initialize the debug sync facility at server start.
437
int debug_sync_init(void)
439
DBUG_ENTER("debug_sync_init");
441
if (opt_debug_sync_timeout)
445
/* Initialize the global variables. */
446
debug_sync_global.ds_signal.length(0);
447
if ((rc= pthread_cond_init(&debug_sync_global.ds_cond, NULL)) ||
448
(rc= pthread_mutex_init(&debug_sync_global.ds_mutex,
449
MY_MUTEX_INIT_FAST)))
450
DBUG_RETURN(rc); /* purecov: inspected */
452
/* Set the call back pointer in C files. */
453
debug_sync_C_callback_ptr= debug_sync_C_callback;
461
End the debug sync facility.
464
This is called at server shutdown or after a thread initialization error.
467
void debug_sync_end(void)
469
DBUG_ENTER("debug_sync_end");
471
/* End the facility only if it had been initialized. */
472
if (debug_sync_C_callback_ptr)
474
/* Clear the call back pointer in C files. */
475
debug_sync_C_callback_ptr= NULL;
477
/* Destroy the global variables. */
478
debug_sync_global.ds_signal.free();
479
(void) pthread_cond_destroy(&debug_sync_global.ds_cond);
480
(void) pthread_mutex_destroy(&debug_sync_global.ds_mutex);
482
/* Print statistics. */
485
sql_print_information("Debug sync points hit: %22s",
486
llstr(debug_sync_global.dsp_hits, llbuff));
487
sql_print_information("Debug sync points executed: %22s",
488
llstr(debug_sync_global.dsp_executed, llbuff));
489
sql_print_information("Debug sync points max active per thread: %22s",
490
llstr(debug_sync_global.dsp_max_active, llbuff));
498
/* purecov: begin tested */
501
Disable the facility after lack of memory if no error can be returned.
504
Do not end the facility here because the global variables can
505
be in use by other threads.
508
static void debug_sync_emergency_disable(void)
510
DBUG_ENTER("debug_sync_emergency_disable");
512
opt_debug_sync_timeout= 0;
514
DBUG_PRINT("debug_sync",
515
("Debug Sync Facility disabled due to lack of memory."));
516
sql_print_error("Debug Sync Facility disabled due to lack of memory.");
525
Initialize the debug sync facility at thread start.
527
@param[in] thd thread handle
530
void debug_sync_init_thread(THD *thd)
532
DBUG_ENTER("debug_sync_init_thread");
535
if (opt_debug_sync_timeout)
537
thd->debug_sync_control= (st_debug_sync_control*)
538
my_malloc(sizeof(st_debug_sync_control), MYF(MY_WME | MY_ZEROFILL));
539
if (!thd->debug_sync_control)
542
Error is reported by my_malloc().
543
We must disable the facility. We have no way to return an error.
545
debug_sync_emergency_disable(); /* purecov: tested */
554
End the debug sync facility at thread end.
556
@param[in] thd thread handle
559
void debug_sync_end_thread(THD *thd)
561
DBUG_ENTER("debug_sync_end_thread");
564
if (thd->debug_sync_control)
566
st_debug_sync_control *ds_control= thd->debug_sync_control;
569
This synchronization point can be used to synchronize on thread end.
570
This is the latest point in a THD's life, where this can be done.
572
DEBUG_SYNC(thd, "thread_end");
574
if (ds_control->ds_action)
576
st_debug_sync_action *action= ds_control->ds_action;
577
st_debug_sync_action *action_end= action + ds_control->ds_allocated;
578
for (; action < action_end; action++)
580
action->signal.free();
581
action->wait_for.free();
582
action->sync_point.free();
584
my_free(ds_control->ds_action, MYF(0));
588
pthread_mutex_lock(&debug_sync_global.ds_mutex);
589
debug_sync_global.dsp_hits+= ds_control->dsp_hits;
590
debug_sync_global.dsp_executed+= ds_control->dsp_executed;
591
if (debug_sync_global.dsp_max_active < ds_control->dsp_max_active)
592
debug_sync_global.dsp_max_active= ds_control->dsp_max_active;
593
pthread_mutex_unlock(&debug_sync_global.ds_mutex);
595
my_free(ds_control, MYF(0));
596
thd->debug_sync_control= NULL;
604
Move a string by length.
606
@param[out] to buffer for the resulting string
607
@param[in] to_end end of buffer
608
@param[in] from source string
609
@param[in] length number of bytes to copy
611
@return pointer to end of copied string
614
static char *debug_sync_bmove_len(char *to, char *to_end,
615
const char *from, size_t length)
619
DBUG_ASSERT(!length || from);
620
set_if_smaller(length, (size_t) (to_end - to));
621
memcpy(to, from, length);
622
return (to + length);
626
#if !defined(DBUG_OFF)
629
Create a string that describes an action.
631
@param[out] result buffer for the resulting string
632
@param[in] size size of result buffer
633
@param[in] action action to describe
636
static void debug_sync_action_string(char *result, uint size,
637
st_debug_sync_action *action)
640
char *wend= wtxt + size - 1; /* Allow emergency '\0'. */
644
/* If an execute count is present, signal or wait_for are needed too. */
645
DBUG_ASSERT(!action->execute ||
646
action->signal.length() || action->wait_for.length());
650
if (action->signal.length())
652
wtxt= debug_sync_bmove_len(wtxt, wend, STRING_WITH_LEN("SIGNAL "));
653
wtxt= debug_sync_bmove_len(wtxt, wend, action->signal.ptr(),
654
action->signal.length());
656
if (action->wait_for.length())
658
if ((wtxt == result) && (wtxt < wend))
660
wtxt= debug_sync_bmove_len(wtxt, wend, STRING_WITH_LEN(" WAIT_FOR "));
661
wtxt= debug_sync_bmove_len(wtxt, wend, action->wait_for.ptr(),
662
action->wait_for.length());
664
if (action->timeout != opt_debug_sync_timeout)
666
wtxt+= my_snprintf(wtxt, wend - wtxt, " TIMEOUT %lu", action->timeout);
669
if (action->execute != 1)
671
wtxt+= my_snprintf(wtxt, wend - wtxt, " EXECUTE %lu", action->execute);
674
if (action->hit_limit)
676
wtxt+= my_snprintf(wtxt, wend - wtxt, "%sHIT_LIMIT %lu",
677
(wtxt == result) ? "" : " ", action->hit_limit);
681
If (wtxt == wend) string may not be terminated.
682
There is one byte left for an emergency termination.
691
@param[in] thd thread handle
694
static void debug_sync_print_actions(THD *thd)
696
st_debug_sync_control *ds_control= thd->debug_sync_control;
698
DBUG_ENTER("debug_sync_print_actions");
704
for (idx= 0; idx < ds_control->ds_active; idx++)
706
const char *dsp_name= ds_control->ds_action[idx].sync_point.c_ptr();
707
char action_string[256];
709
debug_sync_action_string(action_string, sizeof(action_string),
710
ds_control->ds_action + idx);
711
DBUG_PRINT("debug_sync_list", ("%s %s", dsp_name, action_string));
717
#endif /* !defined(DBUG_OFF) */
721
Compare two actions by sync point name length, string.
723
@param[in] arg1 reference to action1
724
@param[in] arg2 reference to action2
727
@retval == 0 length1/string1 is same as length2/string2
728
@retval < 0 length1/string1 is smaller
729
@retval > 0 length1/string1 is bigger
732
static int debug_sync_qsort_cmp(const void* arg1, const void* arg2)
734
st_debug_sync_action *action1= (st_debug_sync_action*) arg1;
735
st_debug_sync_action *action2= (st_debug_sync_action*) arg2;
737
DBUG_ASSERT(action1);
738
DBUG_ASSERT(action2);
740
if (!(diff= action1->sync_point.length() - action2->sync_point.length()))
741
diff= memcmp(action1->sync_point.ptr(), action2->sync_point.ptr(),
742
action1->sync_point.length());
749
Find a debug sync action.
751
@param[in] actionarr array of debug sync actions
752
@param[in] quantity number of actions in array
753
@param[in] dsp_name name of debug sync point to find
754
@param[in] name_len length of name of debug sync point
757
@retval != NULL found sync point in array
758
@retval NULL not found
761
Binary search. Array needs to be sorted by length, sync point name.
764
static st_debug_sync_action *debug_sync_find(st_debug_sync_action *actionarr,
766
const char *dsp_name,
769
st_debug_sync_action *action;
774
DBUG_ASSERT(actionarr);
775
DBUG_ASSERT(dsp_name);
776
DBUG_ASSERT(name_len);
783
mid= (low + high) / 2;
784
action= actionarr + mid;
785
if (!(diff= name_len - action->sync_point.length()) &&
786
!(diff= memcmp(dsp_name, action->sync_point.ptr(), name_len)))
796
action= actionarr + low;
797
if ((name_len == action->sync_point.length()) &&
798
!memcmp(dsp_name, action->sync_point.ptr(), name_len))
807
Reset the debug sync facility.
809
@param[in] thd thread handle
812
Remove all actions of this thread.
813
Clear the global signal.
816
static void debug_sync_reset(THD *thd)
818
st_debug_sync_control *ds_control= thd->debug_sync_control;
819
DBUG_ENTER("debug_sync_reset");
821
DBUG_ASSERT(ds_control);
823
/* Remove all actions of this thread. */
824
ds_control->ds_active= 0;
826
/* Clear the global signal. */
827
pthread_mutex_lock(&debug_sync_global.ds_mutex);
828
debug_sync_global.ds_signal.length(0);
829
pthread_mutex_unlock(&debug_sync_global.ds_mutex);
836
Remove a debug sync action.
838
@param[in] ds_control control object
839
@param[in] action action to be removed
842
Removing an action mainly means to decrement the ds_active counter.
843
But if the action is between other active action in the array, then
844
the array needs to be shrinked. The active actions above the one to
845
be removed have to be moved down by one slot.
848
static void debug_sync_remove_action(st_debug_sync_control *ds_control,
849
st_debug_sync_action *action)
851
uint dsp_idx= action - ds_control->ds_action;
852
DBUG_ENTER("debug_sync_remove_action");
853
DBUG_ASSERT(ds_control);
854
DBUG_ASSERT(ds_control == current_thd->debug_sync_control);
856
DBUG_ASSERT(dsp_idx < ds_control->ds_active);
858
/* Decrement the number of currently active actions. */
859
ds_control->ds_active--;
862
If this was not the last active action in the array, we need to
863
shift remaining active actions down to keep the array gap-free.
864
Otherwise binary search might fail or take longer than necessary at
865
least. Also new actions are always put to the end of the array.
867
if (ds_control->ds_active > dsp_idx)
870
Do not make save_action an object of class st_debug_sync_action.
871
Its destructor would tamper with the String pointers.
873
uchar save_action[sizeof(st_debug_sync_action)];
876
Copy the to-be-removed action object to temporary storage before
877
the shift copies the string pointers over. Do not use assignment
878
because it would use assignment operator methods for the Strings.
879
This would copy the strings. The shift below overwrite the string
880
pointers without freeing them first. By using memmove() we save
881
the pointers, which are overwritten by the shift.
883
memmove(save_action, action, sizeof(st_debug_sync_action));
885
/* Move actions down. */
886
memmove(ds_control->ds_action + dsp_idx,
887
ds_control->ds_action + dsp_idx + 1,
888
(ds_control->ds_active - dsp_idx) *
889
sizeof(st_debug_sync_action));
892
Copy back the saved action object to the now free array slot. This
893
replaces the double references of String pointers that have been
894
produced by the shift. Again do not use an assignment operator to
895
avoid string allocation/copy.
897
memmove(ds_control->ds_action + ds_control->ds_active, save_action,
898
sizeof(st_debug_sync_action));
906
Get a debug sync action.
908
@param[in] thd thread handle
909
@param[in] dsp_name debug sync point name
910
@param[in] name_len length of sync point name
917
Find the debug sync action for a debug sync point or make a new one.
920
static st_debug_sync_action *debug_sync_get_action(THD *thd,
921
const char *dsp_name,
924
st_debug_sync_control *ds_control= thd->debug_sync_control;
925
st_debug_sync_action *action;
926
DBUG_ENTER("debug_sync_get_action");
928
DBUG_ASSERT(dsp_name);
929
DBUG_ASSERT(name_len);
930
DBUG_ASSERT(ds_control);
931
DBUG_PRINT("debug_sync", ("sync_point: '%.*s'", (int) name_len, dsp_name));
932
DBUG_PRINT("debug_sync", ("active: %u allocated: %u",
933
ds_control->ds_active, ds_control->ds_allocated));
935
/* There cannot be more active actions than allocated. */
936
DBUG_ASSERT(ds_control->ds_active <= ds_control->ds_allocated);
937
/* If there are active actions, the action array must be present. */
938
DBUG_ASSERT(!ds_control->ds_active || ds_control->ds_action);
940
/* Try to reuse existing action if there is one for this sync point. */
941
if (ds_control->ds_active &&
942
(action= debug_sync_find(ds_control->ds_action, ds_control->ds_active,
943
dsp_name, name_len)))
945
/* Reuse an already active sync point action. */
946
DBUG_ASSERT((uint)(action - ds_control->ds_action) < ds_control->ds_active);
947
DBUG_PRINT("debug_sync", ("reuse action idx: %ld",
948
(long) (action - ds_control->ds_action)));
952
/* Create a new action. */
953
int dsp_idx= ds_control->ds_active++;
954
set_if_bigger(ds_control->dsp_max_active, ds_control->ds_active);
955
if (ds_control->ds_active > ds_control->ds_allocated)
957
uint new_alloc= ds_control->ds_active + 3;
958
void *new_action= my_realloc(ds_control->ds_action,
959
new_alloc * sizeof(st_debug_sync_action),
960
MYF(MY_WME | MY_ALLOW_ZERO_PTR));
963
/* Error is reported by my_malloc(). */
964
goto err; /* purecov: tested */
966
ds_control->ds_action= (st_debug_sync_action*) new_action;
967
ds_control->ds_allocated= new_alloc;
968
/* Clear memory as we do not run string constructors here. */
969
bzero((uchar*) (ds_control->ds_action + dsp_idx),
970
(new_alloc - dsp_idx) * sizeof(st_debug_sync_action));
972
DBUG_PRINT("debug_sync", ("added action idx: %u", dsp_idx));
973
action= ds_control->ds_action + dsp_idx;
974
if (action->sync_point.copy(dsp_name, name_len, system_charset_info))
976
/* Error is reported by my_malloc(). */
977
goto err; /* purecov: tested */
979
action->need_sort= TRUE;
981
DBUG_ASSERT(action >= ds_control->ds_action);
982
DBUG_ASSERT(action < ds_control->ds_action + ds_control->ds_active);
983
DBUG_PRINT("debug_sync", ("action: 0x%lx array: 0x%lx count: %u",
984
(long) action, (long) ds_control->ds_action,
985
ds_control->ds_active));
989
/* purecov: begin tested */
997
Set a debug sync action.
999
@param[in] thd thread handle
1000
@param[in] action synchronization action
1007
This is called from the debug sync parser. It arms the action for
1008
the requested sync point. If the action parsed into an empty action,
1009
it is removed instead.
1011
Setting an action for a sync point means to make the sync point
1012
active. When it is hit it will execute this action.
1014
Before parsing, we "get" an action object. This is placed at the
1015
end of the thread's action array unless the requested sync point
1016
has an action already.
1018
Then the parser fills the action object from the request string.
1020
Finally the action is "set" for the sync point. If it was parsed
1021
to be empty, it is removed from the array. If it did belong to a
1022
sync point before, the sync point becomes inactive. If the action
1023
became non-empty and it did not belong to a sync point before (it
1024
was added at the end of the action array), the action array needs
1025
to be sorted by sync point.
1027
If the sync point name is "now", it is executed immediately.
1030
static bool debug_sync_set_action(THD *thd, st_debug_sync_action *action)
1032
st_debug_sync_control *ds_control= thd->debug_sync_control;
1033
bool is_dsp_now= FALSE;
1034
DBUG_ENTER("debug_sync_set_action");
1036
DBUG_ASSERT(action);
1037
DBUG_ASSERT(ds_control);
1039
action->activation_count= max(action->hit_limit, action->execute);
1040
if (!action->activation_count)
1042
debug_sync_remove_action(ds_control, action);
1043
DBUG_PRINT("debug_sync", ("action cleared"));
1047
const char *dsp_name= action->sync_point.c_ptr();
1048
DBUG_EXECUTE("debug_sync", {
1049
/* Functions as DBUG_PRINT args can change keyword and line nr. */
1050
const char *sig_emit= action->signal.c_ptr();
1051
const char *sig_wait= action->wait_for.c_ptr();
1052
DBUG_PRINT("debug_sync",
1053
("sync_point: '%s' activation_count: %lu hit_limit: %lu "
1054
"execute: %lu timeout: %lu signal: '%s' wait_for: '%s'",
1055
dsp_name, action->activation_count,
1056
action->hit_limit, action->execute, action->timeout,
1057
sig_emit, sig_wait));});
1059
/* Check this before sorting the array. action may move. */
1060
is_dsp_now= !my_strcasecmp(system_charset_info, dsp_name, "now");
1062
if (action->need_sort)
1064
action->need_sort= FALSE;
1065
/* Sort actions by (name_len, name). */
1066
my_qsort(ds_control->ds_action, ds_control->ds_active,
1067
sizeof(st_debug_sync_action), debug_sync_qsort_cmp);
1070
DBUG_EXECUTE("debug_sync_list", debug_sync_print_actions(thd););
1072
/* Execute the special sync point 'now' if activated above. */
1075
DEBUG_SYNC(thd, "now");
1077
If HIT_LIMIT for sync point "now" was 1, the execution of the sync
1078
point decremented it to 0. In this case the following happened:
1080
- an error message was reported with my_error() and
1081
- the statement was killed with thd->killed= THD::KILL_QUERY.
1083
If a statement reports an error, it must not call send_ok().
1084
The calling functions will not call send_ok(), if we return TRUE
1087
thd->killed is also set if the wait is interrupted from a
1088
KILL or KILL QUERY statement. In this case, no error is reported
1089
and shall not be reported as a result of SET DEBUG_SYNC.
1090
Hence, we check for the first condition above.
1092
if (thd->is_error())
1101
Extract a token from a string.
1103
@param[out] token_p returns start of token
1104
@param[out] token_length_p returns length of token
1105
@param[in,out] ptr current string pointer, adds '\0' terminators
1107
@return string pointer or NULL
1108
@retval != NULL ptr behind token terminator or at string end
1109
@retval NULL no token found in remainder of string
1112
This function assumes that the string is in system_charset_info,
1113
that this charset is single byte for ASCII NUL ('\0'), that no
1114
character except of ASCII NUL ('\0') contains a byte with value 0,
1115
and that ASCII NUL ('\0') is used as the string terminator.
1117
This function needs to return tokens that are terminated with ASCII
1118
NUL ('\0'). The tokens are used in my_strcasecmp(). Unfortunately
1119
there is no my_strncasecmp().
1121
To return the last token without copying it, we require the input
1122
string to be nul terminated.
1125
This function skips space characters at string begin.
1127
It returns a pointer to the first non-space character in *token_p.
1129
If no non-space character is found before the string terminator
1130
ASCII NUL ('\0'), the function returns NULL. *token_p and
1131
*token_length_p remain unchanged in this case (they are not set).
1133
The function takes a space character or an ASCII NUL ('\0') as a
1134
terminator of the token. The space character could be multi-byte.
1136
It returns the length of the token in bytes, excluding the
1137
terminator, in *token_length_p.
1139
If the terminator of the token is ASCII NUL ('\0'), it returns a
1140
pointer to the terminator (string end).
1142
If the terminator is a space character, it replaces the the first
1143
byte of the terminator character by ASCII NUL ('\0'), skips the (now
1144
corrupted) terminator character, and skips all following space
1145
characters. It returns a pointer to the next non-space character or
1146
to the string terminator ASCII NUL ('\0').
1149
static char *debug_sync_token(char **token_p, uint *token_length_p, char *ptr)
1151
DBUG_ASSERT(token_p);
1152
DBUG_ASSERT(token_length_p);
1155
/* Skip leading space */
1156
while (my_isspace(system_charset_info, *ptr))
1157
ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
1165
/* Get token start. */
1168
/* Find token end. */
1169
while (*ptr && !my_isspace(system_charset_info, *ptr))
1170
ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
1172
/* Get token length. */
1173
*token_length_p= ptr - *token_p;
1175
/* If necessary, terminate token. */
1178
/* Get terminator character length. */
1179
uint mbspacelen= my_mbcharlen(system_charset_info, (uchar) *ptr);
1181
/* Terminate token. */
1184
/* Skip the terminator. */
1187
/* Skip trailing space */
1188
while (my_isspace(system_charset_info, *ptr))
1189
ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
1198
Extract a number from a string.
1200
@param[out] number_p returns number
1201
@param[in] actstrptr current pointer in action string
1203
@return string pointer or NULL
1204
@retval != NULL ptr behind token terminator or at string end
1205
@retval NULL no token found or token is not valid number
1208
The same assumptions about charset apply as for debug_sync_token().
1211
This function fetches a token from the string and converts it
1214
If there is no token left in the string, or the token is not a valid
1215
decimal number, NULL is returned. The result in *number_p is
1216
undefined in this case.
1219
static char *debug_sync_number(ulong *number_p, char *actstrptr)
1225
DBUG_ASSERT(number_p);
1226
DBUG_ASSERT(actstrptr);
1228
/* Get token from string. */
1229
if (!(ptr= debug_sync_token(&token, &token_length, actstrptr)))
1232
*number_p= strtoul(token, &ept, 10);
1242
Evaluate a debug sync action string.
1244
@param[in] thd thread handle
1245
@param[in,out] action_str action string to receive '\0' terminators
1252
This is called when the DEBUG_SYNC system variable is set.
1253
Parse action string, build a debug sync action, activate it.
1255
Before parsing, we "get" an action object. This is placed at the
1256
end of the thread's action array unless the requested sync point
1257
has an action already.
1259
Then the parser fills the action object from the request string.
1261
Finally the action is "set" for the sync point. This means that the
1262
sync point becomes active or inactive, depending on the action
1266
The input string needs to be ASCII NUL ('\0') terminated. We split
1267
nul-terminated tokens in it without copy.
1269
@see the function comment of debug_sync_token() for more constraints
1273
static bool debug_sync_eval_action(THD *thd, char *action_str)
1275
st_debug_sync_action *action= NULL;
1279
uint token_length= 0;
1280
DBUG_ENTER("debug_sync_eval_action");
1282
DBUG_ASSERT(action_str);
1285
Get debug sync point name. Or a special command.
1287
if (!(ptr= debug_sync_token(&token, &token_length, action_str)))
1289
errmsg= "Missing synchronization point name";
1294
If there is a second token, the first one is the sync point name.
1298
/* Get an action object to collect the requested action parameters. */
1299
action= debug_sync_get_action(thd, token, token_length);
1302
/* Error message is sent. */
1303
DBUG_RETURN(TRUE); /* purecov: tested */
1308
Get kind of action to be taken at sync point.
1310
if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1312
/* No action present. Try special commands. Token unchanged. */
1317
if (!my_strcasecmp(system_charset_info, token, "RESET"))
1319
/* It is RESET. Reset all actions and global signal. */
1320
debug_sync_reset(thd);
1324
/* Token unchanged. It still contains sync point name. */
1325
errmsg= "Missing action after synchronization point name '%.*s'";
1330
Check for pseudo actions first. Start with actions that work on
1333
DBUG_ASSERT(action);
1338
if (!my_strcasecmp(system_charset_info, token, "TEST"))
1340
/* It is TEST. Nothing must follow it. */
1343
errmsg= "Nothing must follow action TEST";
1347
/* Execute sync point. */
1348
debug_sync(thd, action->sync_point.ptr(), action->sync_point.length());
1349
/* Fix statistics. This was not a real hit of the sync point. */
1350
thd->debug_sync_control->dsp_hits--;
1355
Now check for actions that define a new action.
1356
Initialize action. Do not use bzero(). Strings may have malloced.
1358
action->activation_count= 0;
1359
action->hit_limit= 0;
1362
action->signal.length(0);
1363
action->wait_for.length(0);
1368
if (!my_strcasecmp(system_charset_info, token, "CLEAR"))
1370
/* It is CLEAR. Nothing must follow it. */
1373
errmsg= "Nothing must follow action CLEAR";
1377
/* Set (clear/remove) action. */
1382
Now check for real sync point actions.
1388
if (!my_strcasecmp(system_charset_info, token, "SIGNAL"))
1390
/* It is SIGNAL. Signal name must follow. */
1391
if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1393
errmsg= "Missing signal name after action SIGNAL";
1396
if (action->signal.copy(token, token_length, system_charset_info))
1398
/* Error is reported by my_malloc(). */
1399
/* purecov: begin tested */
1405
/* Set default for EXECUTE option. */
1408
/* Get next token. If none follows, set action. */
1409
if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1416
if (!my_strcasecmp(system_charset_info, token, "WAIT_FOR"))
1418
/* It is WAIT_FOR. Wait_for signal name must follow. */
1419
if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1421
errmsg= "Missing signal name after action WAIT_FOR";
1424
if (action->wait_for.copy(token, token_length, system_charset_info))
1426
/* Error is reported by my_malloc(). */
1427
/* purecov: begin tested */
1433
/* Set default for EXECUTE and TIMEOUT options. */
1435
action->timeout= opt_debug_sync_timeout;
1437
/* Get next token. If none follows, set action. */
1438
if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1444
if (!my_strcasecmp(system_charset_info, token, "TIMEOUT"))
1446
/* It is TIMEOUT. Number must follow. */
1447
if (!(ptr= debug_sync_number(&action->timeout, ptr)))
1449
errmsg= "Missing valid number after TIMEOUT";
1453
/* Get next token. If none follows, set action. */
1454
if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1462
if (!my_strcasecmp(system_charset_info, token, "EXECUTE"))
1465
EXECUTE requires either SIGNAL and/or WAIT_FOR to be present.
1466
In this case action->execute has been preset to 1.
1468
if (!action->execute)
1470
errmsg= "Missing action before EXECUTE";
1474
/* Number must follow. */
1475
if (!(ptr= debug_sync_number(&action->execute, ptr)))
1477
errmsg= "Missing valid number after EXECUTE";
1481
/* Get next token. If none follows, set action. */
1482
if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1489
if (!my_strcasecmp(system_charset_info, token, "HIT_LIMIT"))
1491
/* Number must follow. */
1492
if (!(ptr= debug_sync_number(&action->hit_limit, ptr)))
1494
errmsg= "Missing valid number after HIT_LIMIT";
1498
/* Get next token. If none follows, set action. */
1499
if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1503
errmsg= "Illegal or out of order stuff: '%.*s'";
1509
NOTE: errmsg must either have %.*s or none % at all.
1510
It can be NULL if an error message is already reported
1511
(e.g. by my_malloc()).
1513
set_if_smaller(token_length, 64); /* Limit error message length. */
1514
my_printf_error(ER_PARSE_ERROR, errmsg, MYF(0), token_length, token);
1517
debug_sync_remove_action(thd->debug_sync_control, action);
1521
DBUG_RETURN(debug_sync_set_action(thd, action));
1529
Check if the system variable 'debug_sync' can be set.
1531
@param[in] thd thread handle
1532
@param[in] var set variable request
1535
@retval FALSE ok, variable can be set
1536
@retval TRUE error, variable cannot be set
1539
bool sys_var_debug_sync::check(THD *thd, set_var *var)
1541
DBUG_ENTER("sys_var_debug_sync::check");
1546
Variable can be set for the session only.
1548
This could be changed later. Then we need to have a global array of
1549
actions in addition to the thread local ones. SET GLOBAL would
1550
manage the global array, SET [SESSION] the local array. A sync point
1551
would need to look for a local and a global action. Setting and
1552
executing of global actions need to be protected by a mutex.
1554
The purpose of global actions could be to allow synchronizing with
1555
connectionless threads that cannot execute SET statements.
1557
if (var->type == OPT_GLOBAL)
1559
my_error(ER_LOCAL_VARIABLE, MYF(0), name);
1564
Do not check for disabled facility. Test result should not
1565
unnecessarily differ from enabled facility.
1569
Facility requires SUPER privilege. Sync points could be inside
1570
global mutexes (e.g. LOCK_open). Waiting there forever would
1571
stall the whole server.
1573
DBUG_RETURN(check_global_access(thd, SUPER_ACL));
1578
Set the system variable 'debug_sync'.
1580
@param[in] thd thread handle
1581
@param[in] var set variable request
1584
@retval FALSE ok, variable is set
1585
@retval TRUE error, variable could not be set
1588
"Setting" of the system variable 'debug_sync' does not mean to
1589
assign a value to it as usual. Instead a debug sync action is parsed
1590
from the input string and stored apart from the variable value.
1593
For efficiency reasons, the action string parser places '\0'
1594
terminators in the string. So we need to take a copy here.
1597
bool sys_var_debug_sync::update(THD *thd, set_var *var)
1602
DBUG_ENTER("sys_var_debug_sync::update");
1606
Depending on the value type (string literal, user variable, ...)
1607
val_buf receives a copy of the value or not. But we always need
1608
a copy. So we take a copy, if it is not done by val_str().
1609
If val_str() puts a copy into val_buf, then it returns &val_buf,
1610
otherwise it returns a pointer to the string object that we need
1613
val_ptr= var ? var->value->val_str(&val_buf) : &val_buf;
1614
if (val_ptr != &val_buf)
1616
val_buf.copy(*val_ptr);
1618
val_str= val_buf.c_ptr();
1619
DBUG_PRINT("debug_sync", ("set action: '%s'", val_str));
1622
debug_sync_eval_action() places '\0' in the string, which itself
1623
must be '\0' terminated.
1625
DBUG_RETURN(opt_debug_sync_timeout ?
1626
debug_sync_eval_action(thd, val_str) :
1632
Retrieve the value of the system variable 'debug_sync'.
1634
@param[in] thd thread handle
1635
@param[in] type variable type, unused
1636
@param[in] base variable base, unused
1639
@retval != NULL ok, string pointer
1640
@retval NULL memory allocation error
1643
The value of the system variable 'debug_sync' reflects if
1644
the facility is enabled ("ON") or disabled (default, "OFF").
1646
When "ON", the current signal is added.
1649
uchar *sys_var_debug_sync::value_ptr(THD *thd,
1650
enum_var_type type __attribute__((unused)),
1651
LEX_STRING *base __attribute__((unused)))
1654
DBUG_ENTER("sys_var_debug_sync::value_ptr");
1657
if (opt_debug_sync_timeout)
1659
static char on[]= "ON - current signal: '";
1661
// Ensure exclusive access to debug_sync_global.ds_signal
1662
pthread_mutex_lock(&debug_sync_global.ds_mutex);
1664
size_t lgt= (sizeof(on) /* includes '\0' */ +
1665
debug_sync_global.ds_signal.length() + 1 /* for '\'' */);
1669
if ((value= (char*) alloc_root(thd->mem_root, lgt)))
1671
vend= value + lgt - 1; /* reserve space for '\0'. */
1672
vptr= debug_sync_bmove_len(value, vend, STRING_WITH_LEN(on));
1673
vptr= debug_sync_bmove_len(vptr, vend, debug_sync_global.ds_signal.ptr(),
1674
debug_sync_global.ds_signal.length());
1677
*vptr= '\0'; /* We have one byte reserved for the worst case. */
1679
pthread_mutex_unlock(&debug_sync_global.ds_mutex);
1683
/* purecov: begin tested */
1684
value= strmake_root(thd->mem_root, STRING_WITH_LEN("OFF"));
1688
DBUG_RETURN((uchar*) value);
1693
Execute requested action at a synchronization point.
1695
@param[in] thd thread handle
1696
@param[in] action action to be executed
1699
This is to be called only if activation count > 0.
1702
static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
1704
IF_DBUG(const char *dsp_name= action->sync_point.c_ptr());
1705
IF_DBUG(const char *sig_emit= action->signal.c_ptr());
1706
IF_DBUG(const char *sig_wait= action->wait_for.c_ptr());
1707
DBUG_ENTER("debug_sync_execute");
1709
DBUG_ASSERT(action);
1710
DBUG_PRINT("debug_sync",
1711
("sync_point: '%s' activation_count: %lu hit_limit: %lu "
1712
"execute: %lu timeout: %lu signal: '%s' wait_for: '%s'",
1713
dsp_name, action->activation_count, action->hit_limit,
1714
action->execute, action->timeout, sig_emit, sig_wait));
1716
DBUG_ASSERT(action->activation_count);
1717
action->activation_count--;
1719
if (action->execute)
1721
const char *old_proc_info;
1726
If we will be going to wait, set proc_info for the PROCESSLIST table.
1727
Do this before emitting the signal, so other threads can see it
1728
if they awake before we enter_cond() below.
1730
if (action->wait_for.length())
1732
st_debug_sync_control *ds_control= thd->debug_sync_control;
1733
strxnmov(ds_control->ds_proc_info, sizeof(ds_control->ds_proc_info)-1,
1734
"debug sync point: ", action->sync_point.c_ptr(), NullS);
1735
old_proc_info= thd->proc_info;
1736
thd_proc_info(thd, ds_control->ds_proc_info);
1740
Take mutex to ensure that only one thread access
1741
debug_sync_global.ds_signal at a time. Need to take mutex for
1742
read access too, to create a memory barrier in order to avoid that
1743
threads just reads an old cached version of the signal.
1745
pthread_mutex_lock(&debug_sync_global.ds_mutex);
1747
if (action->signal.length())
1749
/* Copy the signal to the global variable. */
1750
if (debug_sync_global.ds_signal.copy(action->signal))
1753
Error is reported by my_malloc().
1754
We must disable the facility. We have no way to return an error.
1756
debug_sync_emergency_disable(); /* purecov: tested */
1758
/* Wake threads waiting in a sync point. */
1759
pthread_cond_broadcast(&debug_sync_global.ds_cond);
1760
DBUG_PRINT("debug_sync_exec", ("signal '%s' at: '%s'",
1761
sig_emit, dsp_name));
1762
} /* end if (action->signal.length()) */
1764
if (action->wait_for.length())
1766
pthread_mutex_t *old_mutex;
1767
pthread_cond_t *old_cond;
1769
struct timespec abstime;
1772
We don't use enter_cond()/exit_cond(). They do not save old
1773
mutex and cond. This would prohibit the use of DEBUG_SYNC
1774
between other places of enter_cond() and exit_cond().
1776
old_mutex= thd->mysys_var->current_mutex;
1777
old_cond= thd->mysys_var->current_cond;
1778
thd->mysys_var->current_mutex= &debug_sync_global.ds_mutex;
1779
thd->mysys_var->current_cond= &debug_sync_global.ds_cond;
1781
set_timespec(abstime, action->timeout);
1782
DBUG_EXECUTE("debug_sync_exec", {
1783
/* Functions as DBUG_PRINT args can change keyword and line nr. */
1784
const char *sig_glob= debug_sync_global.ds_signal.c_ptr();
1785
DBUG_PRINT("debug_sync_exec",
1786
("wait for '%s' at: '%s' curr: '%s'",
1787
sig_wait, dsp_name, sig_glob));});
1790
Wait until global signal string matches the wait_for string.
1791
Interrupt when thread or query is killed or facility disabled.
1792
The facility can become disabled when some thread cannot get
1793
the required dynamic memory allocated.
1795
while (stringcmp(&debug_sync_global.ds_signal, &action->wait_for) &&
1796
!thd->killed && opt_debug_sync_timeout)
1798
error= pthread_cond_timedwait(&debug_sync_global.ds_cond,
1799
&debug_sync_global.ds_mutex,
1801
DBUG_EXECUTE("debug_sync", {
1802
/* Functions as DBUG_PRINT args can change keyword and line nr. */
1803
const char *sig_glob= debug_sync_global.ds_signal.c_ptr();
1804
DBUG_PRINT("debug_sync",
1805
("awoke from %s global: %s error: %d",
1806
sig_wait, sig_glob, error));});
1807
if (error == ETIMEDOUT || error == ETIME)
1809
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
1810
ER_DEBUG_SYNC_TIMEOUT, ER(ER_DEBUG_SYNC_TIMEOUT));
1815
DBUG_EXECUTE("debug_sync_exec",
1817
DBUG_PRINT("debug_sync_exec",
1818
("killed %d from '%s' at: '%s'",
1819
thd->killed, sig_wait, dsp_name));
1821
DBUG_PRINT("debug_sync_exec",
1822
("%s from '%s' at: '%s'",
1823
error ? "timeout" : "resume",
1824
sig_wait, dsp_name)););
1827
We don't use enter_cond()/exit_cond(). They do not save old
1828
mutex and cond. This would prohibit the use of DEBUG_SYNC
1829
between other places of enter_cond() and exit_cond(). The
1830
protected mutex must always unlocked _before_ mysys_var->mutex
1831
is locked. (See comment in THD::exit_cond().)
1833
pthread_mutex_unlock(&debug_sync_global.ds_mutex);
1834
pthread_mutex_lock(&thd->mysys_var->mutex);
1835
thd->mysys_var->current_mutex= old_mutex;
1836
thd->mysys_var->current_cond= old_cond;
1837
thd_proc_info(thd, old_proc_info);
1838
pthread_mutex_unlock(&thd->mysys_var->mutex);
1843
/* In case we don't wait, we just release the mutex. */
1844
pthread_mutex_unlock(&debug_sync_global.ds_mutex);
1845
} /* end if (action->wait_for.length()) */
1847
} /* end if (action->execute) */
1849
/* hit_limit is zero for infinite. Don't decrement unconditionally. */
1850
if (action->hit_limit)
1852
if (!--action->hit_limit)
1854
thd->killed= THD::KILL_QUERY;
1855
my_error(ER_DEBUG_SYNC_HIT_LIMIT, MYF(0));
1857
DBUG_PRINT("debug_sync_exec", ("hit_limit: %lu at: '%s'",
1858
action->hit_limit, dsp_name));
1866
Execute requested action at a synchronization point.
1868
@param[in] thd thread handle
1869
@param[in] sync_point_name name of synchronization point
1870
@param[in] name_len length of sync point name
1873
void debug_sync(THD *thd, const char *sync_point_name, size_t name_len)
1875
st_debug_sync_control *ds_control= thd->debug_sync_control;
1876
st_debug_sync_action *action;
1877
DBUG_ENTER("debug_sync");
1879
DBUG_ASSERT(sync_point_name);
1880
DBUG_ASSERT(name_len);
1881
DBUG_ASSERT(ds_control);
1882
DBUG_PRINT("debug_sync_point", ("hit: '%s'", sync_point_name));
1885
ds_control->dsp_hits++;
1887
if (ds_control->ds_active &&
1888
(action= debug_sync_find(ds_control->ds_action, ds_control->ds_active,
1889
sync_point_name, name_len)) &&
1890
action->activation_count)
1892
/* Sync point is active (action exists). */
1893
debug_sync_execute(thd, action);
1896
ds_control->dsp_executed++;
1898
/* If action became inactive, remove it to shrink the search array. */
1899
if (!action->activation_count)
1900
debug_sync_remove_action(ds_control, action);
1906
#endif /* defined(ENABLED_DEBUG_SYNC) */