1 /* Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software Foundation,
21 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22
23 /**
24 == Debug Sync Facility ==
25
26 The Debug Sync Facility allows placement of synchronization points in
27 the server code by using the DEBUG_SYNC macro:
28
29 open_tables(...)
30
31 DEBUG_SYNC(thd, "after_open_tables");
32
33 lock_tables(...)
34
35 When activated, a sync point can
36
37 - Emit a signal and/or
38 - Wait for a signal
39
40 Nomenclature:
41
42 - signal: An event identified by a name that a signal
43 thread uses to notify the wait thread that
44 waits on this event. When the signal thread
45 notifies the wait thread, the signal name
46 is copied into global list and the wait thread
47 is signalled to wake up and proceed with further
48 processing.
49
50 - emit a signal: Signal thread wakes up wait thread or multiple
51 wait threads that shall wait for the signal identified
52 by a signal name. This signal thread copies the signal
53 name into a global list and broadcasts the event which
54 wakes the threads that wait for this event.
55
56 - wait for a signal: Wait on a event indentified by the signal name until
57 the signal thread signals the event.
58
59 By default, all sync points are inactive. They do nothing (except to
60 burn a couple of CPU cycles for checking if they are active).
61
62 A sync point becomes active when an action is requested for it.
63 To do so, put a line like this in the test case file:
64
65 SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
66
67 This activates the sync point 'after_open_tables'. It requests it to
68 emit the signal 'opened' and wait for another thread to emit the signal
69 'flushed' when the thread's execution runs through the sync point.
70
71 For every sync point there can be one action per thread only. Every
72 thread can request multiple actions, but only one per sync point. In
73 other words, a thread can activate multiple sync points.
74
75 Here is an example how to activate and use the sync points:
76
77 --connection conn1
78 SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
79 send INSERT INTO t1 VALUES(1);
80 --connection conn2
81 SET DEBUG_SYNC= 'now WAIT_FOR opened';
82 SET DEBUG_SYNC= 'after_abort_locks SIGNAL flushed';
83 FLUSH TABLE t1;
84
85 When conn1 runs through the INSERT statement, it hits the sync point
86 'after_open_tables'. It notices that it is active and executes its
87 action. It emits the signal 'opened' and waits for another thread to
88 emit the signal 'flushed'.
89
90 conn2 waits immediately at the special sync point 'now' for another
91 thread to emit the 'opened' signal.
92
93 If conn1 signals 'opened' before conn2 reaches 'now', conn2 will find
94 the 'opened' signal. The wait thread shall not wait in this case.
95
96 When conn2 reaches 'after_abort_locks', it signals 'flushed', which lets
97 conn1 awake and clears the 'flushed' signal from the global list. In case
98 the 'flushed' signal is to be notified to multiple wait threads, an attribute
99 NO_CLEAR_EVENT need to be specified with the WAIT_FOR in addition to signal
100 the name as:
101 SET DEBUG_SYNC= 'WAIT_FOR flushed NO_CLEAR_EVENT';
102 It is up to the user to ensure once when all the wait threads have processed
103 the 'flushed' signal to clear/deactivate the signal using the RESET action
104 of DEBUG_SYNC accordingly.
105
106
107 Normally the activation of a sync point is cleared when it has been
108 executed. Sometimes it is necessary to keep the sync point active for
109 another execution. You can add an execute count to the action:
110
111 SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 3';
112
113 This sets the signal point's activation counter to 3. Each execution
114 decrements the counter. After the third execution the sync point
115 becomes inactive.
116
117 One of the primary goals of this facility is to eliminate sleeps from
118 the test suite. In most cases it should be possible to rewrite test
119 cases so that they do not need to sleep. (But this facility cannot
120 synchronize multiple processes.) However, to support test development,
121 and as a last resort, sync point waiting times out. There is a default
122 timeout, but it can be overridden:
123
124 SET DEBUG_SYNC= 'name WAIT_FOR sig TIMEOUT 10 EXECUTE 2';
125
126 TIMEOUT 0 is special: If the signal is not present, the wait times out
127 immediately.
128
129 When a wait timed out (even on TIMEOUT 0), a warning is generated so
130 that it shows up in the test result.
131
132 You can throw an error message and kill the query when a synchronization
133 point is hit a certain number of times:
134
135 SET DEBUG_SYNC= 'name HIT_LIMIT 3';
136
137 Or combine it with signal and/or wait:
138
139 SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 2 HIT_LIMIT 3';
140
141 Here the first two hits emit the signal, the third hit returns the error
142 message and kills the query.
143
144 For cases where you are not sure that an action is taken and thus
145 cleared in any case, you can force to clear (deactivate) a sync point:
146
147 SET DEBUG_SYNC= 'name CLEAR';
148
149 If you want to clear all actions and clear the global signal, use:
150
151 SET DEBUG_SYNC= 'RESET';
152
153 This is the only way to reset the global signal to an empty string.
154
155 For testing of the facility itself you can execute a sync point just
156 as if it had been hit:
157
158 SET DEBUG_SYNC= 'name TEST';
159
160
161 === Formal Syntax ===
162
163 The string to "assign" to the DEBUG_SYNC variable can contain:
164
165 {RESET |
166 <sync point name> TEST |
167 <sync point name> CLEAR |
168 <sync point name> {{SIGNAL <signal name> |
169 WAIT_FOR <signal name> [TIMEOUT <seconds>]
170 [NO_CLEAR_EVENT]}
171 [EXECUTE <count>] &| HIT_LIMIT <count>}
172
173 Here '&|' means 'and/or'. This means that one of the sections
174 separated by '&|' must be present or both of them.
175
176
177 === Activation/Deactivation ===
178
179 The facility is an optional part of the MySQL server.
180 It is enabled in a debug server by default.
181
182 The Debug Sync Facility, when compiled in, is disabled by default. It
183 can be enabled by a mysqld command line option:
184
185 --debug-sync-timeout[=default_wait_timeout_value_in_seconds]
186
187 'default_wait_timeout_value_in_seconds' is the default timeout for the
188 WAIT_FOR action. If set to zero, the facility stays disabled.
189
190 The facility is enabled by default in the test suite, but can be
191 disabled with:
192
193 mysql-test-run.pl ... --debug-sync-timeout=0 ...
194
195 Likewise the default wait timeout can be set:
196
197 mysql-test-run.pl ... --debug-sync-timeout=10 ...
198
199 The command line option influences the readable value of the system
200 variable 'debug_sync'.
201
202 * If the facility is not compiled in, the system variable does not exist.
203
204 * If --debug-sync-timeout=0 the value of the variable reads as "OFF".
205
206 * Otherwise the value reads as "ON - current signal: " followed by the
207 current signal string, which can be empty.
208
209 The readable variable value is the same, regardless if read as global
210 or session value.
211
212 Setting the 'debug-sync' system variable requires 'SUPER' privilege.
213 You can never read back the string that you assigned to the variable,
214 unless you assign the value that the variable does already have. But
215 that would give a parse error. A syntactically correct string is
216 parsed into a debug sync action and stored apart from the variable value.
217
218
219 === Implementation ===
220
221 Pseudo code for a sync point:
222
223 #define DEBUG_SYNC(thd, sync_point_name)
224 if (unlikely(opt_debug_sync_timeout))
225 debug_sync(thd, STRING_WITH_LEN(sync_point_name))
226
227 The sync point performs a binary search in a sorted array of actions
228 for this thread.
229
230 The SET DEBUG_SYNC statement adds a requested action to the array or
231 overwrites an existing action for the same sync point. When it adds a
232 new action, the array is sorted again.
233
234
235 === A typical synchronization pattern ===
236
237 There are quite a few places in MySQL, where we use a synchronization
238 pattern like this:
239
240 mysql_mutex_lock(&mutex);
241 thd->enter_cond(&condition_variable, &mutex, new_message);
242 #if defined(ENABLE_DEBUG_SYNC)
243 if (!thd->killed && !end_of_wait_condition)
244 DEBUG_SYNC(thd, "sync_point_name");
245 #endif
246 while (!thd->killed && !end_of_wait_condition)
247 mysql_cond_wait(&condition_variable, &mutex);
248 thd->exit_cond(old_message);
249
250 Here some explanations:
251
252 thd->enter_cond() is used to register the condition variable and the
253 mutex in thd->mysys_var. This is done to allow the thread to be
254 interrupted (killed) from its sleep. Another thread can find the
255 condition variable to signal and mutex to use for synchronization in
256 this thread's THD::mysys_var.
257
258 thd->enter_cond() requires the mutex to be acquired in advance.
259
260 thd->exit_cond() unregisters the condition variable and mutex and
261 releases the mutex.
262
263 If you want to have a Debug Sync point with the wait, please place it
264 behind enter_cond(). Only then you can safely decide, if the wait will
265 be taken. Also you will have THD::proc_info correct when the sync
266 point emits a signal. DEBUG_SYNC sets its own proc_info, but restores
267 the previous one before releasing its internal mutex. As soon as
268 another thread sees the signal, it does also see the proc_info from
269 before entering the sync point. In this case it will be "new_message",
270 which is associated with the wait that is to be synchronized.
271
272 In the example above, the wait condition is repeated before the sync
273 point. This is done to skip the sync point, if no wait takes place.
274 The sync point is before the loop (not inside the loop) to have it hit
275 once only. It is possible that the condition variable is signaled
276 multiple times without the wait condition to be true.
277
278 A bit off-topic: At some places, the loop is taken around the whole
279 synchronization pattern:
280
281 while (!thd->killed && !end_of_wait_condition)
282 {
283 mysql_mutex_lock(&mutex);
284 thd->enter_cond(&condition_variable, &mutex, new_message);
285 if (!thd->killed [&& !end_of_wait_condition])
286 {
287 [DEBUG_SYNC(thd, "sync_point_name");]
288 mysql_cond_wait(&condition_variable, &mutex);
289 }
290 thd->exit_cond(old_message);
291 }
292
293 Note that it is important to repeat the test for thd->killed after
294 enter_cond(). Otherwise the killing thread may kill this thread after
295 it tested thd->killed in the loop condition and before it registered
296 the condition variable and mutex in enter_cond(). In this case, the
297 killing thread does not know that this thread is going to wait on a
298 condition variable. It would just set THD::killed. But if we would not
299 test it again, we would go asleep though we are killed. If the killing
300 thread would kill us when we are after the second test, but still
301 before sleeping, we hold the mutex, which is registered in mysys_var.
302 The killing thread would try to acquire the mutex before signaling
303 the condition variable. Since the mutex is only released implicitly in
304 mysql_cond_wait(), the signaling happens at the right place. We
305 have a safe synchronization.
306
307 === Co-work with the DBUG facility ===
308
309 When running the MySQL test suite with the --debug command line
310 option, the Debug Sync Facility writes trace messages to the DBUG
311 trace. The following shell commands proved very useful in extracting
312 relevant information:
313
314 egrep 'query:|debug_sync_exec:' mysql-test/var/log/mysqld.1.trace
315
316 It shows all executed SQL statements and all actions executed by
317 synchronization points.
318
319 Sometimes it is also useful to see, which synchronization points have
320 been run through (hit) with or without executing actions. Then add
321 "|debug_sync_point:" to the egrep pattern.
322
323 === Further reading ===
324
325 For a discussion of other methods to synchronize threads see
326 http://forge.mysql.com/wiki/MySQL_Internals_Test_Synchronization
327
328 For complete syntax tests, functional tests, and examples see the test
329 case debug_sync.test.
330
331 See also worklog entry WL#4259 - Test Synchronization Facility
332 */
333
334 #include "debug_sync.h"
335
336 #if defined(ENABLED_DEBUG_SYNC)
337
338 /*
339 Due to weaknesses in our include files, we need to include
340 sql_priv.h here. To have THD declared, we need to include
341 sql_class.h. This includes log_event.h, which in turn requires
342 declarations from sql_priv.h (e.g. OPTION_AUTO_IS_NULL).
343 sql_priv.h includes almost everything, so is sufficient here.
344 */
345 #include "sql_priv.h"
346 #include "sql_parse.h"
347
348 #include <set>
349 #include <string>
350
351 using std::max;
352 using std::min;
353
354 /*
355 Action to perform at a synchronization point.
356 NOTE: This structure is moved around in memory by realloc(), qsort(),
357 and memmove(). Do not add objects with non-trivial constuctors
358 or destructors, which might prevent moving of this structure
359 with these functions.
360 */
361 struct st_debug_sync_action
362 {
363 ulong activation_count; /* max(hit_limit, execute) */
364 ulong hit_limit; /* hits before kill query */
365 ulong execute; /* executes before self-clear */
366 ulong timeout; /* wait_for timeout */
367 String signal; /* signal to emit */
368 String wait_for; /* signal to wait for */
369 String sync_point; /* sync point name */
370 bool need_sort; /* if new action, array needs sort */
371 bool clear_event; /* do not clear signal if false */
372 };
373
374 /* Debug sync control. Referenced by THD. */
375 struct st_debug_sync_control
376 {
377 st_debug_sync_action *ds_action; /* array of actions */
378 uint ds_active; /* # active actions */
379 uint ds_allocated; /* # allocated actions */
380 ulonglong dsp_hits; /* statistics */
381 ulonglong dsp_executed; /* statistics */
382 ulonglong dsp_max_active; /* statistics */
383 /*
384 thd->proc_info points at unsynchronized memory.
385 It must not go away as long as the thread exists.
386 */
387 char ds_proc_info[80]; /* proc_info string */
388 };
389
390 typedef std::set<std::string> signal_event_set;
391
392 /**
393 Definitions for the debug sync facility.
394 1. Global set of signal names which are signalled.
395 2. Global condition variable for signaling and waiting.
396 3. Global mutex to synchronize access to the above.
397 */
398 struct st_debug_sync_globals
399 {
400 signal_event_set ds_signal_set; /* list of signals signalled */
401 mysql_cond_t ds_cond; /* condition variable */
402 mysql_mutex_t ds_mutex; /* mutex variable */
403 ulonglong dsp_hits; /* statistics */
404 ulonglong dsp_executed; /* statistics */
405 ulonglong dsp_max_active; /* statistics */
406
st_debug_sync_globalsst_debug_sync_globals407 st_debug_sync_globals() : dsp_hits(0), dsp_executed(0), dsp_max_active(0) {}
408 private:
409 // Not implemented:
410 st_debug_sync_globals(const st_debug_sync_globals&);
411 st_debug_sync_globals &operator=(const st_debug_sync_globals&);
412 };
413 static st_debug_sync_globals debug_sync_global; /* All globals in one object */
414
415 /**
416 Callback pointer for C files.
417 */
418 extern "C" void (*debug_sync_C_callback_ptr)(const char *, size_t);
419
420 /**
421 Callbacks from C files.
422 */
423 C_MODE_START
424 static void debug_sync_C_callback(const char *, size_t);
425 static int debug_sync_qsort_cmp(const void *, const void *);
426 C_MODE_END
427
428 /**
429 Callback for debug sync, to be used by C files. See thr_lock.c for example.
430
431 @description
432
433 We cannot place a sync point directly in C files (like those in mysys or
434 certain storage engines written mostly in C like MyISAM or Maria). Because
435 they are C code and do not include sql_priv.h. So they do not know the
436 macro DEBUG_SYNC(thd, sync_point_name). The macro needs a 'thd' argument.
437 Hence it cannot be used in files outside of the sql/ directory.
438
439 The workaround is to call back simple functions like this one from
440 non-sql/ files.
441
442 We want to allow modules like thr_lock to be used without sql/ and
443 especially without Debug Sync. So we cannot just do a simple call
444 of the callback function. Instead we provide a global pointer in
445 the other file, which is to be set to the callback by Debug Sync.
446 If the pointer is not set, no call back will be done. If Debug
447 Sync sets the pointer to a callback function like this one, it will
448 be called. That way thr_lock.c does not have an undefined reference
449 to Debug Sync and can be used without it. Debug Sync, in contrast,
450 has an undefined reference to that pointer and thus requires
451 thr_lock to be linked too. But this is not a problem as it is part
452 of the MySQL server anyway.
453
454 @note
455 The callback pointer in C files is set only if debug sync is
456 initialized. And this is done only if opt_debug_sync_timeout is set.
457 */
458
debug_sync_C_callback(const char * sync_point_name,size_t name_len)459 static void debug_sync_C_callback(const char *sync_point_name,
460 size_t name_len)
461 {
462 if (unlikely(opt_debug_sync_timeout))
463 debug_sync(current_thd, sync_point_name, name_len);
464 }
465
466 #ifdef HAVE_PSI_INTERFACE
467 static PSI_mutex_key key_debug_sync_globals_ds_mutex;
468
469 static PSI_mutex_info all_debug_sync_mutexes[]=
470 {
471 { &key_debug_sync_globals_ds_mutex, "DEBUG_SYNC::mutex", PSI_FLAG_GLOBAL}
472 };
473
474 static PSI_cond_key key_debug_sync_globals_ds_cond;
475
476 static PSI_cond_info all_debug_sync_conds[]=
477 {
478 { &key_debug_sync_globals_ds_cond, "DEBUG_SYNC::cond", PSI_FLAG_GLOBAL}
479 };
480
init_debug_sync_psi_keys(void)481 static void init_debug_sync_psi_keys(void)
482 {
483 const char* category= "sql";
484 int count;
485
486 count= array_elements(all_debug_sync_mutexes);
487 mysql_mutex_register(category, all_debug_sync_mutexes, count);
488
489 count= array_elements(all_debug_sync_conds);
490 mysql_cond_register(category, all_debug_sync_conds, count);
491 }
492 #endif /* HAVE_PSI_INTERFACE */
493
494
495 /**
496 Initialize the debug sync facility at server start.
497
498 @return status
499 @retval 0 ok
500 @retval != 0 error
501 */
502
debug_sync_init(void)503 int debug_sync_init(void)
504 {
505 DBUG_ENTER("debug_sync_init");
506
507 #ifdef HAVE_PSI_INTERFACE
508 init_debug_sync_psi_keys();
509 #endif
510
511 if (opt_debug_sync_timeout)
512 {
513 int rc;
514
515 /* Initialize the global variables. */
516 if ((rc= mysql_cond_init(key_debug_sync_globals_ds_cond,
517 &debug_sync_global.ds_cond, NULL)) ||
518 (rc= mysql_mutex_init(key_debug_sync_globals_ds_mutex,
519 &debug_sync_global.ds_mutex,
520 MY_MUTEX_INIT_FAST)))
521 DBUG_RETURN(rc); /* purecov: inspected */
522
523 /* Set the call back pointer in C files. */
524 debug_sync_C_callback_ptr= debug_sync_C_callback;
525 }
526
527 DBUG_RETURN(0);
528 }
529
530
531 /**
532 End the debug sync facility.
533
534 @description
535 This is called at server shutdown or after a thread initialization error.
536 */
537
debug_sync_end(void)538 void debug_sync_end(void)
539 {
540 DBUG_ENTER("debug_sync_end");
541
542 /* End the facility only if it had been initialized. */
543 if (debug_sync_C_callback_ptr)
544 {
545 /* Clear the call back pointer in C files. */
546 debug_sync_C_callback_ptr= NULL;
547
548 /* Destroy the global variables. */
549 debug_sync_global.ds_signal_set.clear();
550 mysql_cond_destroy(&debug_sync_global.ds_cond);
551 mysql_mutex_destroy(&debug_sync_global.ds_mutex);
552
553 /* Print statistics. */
554 {
555 char llbuff[22];
556 sql_print_information("Debug sync points hit: %22s",
557 llstr(debug_sync_global.dsp_hits, llbuff));
558 sql_print_information("Debug sync points executed: %22s",
559 llstr(debug_sync_global.dsp_executed, llbuff));
560 sql_print_information("Debug sync points max active per thread: %22s",
561 llstr(debug_sync_global.dsp_max_active, llbuff));
562 }
563 }
564
565 DBUG_VOID_RETURN;
566 }
567
568
569 /* purecov: begin tested */
570
571 /**
572 Disable the facility after lack of memory if no error can be returned.
573
574 @note
575 Do not end the facility here because the global variables can
576 be in use by other threads.
577 */
578
debug_sync_emergency_disable(void)579 static void debug_sync_emergency_disable(void)
580 {
581 DBUG_ENTER("debug_sync_emergency_disable");
582
583 opt_debug_sync_timeout= 0;
584
585 DBUG_PRINT("debug_sync",
586 ("Debug Sync Facility disabled due to lack of memory."));
587 sql_print_error("Debug Sync Facility disabled due to lack of memory.");
588
589 DBUG_VOID_RETURN;
590 }
591
592 /* purecov: end */
593
594
595 /**
596 Initialize the debug sync facility at thread start.
597
598 @param[in] thd thread handle
599 */
600
debug_sync_init_thread(THD * thd)601 void debug_sync_init_thread(THD *thd)
602 {
603 DBUG_ENTER("debug_sync_init_thread");
604 DBUG_ASSERT(thd);
605
606 if (opt_debug_sync_timeout)
607 {
608 thd->debug_sync_control= (st_debug_sync_control*)
609 my_malloc(sizeof(st_debug_sync_control), MYF(MY_WME | MY_ZEROFILL));
610 if (!thd->debug_sync_control)
611 {
612 /*
613 Error is reported by my_malloc().
614 We must disable the facility. We have no way to return an error.
615 */
616 debug_sync_emergency_disable(); /* purecov: tested */
617 }
618 }
619
620 DBUG_VOID_RETURN;
621 }
622
623
624 /**
625 End the debug sync facility at thread end.
626
627 @param[in] thd thread handle
628 */
629
debug_sync_end_thread(THD * thd)630 void debug_sync_end_thread(THD *thd)
631 {
632 DBUG_ENTER("debug_sync_end_thread");
633 DBUG_ASSERT(thd);
634
635 if (thd->debug_sync_control)
636 {
637 st_debug_sync_control *ds_control= thd->debug_sync_control;
638
639 /*
640 This synchronization point can be used to synchronize on thread end.
641 This is the latest point in a THD's life, where this can be done.
642 */
643 DEBUG_SYNC(thd, "thread_end");
644
645 if (ds_control->ds_action)
646 {
647 st_debug_sync_action *action= ds_control->ds_action;
648 st_debug_sync_action *action_end= action + ds_control->ds_allocated;
649 for (; action < action_end; action++)
650 {
651 action->signal.free();
652 action->wait_for.free();
653 action->sync_point.free();
654 }
655 my_free(ds_control->ds_action);
656 }
657
658 /* Statistics. */
659 mysql_mutex_lock(&debug_sync_global.ds_mutex);
660 debug_sync_global.dsp_hits+= ds_control->dsp_hits;
661 debug_sync_global.dsp_executed+= ds_control->dsp_executed;
662 if (debug_sync_global.dsp_max_active < ds_control->dsp_max_active)
663 debug_sync_global.dsp_max_active= ds_control->dsp_max_active;
664 mysql_mutex_unlock(&debug_sync_global.ds_mutex);
665
666 my_free(ds_control);
667 thd->debug_sync_control= NULL;
668 }
669
670 DBUG_VOID_RETURN;
671 }
672
673
674 /**
675 Move a string by length.
676
677 @param[out] to buffer for the resulting string
678 @param[in] to_end end of buffer
679 @param[in] from source string
680 @param[in] length number of bytes to copy
681
682 @return pointer to end of copied string
683 */
684
debug_sync_bmove_len(char * to,char * to_end,const char * from,size_t length)685 static char *debug_sync_bmove_len(char *to, char *to_end,
686 const char *from, size_t length)
687 {
688 DBUG_ASSERT(to);
689 DBUG_ASSERT(to_end);
690 DBUG_ASSERT(!length || from);
691 set_if_smaller(length, (size_t) (to_end - to));
692 memcpy(to, from, length);
693 return (to + length);
694 }
695
696
697 #if !defined(DBUG_OFF)
698
699 /**
700 Create a string that describes an action.
701
702 @param[out] result buffer for the resulting string
703 @param[in] size size of result buffer
704 @param[in] action action to describe
705 */
706
debug_sync_action_string(char * result,uint size,st_debug_sync_action * action)707 static void debug_sync_action_string(char *result, uint size,
708 st_debug_sync_action *action)
709 {
710 char *wtxt= result;
711 char *wend= wtxt + size - 1; /* Allow emergency '\0'. */
712 DBUG_ASSERT(result);
713 DBUG_ASSERT(action);
714
715 /* If an execute count is present, signal or wait_for are needed too. */
716 DBUG_ASSERT(!action->execute ||
717 action->signal.length() || action->wait_for.length());
718
719 if (action->execute)
720 {
721 if (action->signal.length())
722 {
723 wtxt= debug_sync_bmove_len(wtxt, wend, STRING_WITH_LEN("SIGNAL "));
724 wtxt= debug_sync_bmove_len(wtxt, wend, action->signal.ptr(),
725 action->signal.length());
726 }
727 if (action->wait_for.length())
728 {
729 if ((wtxt == result) && (wtxt < wend))
730 *(wtxt++)= ' ';
731 wtxt= debug_sync_bmove_len(wtxt, wend, STRING_WITH_LEN(" WAIT_FOR "));
732 wtxt= debug_sync_bmove_len(wtxt, wend, action->wait_for.ptr(),
733 action->wait_for.length());
734
735 if (action->timeout != opt_debug_sync_timeout)
736 {
737 wtxt+= my_snprintf(wtxt, wend - wtxt, " TIMEOUT %lu", action->timeout);
738 }
739 }
740 if (action->execute != 1)
741 {
742 wtxt+= my_snprintf(wtxt, wend - wtxt, " EXECUTE %lu", action->execute);
743 }
744 }
745 if (action->hit_limit)
746 {
747 wtxt+= my_snprintf(wtxt, wend - wtxt, "%sHIT_LIMIT %lu",
748 (wtxt == result) ? "" : " ", action->hit_limit);
749 }
750
751 /*
752 If (wtxt == wend) string may not be terminated.
753 There is one byte left for an emergency termination.
754 */
755 *wtxt= '\0';
756 }
757
758
759 /**
760 Print actions.
761
762 @param[in] thd thread handle
763 */
764
debug_sync_print_actions(THD * thd)765 static void debug_sync_print_actions(THD *thd)
766 {
767 st_debug_sync_control *ds_control= thd->debug_sync_control;
768 uint idx;
769 DBUG_ENTER("debug_sync_print_actions");
770 DBUG_ASSERT(thd);
771
772 if (!ds_control)
773 DBUG_VOID_RETURN;
774
775 for (idx= 0; idx < ds_control->ds_active; idx++)
776 {
777 const char *dsp_name= ds_control->ds_action[idx].sync_point.c_ptr();
778 char action_string[256];
779
780 debug_sync_action_string(action_string, sizeof(action_string),
781 ds_control->ds_action + idx);
782 DBUG_PRINT("debug_sync_list", ("%s %s", dsp_name, action_string));
783 }
784
785 DBUG_VOID_RETURN;
786 }
787
788 #endif /* !defined(DBUG_OFF) */
789
790
791 /**
792 Compare two actions by sync point name length, string.
793
794 @param[in] arg1 reference to action1
795 @param[in] arg2 reference to action2
796
797 @return difference
798 @retval == 0 length1/string1 is same as length2/string2
799 @retval < 0 length1/string1 is smaller
800 @retval > 0 length1/string1 is bigger
801 */
802
debug_sync_qsort_cmp(const void * arg1,const void * arg2)803 static int debug_sync_qsort_cmp(const void* arg1, const void* arg2)
804 {
805 st_debug_sync_action *action1= (st_debug_sync_action*) arg1;
806 st_debug_sync_action *action2= (st_debug_sync_action*) arg2;
807 int diff;
808 DBUG_ASSERT(action1);
809 DBUG_ASSERT(action2);
810
811 if (!(diff= action1->sync_point.length() - action2->sync_point.length()))
812 diff= memcmp(action1->sync_point.ptr(), action2->sync_point.ptr(),
813 action1->sync_point.length());
814
815 return diff;
816 }
817
818
819 /**
820 Find a debug sync action.
821
822 @param[in] actionarr array of debug sync actions
823 @param[in] quantity number of actions in array
824 @param[in] dsp_name name of debug sync point to find
825 @param[in] name_len length of name of debug sync point
826
827 @return action
828 @retval != NULL found sync point in array
829 @retval NULL not found
830
831 @description
832 Binary search. Array needs to be sorted by length, sync point name.
833 */
834
debug_sync_find(st_debug_sync_action * actionarr,int quantity,const char * dsp_name,uint name_len)835 static st_debug_sync_action *debug_sync_find(st_debug_sync_action *actionarr,
836 int quantity,
837 const char *dsp_name,
838 uint name_len)
839 {
840 st_debug_sync_action *action;
841 int low ;
842 int high ;
843 int mid ;
844 int diff ;
845 DBUG_ASSERT(actionarr);
846 DBUG_ASSERT(dsp_name);
847 DBUG_ASSERT(name_len);
848
849 low= 0;
850 high= quantity;
851
852 while (low < high)
853 {
854 mid= (low + high) / 2;
855 action= actionarr + mid;
856 if (!(diff= name_len - action->sync_point.length()) &&
857 !(diff= memcmp(dsp_name, action->sync_point.ptr(), name_len)))
858 return action;
859 if (diff > 0)
860 low= mid + 1;
861 else
862 high= mid - 1;
863 }
864
865 if (low < quantity)
866 {
867 action= actionarr + low;
868 if ((name_len == action->sync_point.length()) &&
869 !memcmp(dsp_name, action->sync_point.ptr(), name_len))
870 return action;
871 }
872
873 return NULL;
874 }
875
876
877 /**
878 Reset the debug sync facility.
879
880 @param[in] thd thread handle
881
882 @description
883 Remove all actions of this thread.
884 Clear the global signal.
885 */
886
debug_sync_reset(THD * thd)887 static void debug_sync_reset(THD *thd)
888 {
889 st_debug_sync_control *ds_control= thd->debug_sync_control;
890 DBUG_ENTER("debug_sync_reset");
891 DBUG_ASSERT(thd);
892 DBUG_ASSERT(ds_control);
893
894 /* Remove all actions of this thread. */
895 ds_control->ds_active= 0;
896
897 /* Clear the signals. */
898 mysql_mutex_lock(&debug_sync_global.ds_mutex);
899 debug_sync_global.ds_signal_set.clear();
900 mysql_mutex_unlock(&debug_sync_global.ds_mutex);
901
902 DBUG_VOID_RETURN;
903 }
904
905
906 /**
907 Remove a debug sync action.
908
909 @param[in] ds_control control object
910 @param[in] action action to be removed
911
912 @description
913 Removing an action mainly means to decrement the ds_active counter.
914 But if the action is between other active action in the array, then
915 the array needs to be shrinked. The active actions above the one to
916 be removed have to be moved down by one slot.
917 */
918
debug_sync_remove_action(st_debug_sync_control * ds_control,st_debug_sync_action * action)919 static void debug_sync_remove_action(st_debug_sync_control *ds_control,
920 st_debug_sync_action *action)
921 {
922 uint dsp_idx= action - ds_control->ds_action;
923 DBUG_ENTER("debug_sync_remove_action");
924 DBUG_ASSERT(ds_control);
925 DBUG_ASSERT(ds_control == current_thd->debug_sync_control);
926 DBUG_ASSERT(action);
927 DBUG_ASSERT(dsp_idx < ds_control->ds_active);
928
929 /* Decrement the number of currently active actions. */
930 ds_control->ds_active--;
931
932 /*
933 If this was not the last active action in the array, we need to
934 shift remaining active actions down to keep the array gap-free.
935 Otherwise binary search might fail or take longer than necessary at
936 least. Also new actions are always put to the end of the array.
937 */
938 if (ds_control->ds_active > dsp_idx)
939 {
940 /*
941 Do not make save_action an object of class st_debug_sync_action.
942 Its destructor would tamper with the String pointers.
943 */
944 uchar save_action[sizeof(st_debug_sync_action)];
945
946 /*
947 Copy the to-be-removed action object to temporary storage before
948 the shift copies the string pointers over. Do not use assignment
949 because it would use assignment operator methods for the Strings.
950 This would copy the strings. The shift below overwrite the string
951 pointers without freeing them first. By using memmove() we save
952 the pointers, which are overwritten by the shift.
953 */
954 memmove(save_action, action, sizeof(st_debug_sync_action));
955
956 /* Move actions down. */
957 memmove(static_cast<void*>(ds_control->ds_action + dsp_idx),
958 ds_control->ds_action + dsp_idx + 1,
959 (ds_control->ds_active - dsp_idx) *
960 sizeof(st_debug_sync_action));
961
962 /*
963 Copy back the saved action object to the now free array slot. This
964 replaces the double references of String pointers that have been
965 produced by the shift. Again do not use an assignment operator to
966 avoid string allocation/copy.
967 */
968 memmove(static_cast<void*>(ds_control->ds_action + ds_control->ds_active),
969 save_action, sizeof(st_debug_sync_action));
970 }
971
972 DBUG_VOID_RETURN;
973 }
974
975
976 /**
977 Get a debug sync action.
978
979 @param[in] thd thread handle
980 @param[in] dsp_name debug sync point name
981 @param[in] name_len length of sync point name
982
983 @return action
984 @retval != NULL ok
985 @retval NULL error
986
987 @description
988 Find the debug sync action for a debug sync point or make a new one.
989 */
990
debug_sync_get_action(THD * thd,const char * dsp_name,uint name_len)991 static st_debug_sync_action *debug_sync_get_action(THD *thd,
992 const char *dsp_name,
993 uint name_len)
994 {
995 st_debug_sync_control *ds_control= thd->debug_sync_control;
996 st_debug_sync_action *action;
997 DBUG_ENTER("debug_sync_get_action");
998 DBUG_ASSERT(thd);
999 DBUG_ASSERT(dsp_name);
1000 DBUG_ASSERT(name_len);
1001 DBUG_ASSERT(ds_control);
1002 DBUG_PRINT("debug_sync", ("sync_point: '%.*s'", (int) name_len, dsp_name));
1003 DBUG_PRINT("debug_sync", ("active: %u allocated: %u",
1004 ds_control->ds_active, ds_control->ds_allocated));
1005
1006 /* There cannot be more active actions than allocated. */
1007 DBUG_ASSERT(ds_control->ds_active <= ds_control->ds_allocated);
1008 /* If there are active actions, the action array must be present. */
1009 DBUG_ASSERT(!ds_control->ds_active || ds_control->ds_action);
1010
1011 /* Try to reuse existing action if there is one for this sync point. */
1012 if (ds_control->ds_active &&
1013 (action= debug_sync_find(ds_control->ds_action, ds_control->ds_active,
1014 dsp_name, name_len)))
1015 {
1016 /* Reuse an already active sync point action. */
1017 DBUG_ASSERT((uint)(action - ds_control->ds_action) < ds_control->ds_active);
1018 DBUG_PRINT("debug_sync", ("reuse action idx: %ld",
1019 (long) (action - ds_control->ds_action)));
1020 }
1021 else
1022 {
1023 /* Create a new action. */
1024 int dsp_idx= ds_control->ds_active++;
1025 set_if_bigger(ds_control->dsp_max_active, ds_control->ds_active);
1026 if (ds_control->ds_active > ds_control->ds_allocated)
1027 {
1028 uint new_alloc= ds_control->ds_active + 3;
1029 void *new_action= my_realloc(ds_control->ds_action,
1030 new_alloc * sizeof(st_debug_sync_action),
1031 MYF(MY_WME | MY_ALLOW_ZERO_PTR));
1032 if (!new_action)
1033 {
1034 /* Error is reported by my_malloc(). */
1035 goto err; /* purecov: tested */
1036 }
1037 ds_control->ds_action= (st_debug_sync_action*) new_action;
1038 ds_control->ds_allocated= new_alloc;
1039 /* Clear memory as we do not run string constructors here. */
1040 memset(static_cast<void*>(ds_control->ds_action + dsp_idx), 0,
1041 (new_alloc - dsp_idx) * sizeof(st_debug_sync_action));
1042 }
1043 DBUG_PRINT("debug_sync", ("added action idx: %u", dsp_idx));
1044 action= ds_control->ds_action + dsp_idx;
1045 if (action->sync_point.copy(dsp_name, name_len, system_charset_info))
1046 {
1047 /* Error is reported by my_malloc(). */
1048 goto err; /* purecov: tested */
1049 }
1050 action->need_sort= TRUE;
1051 }
1052 DBUG_ASSERT(action >= ds_control->ds_action);
1053 DBUG_ASSERT(action < ds_control->ds_action + ds_control->ds_active);
1054 DBUG_PRINT("debug_sync", ("action: 0x%lx array: 0x%lx count: %u",
1055 (long) action, (long) ds_control->ds_action,
1056 ds_control->ds_active));
1057
1058 DBUG_RETURN(action);
1059
1060 /* purecov: begin tested */
1061 err:
1062 DBUG_RETURN(NULL);
1063 /* purecov: end */
1064 }
1065
1066
1067 /**
1068 Set a debug sync action.
1069
1070 @param[in] thd thread handle
1071 @param[in] action synchronization action
1072
1073 @return status
1074 @retval FALSE ok
1075 @retval TRUE error
1076
1077 @description
1078 This is called from the debug sync parser. It arms the action for
1079 the requested sync point. If the action parsed into an empty action,
1080 it is removed instead.
1081
1082 Setting an action for a sync point means to make the sync point
1083 active. When it is hit it will execute this action.
1084
1085 Before parsing, we "get" an action object. This is placed at the
1086 end of the thread's action array unless the requested sync point
1087 has an action already.
1088
1089 Then the parser fills the action object from the request string.
1090
1091 Finally the action is "set" for the sync point. If it was parsed
1092 to be empty, it is removed from the array. If it did belong to a
1093 sync point before, the sync point becomes inactive. If the action
1094 became non-empty and it did not belong to a sync point before (it
1095 was added at the end of the action array), the action array needs
1096 to be sorted by sync point.
1097
1098 If the sync point name is "now", it is executed immediately.
1099 */
1100
debug_sync_set_action(THD * thd,st_debug_sync_action * action)1101 static bool debug_sync_set_action(THD *thd, st_debug_sync_action *action)
1102 {
1103 st_debug_sync_control *ds_control= thd->debug_sync_control;
1104 bool is_dsp_now= FALSE;
1105 DBUG_ENTER("debug_sync_set_action");
1106 DBUG_ASSERT(thd);
1107 DBUG_ASSERT(action);
1108 DBUG_ASSERT(ds_control);
1109
1110 action->activation_count= max(action->hit_limit, action->execute);
1111 if (!action->activation_count)
1112 {
1113 debug_sync_remove_action(ds_control, action);
1114 DBUG_PRINT("debug_sync", ("action cleared"));
1115 }
1116 else
1117 {
1118 const char *dsp_name= action->sync_point.c_ptr();
1119 DBUG_EXECUTE("debug_sync", {
1120 /* Functions as DBUG_PRINT args can change keyword and line nr. */
1121 const char *sig_emit= action->signal.c_ptr();
1122 const char *sig_wait= action->wait_for.c_ptr();
1123 DBUG_PRINT("debug_sync",
1124 ("sync_point: '%s' activation_count: %lu hit_limit: %lu "
1125 "execute: %lu timeout: %lu signal: '%s' wait_for: '%s'",
1126 dsp_name, action->activation_count,
1127 action->hit_limit, action->execute, action->timeout,
1128 sig_emit, sig_wait));});
1129
1130 /* Check this before sorting the array. action may move. */
1131 is_dsp_now= !my_strcasecmp(system_charset_info, dsp_name, "now");
1132
1133 if (action->need_sort)
1134 {
1135 action->need_sort= FALSE;
1136 /* Sort actions by (name_len, name). */
1137 my_qsort(ds_control->ds_action, ds_control->ds_active,
1138 sizeof(st_debug_sync_action), debug_sync_qsort_cmp);
1139 }
1140 }
1141 DBUG_EXECUTE("debug_sync_list", debug_sync_print_actions(thd););
1142
1143 /* Execute the special sync point 'now' if activated above. */
1144 if (is_dsp_now)
1145 {
1146 DEBUG_SYNC(thd, "now");
1147 /*
1148 If HIT_LIMIT for sync point "now" was 1, the execution of the sync
1149 point decremented it to 0. In this case the following happened:
1150
1151 - an error message was reported with my_error() and
1152 - the statement was killed with thd->killed= THD::KILL_QUERY.
1153
1154 If a statement reports an error, it must not call send_ok().
1155 The calling functions will not call send_ok(), if we return TRUE
1156 from this function.
1157
1158 thd->killed is also set if the wait is interrupted from a
1159 KILL or KILL QUERY statement. In this case, no error is reported
1160 and shall not be reported as a result of SET DEBUG_SYNC.
1161 Hence, we check for the first condition above.
1162 */
1163 if (thd->is_error())
1164 DBUG_RETURN(TRUE);
1165 }
1166
1167 DBUG_RETURN(FALSE);
1168 }
1169
1170
1171 /*
1172 Advance the pointer by length of multi-byte character.
1173
1174 @param ptr pointer to multibyte character.
1175
1176 @return NULL or pointer after advancing pointer by the
1177 length of multi-byte character pointed to.
1178 */
1179
advance_mbchar_ptr(const char * ptr)1180 static inline const char *advance_mbchar_ptr(const char *ptr)
1181 {
1182 uint clen= my_mbcharlen(system_charset_info, (uchar) *ptr);
1183
1184 return (clen != 0) ? ptr + clen : NULL;
1185 }
1186
1187
1188 /*
1189 Skip whitespace characters from the beginning of the multi-byte string.
1190
1191 @param ptr pointer to the multi-byte string.
1192
1193 @return a pointer to the first non-whitespace character or NULL if the
1194 string consists from whitespace characters only.
1195 */
1196
skip_whitespace(const char * ptr)1197 static inline const char *skip_whitespace(const char *ptr)
1198 {
1199 while (ptr != NULL && *ptr && my_isspace(system_charset_info, *ptr))
1200 ptr= advance_mbchar_ptr(ptr);
1201
1202 return ptr;
1203 }
1204
1205
1206 /*
1207 Get pointer to end of token.
1208
1209 @param ptr pointer to start of token
1210
1211 @return NULL or pointer to end of token.
1212 */
1213
get_token_end_ptr(const char * ptr)1214 static inline const char *get_token_end_ptr(const char *ptr)
1215 {
1216 while (ptr != NULL && *ptr && !my_isspace(system_charset_info, *ptr))
1217 ptr= advance_mbchar_ptr(ptr);
1218
1219 return ptr;
1220 }
1221
1222
1223 /**
1224 Extract a token from a string.
1225
1226 @param[out] token_p returns start of token
1227 @param[out] token_length_p returns length of token
1228 @param[in,out] ptr current string pointer, adds '\0' terminators
1229
1230 @return string pointer or NULL
1231 @retval != NULL ptr behind token terminator or at string end
1232 @retval NULL no token found in remainder of string
1233
1234 @note
1235 This function assumes that the string is in system_charset_info,
1236 that this charset is single byte for ASCII NUL ('\0'), that no
1237 character except of ASCII NUL ('\0') contains a byte with value 0,
1238 and that ASCII NUL ('\0') is used as the string terminator.
1239
1240 This function needs to return tokens that are terminated with ASCII
1241 NUL ('\0'). The tokens are used in my_strcasecmp(). Unfortunately
1242 there is no my_strncasecmp().
1243
1244 To return the last token without copying it, we require the input
1245 string to be nul terminated.
1246
1247 @description
1248 This function skips space characters at string begin.
1249
1250 It returns a pointer to the first non-space character in *token_p.
1251
1252 If no non-space character is found before the string terminator
1253 ASCII NUL ('\0'), the function returns NULL. *token_p and
1254 *token_length_p remain unchanged in this case (they are not set).
1255
1256 The function takes a space character or an ASCII NUL ('\0') as a
1257 terminator of the token. The space character could be multi-byte.
1258
1259 It returns the length of the token in bytes, excluding the
1260 terminator, in *token_length_p.
1261
1262 If the terminator of the token is ASCII NUL ('\0'), it returns a
1263 pointer to the terminator (string end).
1264
1265 If the terminator is a space character, it replaces the the first
1266 byte of the terminator character by ASCII NUL ('\0'), skips the (now
1267 corrupted) terminator character, and skips all following space
1268 characters. It returns a pointer to the next non-space character or
1269 to the string terminator ASCII NUL ('\0').
1270 */
1271
debug_sync_token(char ** token_p,uint * token_length_p,char * ptr)1272 static char *debug_sync_token(char **token_p, uint *token_length_p, char *ptr)
1273 {
1274 DBUG_ASSERT(token_p);
1275 DBUG_ASSERT(token_length_p);
1276 DBUG_ASSERT(ptr);
1277
1278
1279 /* Skip leading space */
1280 ptr= const_cast<char*>(skip_whitespace(ptr));
1281
1282 if (ptr == NULL || !*ptr)
1283 return NULL;
1284
1285 /* Get token start. */
1286 *token_p= ptr;
1287
1288 /* Find token end. */
1289 ptr= const_cast<char*>(get_token_end_ptr(ptr));
1290
1291 if (ptr == NULL)
1292 return NULL;
1293
1294 /* Get token length. */
1295 *token_length_p= ptr - *token_p;
1296
1297 /* If necessary, terminate token. */
1298 if (*ptr)
1299 {
1300 char* tmp= ptr;
1301
1302 /* Advance by terminator character length. */
1303 ptr= const_cast<char*>(advance_mbchar_ptr(ptr));
1304 if (ptr != NULL)
1305 {
1306 /* Terminate token. */
1307 *tmp= '\0';
1308
1309 /* Skip trailing space */
1310 ptr= const_cast<char*>(skip_whitespace(ptr));
1311 }
1312 }
1313 return ptr;
1314 }
1315
1316
1317 /**
1318 Extract a number from a string.
1319
1320 @param[out] number_p returns number
1321 @param[in] actstrptr current pointer in action string
1322
1323 @return string pointer or NULL
1324 @retval != NULL ptr behind token terminator or at string end
1325 @retval NULL no token found or token is not valid number
1326
1327 @note
1328 The same assumptions about charset apply as for debug_sync_token().
1329
1330 @description
1331 This function fetches a token from the string and converts it
1332 into a number.
1333
1334 If there is no token left in the string, or the token is not a valid
1335 decimal number, NULL is returned. The result in *number_p is
1336 undefined in this case.
1337 */
1338
debug_sync_number(ulong * number_p,char * actstrptr)1339 static char *debug_sync_number(ulong *number_p, char *actstrptr)
1340 {
1341 char *ptr;
1342 char *ept;
1343 char *token;
1344 uint token_length;
1345 DBUG_ASSERT(number_p);
1346 DBUG_ASSERT(actstrptr);
1347
1348 /* Get token from string. */
1349 if (!(ptr= debug_sync_token(&token, &token_length, actstrptr)))
1350 goto end;
1351
1352 *number_p= strtoul(token, &ept, 10);
1353 if (*ept)
1354 ptr= NULL;
1355
1356 end:
1357 return ptr;
1358 }
1359
1360
1361 /**
1362 Evaluate a debug sync action string.
1363
1364 @param[in] thd thread handle
1365 @param[in,out] action_str action string to receive '\0' terminators
1366
1367 @return status
1368 @retval FALSE ok
1369 @retval TRUE error
1370
1371 @description
1372 This is called when the DEBUG_SYNC system variable is set.
1373 Parse action string, build a debug sync action, activate it.
1374
1375 Before parsing, we "get" an action object. This is placed at the
1376 end of the thread's action array unless the requested sync point
1377 has an action already.
1378
1379 Then the parser fills the action object from the request string.
1380
1381 Finally the action is "set" for the sync point. This means that the
1382 sync point becomes active or inactive, depending on the action
1383 values.
1384
1385 @note
1386 The input string needs to be ASCII NUL ('\0') terminated. We split
1387 nul-terminated tokens in it without copy.
1388
1389 @see the function comment of debug_sync_token() for more constraints
1390 for the string.
1391 */
1392
debug_sync_eval_action(THD * thd,char * action_str)1393 static bool debug_sync_eval_action(THD *thd, char *action_str)
1394 {
1395 st_debug_sync_action *action= NULL;
1396 const char *errmsg;
1397 char *ptr;
1398 char *token;
1399 uint token_length= 0;
1400 DBUG_ENTER("debug_sync_eval_action");
1401 DBUG_ASSERT(thd);
1402 DBUG_ASSERT(action_str);
1403
1404 /*
1405 Get debug sync point name. Or a special command.
1406 */
1407 if (!(ptr= debug_sync_token(&token, &token_length, action_str)))
1408 {
1409 errmsg= "Missing synchronization point name";
1410 goto err;
1411 }
1412
1413 /*
1414 If there is a second token, the first one is the sync point name.
1415 */
1416 if (*ptr)
1417 {
1418 /* Get an action object to collect the requested action parameters. */
1419 action= debug_sync_get_action(thd, token, token_length);
1420 if (!action)
1421 {
1422 /* Error message is sent. */
1423 DBUG_RETURN(TRUE); /* purecov: tested */
1424 }
1425 }
1426
1427 /*
1428 Get kind of action to be taken at sync point.
1429 */
1430 if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1431 {
1432 /* No action present. Try special commands. Token unchanged. */
1433
1434 /*
1435 Try RESET.
1436 */
1437 if (!my_strcasecmp(system_charset_info, token, "RESET"))
1438 {
1439 /* It is RESET. Reset all actions and global signal. */
1440 debug_sync_reset(thd);
1441 goto end;
1442 }
1443
1444 /* Token unchanged. It still contains sync point name. */
1445 errmsg= "Missing action after synchronization point name '%.*s'";
1446 goto err;
1447 }
1448
1449 /*
1450 Check for pseudo actions first. Start with actions that work on
1451 an existing action.
1452 */
1453 DBUG_ASSERT(action);
1454
1455 /*
1456 Try TEST.
1457 */
1458 if (!my_strcasecmp(system_charset_info, token, "TEST"))
1459 {
1460 /* It is TEST. Nothing must follow it. */
1461 if (*ptr)
1462 {
1463 errmsg= "Nothing must follow action TEST";
1464 goto err;
1465 }
1466
1467 /* Execute sync point. */
1468 debug_sync(thd, action->sync_point.ptr(), action->sync_point.length());
1469 /* Fix statistics. This was not a real hit of the sync point. */
1470 thd->debug_sync_control->dsp_hits--;
1471 goto end;
1472 }
1473
1474 /*
1475 Now check for actions that define a new action.
1476 Initialize action. Do not use memset(). Strings may have malloced.
1477 */
1478 action->activation_count= 0;
1479 action->hit_limit= 0;
1480 action->execute= 0;
1481 action->timeout= 0;
1482 action->signal.length(0);
1483 action->wait_for.length(0);
1484
1485 /*
1486 Try CLEAR.
1487 */
1488 if (!my_strcasecmp(system_charset_info, token, "CLEAR"))
1489 {
1490 /* It is CLEAR. Nothing must follow it. */
1491 if (*ptr)
1492 {
1493 errmsg= "Nothing must follow action CLEAR";
1494 goto err;
1495 }
1496
1497 /* Set (clear/remove) action. */
1498 goto set_action;
1499 }
1500
1501 /*
1502 Now check for real sync point actions.
1503 */
1504
1505 /*
1506 Try SIGNAL.
1507 */
1508 if (!my_strcasecmp(system_charset_info, token, "SIGNAL"))
1509 {
1510 /* It is SIGNAL. Signal name must follow. */
1511 if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1512 {
1513 errmsg= "Missing signal name after action SIGNAL";
1514 goto err;
1515 }
1516 if (action->signal.copy(token, token_length, system_charset_info))
1517 {
1518 /* Error is reported by my_malloc(). */
1519 /* purecov: begin tested */
1520 errmsg= NULL;
1521 goto err;
1522 /* purecov: end */
1523 }
1524
1525 /* Set default for EXECUTE option. */
1526 action->execute= 1;
1527
1528 /* Get next token. If none follows, set action. */
1529 if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1530 goto set_action;
1531 }
1532
1533 /*
1534 Try WAIT_FOR.
1535 */
1536 if (!my_strcasecmp(system_charset_info, token, "WAIT_FOR"))
1537 {
1538 /* It is WAIT_FOR. Wait_for signal name must follow. */
1539 if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1540 {
1541 errmsg= "Missing signal name after action WAIT_FOR";
1542 goto err;
1543 }
1544 if (action->wait_for.copy(token, token_length, system_charset_info))
1545 {
1546 /* Error is reported by my_malloc(). */
1547 /* purecov: begin tested */
1548 errmsg= NULL;
1549 goto err;
1550 /* purecov: end */
1551 }
1552
1553 /* Set default for EXECUTE and TIMEOUT options. */
1554 action->execute= 1;
1555 action->timeout= opt_debug_sync_timeout;
1556 action->clear_event= true;
1557
1558 /* Get next token. If none follows, set action. */
1559 if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1560 goto set_action;
1561
1562 /*
1563 Try TIMEOUT.
1564 */
1565 if (!my_strcasecmp(system_charset_info, token, "TIMEOUT"))
1566 {
1567 /* It is TIMEOUT. Number must follow. */
1568 if (!(ptr= debug_sync_number(&action->timeout, ptr)))
1569 {
1570 errmsg= "Missing valid number after TIMEOUT";
1571 goto err;
1572 }
1573
1574 /* Get next token. If none follows, set action. */
1575 if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1576 goto set_action;
1577 }
1578 }
1579
1580 /*
1581 Try EXECUTE.
1582 */
1583 if (!my_strcasecmp(system_charset_info, token, "EXECUTE"))
1584 {
1585 /*
1586 EXECUTE requires either SIGNAL and/or WAIT_FOR to be present.
1587 In this case action->execute has been preset to 1.
1588 */
1589 if (!action->execute)
1590 {
1591 errmsg= "Missing action before EXECUTE";
1592 goto err;
1593 }
1594
1595 /* Number must follow. */
1596 if (!(ptr= debug_sync_number(&action->execute, ptr)))
1597 {
1598 errmsg= "Missing valid number after EXECUTE";
1599 goto err;
1600 }
1601
1602 /* Get next token. If none follows, set action. */
1603 if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1604 goto set_action;
1605 }
1606
1607 /*
1608 Try NO_CLEAR_EVENT.
1609 */
1610 if (!my_strcasecmp(system_charset_info, token, "NO_CLEAR_EVENT"))
1611 {
1612 action->clear_event= false;
1613 /* Get next token. If none follows, set action. */
1614 if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1615 goto set_action;
1616 }
1617
1618 /*
1619 Try HIT_LIMIT.
1620 */
1621 if (!my_strcasecmp(system_charset_info, token, "HIT_LIMIT"))
1622 {
1623 /* Number must follow. */
1624 if (!(ptr= debug_sync_number(&action->hit_limit, ptr)))
1625 {
1626 errmsg= "Missing valid number after HIT_LIMIT";
1627 goto err;
1628 }
1629
1630 /* Get next token. If none follows, set action. */
1631 if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1632 goto set_action;
1633 }
1634
1635 errmsg= "Illegal or out of order stuff: '%.*s'";
1636
1637 err:
1638 if (errmsg)
1639 {
1640 /*
1641 NOTE: errmsg must either have %.*s or none % at all.
1642 It can be NULL if an error message is already reported
1643 (e.g. by my_malloc()).
1644 */
1645 set_if_smaller(token_length, 64); /* Limit error message length. */
1646 my_printf_error(ER_PARSE_ERROR, errmsg, MYF(0), token_length, token);
1647 }
1648 if (action)
1649 debug_sync_remove_action(thd->debug_sync_control, action);
1650 DBUG_RETURN(TRUE);
1651
1652 set_action:
1653 DBUG_RETURN(debug_sync_set_action(thd, action));
1654
1655 end:
1656 DBUG_RETURN(FALSE);
1657 }
1658
1659 /**
1660 Set the system variable 'debug_sync'.
1661
1662 @param[in] thd thread handle
1663 @param[in] var set variable request
1664
1665 @return status
1666 @retval FALSE ok, variable is set
1667 @retval TRUE error, variable could not be set
1668
1669 @note
1670 "Setting" of the system variable 'debug_sync' does not mean to
1671 assign a value to it as usual. Instead a debug sync action is parsed
1672 from the input string and stored apart from the variable value.
1673
1674 @note
1675 For efficiency reasons, the action string parser places '\0'
1676 terminators in the string. So we need to take a copy here.
1677 */
1678
debug_sync_update(THD * thd,char * val_str)1679 bool debug_sync_update(THD *thd, char *val_str)
1680 {
1681 DBUG_ENTER("debug_sync_update");
1682 DBUG_PRINT("debug_sync", ("set action: '%s'", val_str));
1683
1684 /*
1685 debug_sync_eval_action() places '\0' in the string, which itself
1686 must be '\0' terminated.
1687 */
1688 DBUG_RETURN(opt_debug_sync_timeout ?
1689 debug_sync_eval_action(thd, val_str) :
1690 FALSE);
1691 }
1692
1693
1694 /**
1695 Retrieve the value of the system variable 'debug_sync'.
1696
1697 @param[in] thd thread handle
1698
1699 @return string
1700 @retval != NULL ok, string pointer
1701 @retval NULL memory allocation error
1702
1703 @note
1704 The value of the system variable 'debug_sync' reflects if
1705 the facility is enabled ("ON") or disabled (default, "OFF").
1706
1707 When "ON", the list of signals signalled are added separated by comma.
1708 */
1709
debug_sync_value_ptr(THD * thd)1710 uchar *debug_sync_value_ptr(THD *thd)
1711 {
1712 char *value;
1713 DBUG_ENTER("debug_sync_value_ptr");
1714
1715 if (opt_debug_sync_timeout)
1716 {
1717 std::string signals_on("ON - signals: '");
1718 static char sep[]= ",";
1719
1720 // Ensure exclusive access to debug_sync_global.ds_signal_set
1721 mysql_mutex_lock(&debug_sync_global.ds_mutex);
1722
1723 signal_event_set::const_iterator iter;
1724 for (iter= debug_sync_global.ds_signal_set.begin();
1725 iter != debug_sync_global.ds_signal_set.end(); )
1726 {
1727 signals_on.append(*iter);
1728 if ((++iter) != debug_sync_global.ds_signal_set.end())
1729 signals_on.append(sep);
1730 }
1731 signals_on.append("'");
1732
1733 const char *c_str= signals_on.c_str();
1734 const size_t lgt= strlen(c_str) + 1;
1735
1736 if ((value= (char*) alloc_root(thd->mem_root, lgt)))
1737 memcpy(value, c_str, lgt);
1738
1739 mysql_mutex_unlock(&debug_sync_global.ds_mutex);
1740 }
1741 else
1742 {
1743 /* purecov: begin tested */
1744 value= const_cast<char*>("OFF");
1745 /* purecov: end */
1746 }
1747
1748 DBUG_RETURN((uchar*) value);
1749 }
1750
1751
1752 /**
1753 Return true if the signal is found in global signal list.
1754
1755 @param signal Signal name identifying the signal.
1756
1757 @note
1758 If signal is found in the global signal set, it means that the
1759 signal thread has signalled to the waiting thread. This method
1760 must be called with the debug_sync_global.ds_mutex held.
1761
1762 @eretval true if signal is found in the global signal list.
1763 @retval false otherwise.
1764 */
1765
is_signalled(const std::string * signal_name)1766 static inline bool is_signalled(const std::string *signal_name)
1767 {
1768 return (debug_sync_global.ds_signal_set.find(*signal_name) !=
1769 debug_sync_global.ds_signal_set.end());
1770 }
1771
1772
1773 /**
1774 Return false if signal has been added to global signal list.
1775
1776 @param signal signal name that is to be added to the global signal
1777 list.
1778
1779 @note
1780 This method add signal name to the global signal list and signals
1781 the waiting thread that this signal has been emitted. This method
1782 must be called with the debug_sync_global.ds_mutex held.
1783 */
1784
add_signal_event(const std::string * signal_name)1785 static inline void add_signal_event(const std::string *signal_name)
1786 {
1787 debug_sync_global.ds_signal_set.insert(*signal_name);
1788 }
1789
1790
1791 /**
1792 Remove the signal from the global signal list.
1793
1794 @param signal signal name to be removed from the global signal list.
1795
1796 @note
1797 This method erases the signal from the signal list. This happens
1798 when the wait thread has processed the signal event from the
1799 signalling thread. This method should be called with the
1800 debug_sync_global.ds_mutex held.
1801 */
clear_signal_event(const std::string * signal_name)1802 static inline void clear_signal_event(const std::string *signal_name)
1803 {
1804 debug_sync_global.ds_signal_set.erase(*signal_name);
1805 }
1806
1807
1808 /**
1809 Execute requested action at a synchronization point.
1810
1811 @param[in] thd thread handle
1812 @param[in] action action to be executed
1813
1814 @note
1815 This is to be called only if activation count > 0.
1816 */
1817
debug_sync_execute(THD * thd,st_debug_sync_action * action)1818 static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
1819 {
1820 #ifndef DBUG_OFF
1821 const char *dsp_name= action->sync_point.c_ptr();
1822 const char *sig_emit= action->signal.c_ptr();
1823 const char *sig_wait= action->wait_for.c_ptr();
1824 #endif
1825 DBUG_ENTER("debug_sync_execute");
1826 DBUG_ASSERT(thd);
1827 DBUG_ASSERT(action);
1828 DBUG_PRINT("debug_sync",
1829 ("sync_point: '%s' activation_count: %lu hit_limit: %lu "
1830 "execute: %lu timeout: %lu signal: '%s' wait_for: '%s'",
1831 dsp_name, action->activation_count, action->hit_limit,
1832 action->execute, action->timeout, sig_emit, sig_wait));
1833
1834 DBUG_ASSERT(action->activation_count);
1835 action->activation_count--;
1836
1837 if (action->execute)
1838 {
1839 const char *UNINIT_VAR(old_proc_info);
1840
1841 action->execute--;
1842
1843 /*
1844 If we will be going to wait, set proc_info for the PROCESSLIST table.
1845 Do this before emitting the signal, so other threads can see it
1846 if they awake before we enter_cond() below.
1847 */
1848 if (action->wait_for.length())
1849 {
1850 st_debug_sync_control *ds_control= thd->debug_sync_control;
1851 strxnmov(ds_control->ds_proc_info, sizeof(ds_control->ds_proc_info)-1,
1852 "debug sync point: ", action->sync_point.c_ptr(), NullS);
1853 old_proc_info= thd->proc_info;
1854 thd_proc_info(thd, ds_control->ds_proc_info);
1855 }
1856
1857 /*
1858 Take mutex to ensure that only one thread access
1859 debug_sync_global.ds_signal_set at a time. Need to take mutex for
1860 read access too, to create a memory barrier in order to avoid that
1861 threads just reads an old cached version of the signal.
1862 */
1863 mysql_mutex_lock(&debug_sync_global.ds_mutex);
1864
1865 if (action->signal.length())
1866 {
1867 std::string signal= action->signal.ptr();
1868 /* Copy the signal to the global set. */
1869 add_signal_event(&signal);
1870 /* Wake threads waiting in a sync point. */
1871 mysql_cond_broadcast(&debug_sync_global.ds_cond);
1872 DBUG_PRINT("debug_sync_exec", ("signal '%s' at: '%s'",
1873 sig_emit, dsp_name));
1874 } /* end if (action->signal.length()) */
1875
1876 if (action->wait_for.length())
1877 {
1878 mysql_mutex_t *old_mutex;
1879 mysql_cond_t *old_cond= 0;
1880 int error= 0;
1881 struct timespec abstime;
1882 std::string wait_for= action->wait_for.ptr();
1883
1884 /*
1885 We don't use enter_cond()/exit_cond(). They do not save old
1886 mutex and cond. This would prohibit the use of DEBUG_SYNC
1887 between other places of enter_cond() and exit_cond().
1888
1889 We need to check for existence of thd->mysys_var to also make
1890 it possible to use DEBUG_SYNC framework in scheduler when this
1891 variable has been set to NULL.
1892 */
1893 if (thd->mysys_var)
1894 {
1895 old_mutex= thd->mysys_var->current_mutex;
1896 old_cond= thd->mysys_var->current_cond;
1897 thd->mysys_var->current_mutex= &debug_sync_global.ds_mutex;
1898 thd->mysys_var->current_cond= &debug_sync_global.ds_cond;
1899 }
1900 else
1901 old_mutex= NULL;
1902
1903 set_timespec(abstime, action->timeout);
1904 DBUG_EXECUTE("debug_sync_exec", {
1905 DBUG_PRINT("debug_sync_exec",
1906 ("wait for '%s' at: '%s'",
1907 sig_wait, dsp_name));});
1908 /*
1909 Wait until global signal string matches the wait_for string.
1910 Interrupt when thread or query is killed or facility disabled.
1911 The facility can become disabled when some thread cannot get
1912 the required dynamic memory allocated.
1913 */
1914 while (!is_signalled(&wait_for) &&
1915 !thd->killed && opt_debug_sync_timeout)
1916 {
1917 error= mysql_cond_timedwait(&debug_sync_global.ds_cond,
1918 &debug_sync_global.ds_mutex,
1919 &abstime);
1920
1921 DBUG_EXECUTE("debug_sync", {
1922 /* Functions as DBUG_PRINT args can change keyword and line nr. */
1923 DBUG_PRINT("debug_sync",
1924 ("awoke from %s error: %d", sig_wait, error)); });
1925
1926 if (error == ETIMEDOUT || error == ETIME)
1927 {
1928 // We should not make the statement fail, even if in strict mode.
1929 const bool save_abort_on_warning= thd->abort_on_warning;
1930 thd->abort_on_warning= false;
1931 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
1932 ER_DEBUG_SYNC_TIMEOUT, ER(ER_DEBUG_SYNC_TIMEOUT));
1933 thd->abort_on_warning= save_abort_on_warning;
1934 DBUG_EXECUTE_IF("debug_sync_abort_on_timeout", DBUG_ABORT(););
1935 break;
1936 }
1937 error= 0;
1938 }
1939 if (action->clear_event)
1940 clear_signal_event(&wait_for);
1941
1942 DBUG_EXECUTE("debug_sync_exec",
1943 if (thd->killed)
1944 DBUG_PRINT("debug_sync_exec",
1945 ("killed %d from '%s' at: '%s'",
1946 thd->killed, sig_wait, dsp_name));
1947 else
1948 DBUG_PRINT("debug_sync_exec",
1949 ("%s from '%s' at: '%s'",
1950 error ? "timeout" : "resume",
1951 sig_wait, dsp_name)););
1952
1953 /*
1954 We don't use enter_cond()/exit_cond(). They do not save old
1955 mutex and cond. This would prohibit the use of DEBUG_SYNC
1956 between other places of enter_cond() and exit_cond(). The
1957 protected mutex must always unlocked _before_ mysys_var->mutex
1958 is locked. (See comment in THD::exit_cond().)
1959 */
1960 mysql_mutex_unlock(&debug_sync_global.ds_mutex);
1961 if (old_mutex)
1962 {
1963 mysql_mutex_lock(&thd->mysys_var->mutex);
1964 thd->mysys_var->current_mutex= old_mutex;
1965 thd->mysys_var->current_cond= old_cond;
1966 thd_proc_info(thd, old_proc_info);
1967 mysql_mutex_unlock(&thd->mysys_var->mutex);
1968 }
1969 else
1970 thd_proc_info(thd, old_proc_info);
1971 }
1972 else
1973 {
1974 /* In case we don't wait, we just release the mutex. */
1975 mysql_mutex_unlock(&debug_sync_global.ds_mutex);
1976 } /* end if (action->wait_for.length()) */
1977
1978 } /* end if (action->execute) */
1979
1980 /* hit_limit is zero for infinite. Don't decrement unconditionally. */
1981 if (action->hit_limit)
1982 {
1983 if (!--action->hit_limit)
1984 {
1985 thd->killed= THD::KILL_QUERY;
1986 my_error(ER_DEBUG_SYNC_HIT_LIMIT, MYF(0));
1987 }
1988 DBUG_PRINT("debug_sync_exec", ("hit_limit: %lu at: '%s'",
1989 action->hit_limit, dsp_name));
1990 }
1991
1992 DBUG_VOID_RETURN;
1993 }
1994
1995
1996 /**
1997 Execute requested action at a synchronization point.
1998
1999 @param[in] thd thread handle
2000 @param[in] sync_point_name name of synchronization point
2001 @param[in] name_len length of sync point name
2002 */
2003
debug_sync(THD * thd,const char * sync_point_name,size_t name_len)2004 void debug_sync(THD *thd, const char *sync_point_name, size_t name_len)
2005 {
2006 if(!thd)
2007 {
2008 return;
2009 }
2010
2011 st_debug_sync_control *ds_control= thd->debug_sync_control;
2012 st_debug_sync_action *action;
2013 DBUG_ENTER("debug_sync");
2014 DBUG_ASSERT(thd);
2015 DBUG_ASSERT(sync_point_name);
2016 DBUG_ASSERT(name_len);
2017 DBUG_ASSERT(ds_control);
2018 DBUG_PRINT("debug_sync_point", ("hit: '%s'", sync_point_name));
2019
2020 /* Statistics. */
2021 ds_control->dsp_hits++;
2022
2023 if (ds_control->ds_active &&
2024 (action= debug_sync_find(ds_control->ds_action, ds_control->ds_active,
2025 sync_point_name, name_len)) &&
2026 action->activation_count)
2027 {
2028 /* Sync point is active (action exists). */
2029 debug_sync_execute(thd, action);
2030
2031 /* Statistics. */
2032 ds_control->dsp_executed++;
2033
2034 /* If action became inactive, remove it to shrink the search array. */
2035 if (!action->activation_count)
2036 debug_sync_remove_action(ds_control, action);
2037 }
2038
2039 DBUG_VOID_RETURN;
2040 }
2041
2042 /**
2043 Define debug sync action.
2044
2045 @param[in] thd thread handle
2046 @param[in] action_str action string
2047
2048 @return status
2049 @retval FALSE ok
2050 @retval TRUE error
2051
2052 @description
2053 The function is similar to @c debug_sync_eval_action but is
2054 to be called immediately from the server code rather than
2055 to be triggered by setting a value to DEBUG_SYNC system variable.
2056
2057 @note
2058 The input string is copied prior to be fed to
2059 @c debug_sync_eval_action to let the latter modify it.
2060
2061 Caution.
2062 The function allocates in THD::mem_root and therefore
2063 is not recommended to be deployed inside big loops.
2064 */
2065
debug_sync_set_action(THD * thd,const char * action_str,size_t len)2066 bool debug_sync_set_action(THD *thd, const char *action_str, size_t len)
2067 {
2068 bool rc;
2069 char *value;
2070 DBUG_ENTER("debug_sync_set_action");
2071 DBUG_ASSERT(thd);
2072 DBUG_ASSERT(action_str);
2073
2074 value= strmake_root(thd->mem_root, action_str, len);
2075 rc= debug_sync_eval_action(thd, value);
2076 DBUG_RETURN(rc);
2077 }
2078
2079
2080 #endif /* defined(ENABLED_DEBUG_SYNC) */
2081