1/* Copyright (c) 2009, 2013, Oracle and/or its affiliates.
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 as published by
5 the Free Software Foundation; version 2 of the License.
6
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.
11
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 Foundation,
14 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
15
16/* see include/mysql/service_debug_sync.h for debug sync documentation */
17
18#include "mariadb.h"
19#include "debug_sync.h"
20
21#if defined(ENABLED_DEBUG_SYNC)
22
23/*
24 Due to weaknesses in our include files, we need to include
25 sql_priv.h here. To have THD declared, we need to include
26 sql_class.h. This includes log_event.h, which in turn requires
27 declarations from sql_priv.h (e.g. OPTION_AUTO_IS_NULL).
28 sql_priv.h includes almost everything, so is sufficient here.
29*/
30#include "sql_priv.h"
31#include "sql_parse.h"
32
33/*
34 Action to perform at a synchronization point.
35 NOTE: This structure is moved around in memory by realloc(), qsort(),
36 and memmove(). Do not add objects with non-trivial constuctors
37 or destructors, which might prevent moving of this structure
38 with these functions.
39*/
40struct st_debug_sync_action
41{
42 ulong activation_count; /* MY_MAX(hit_limit, execute) */
43 ulong hit_limit; /* hits before kill query */
44 ulong execute; /* executes before self-clear */
45 ulong timeout; /* wait_for timeout */
46 String signal; /* signal to emit */
47 String wait_for; /* signal to wait for */
48 String sync_point; /* sync point name */
49 bool need_sort; /* if new action, array needs sort */
50};
51
52/* Debug sync control. Referenced by THD. */
53struct st_debug_sync_control
54{
55 st_debug_sync_action *ds_action; /* array of actions */
56 uint ds_active; /* # active actions */
57 uint ds_allocated; /* # allocated actions */
58 ulonglong dsp_hits; /* statistics */
59 ulonglong dsp_executed; /* statistics */
60 ulonglong dsp_max_active; /* statistics */
61 /*
62 thd->proc_info points at unsynchronized memory.
63 It must not go away as long as the thread exists.
64 */
65 char ds_proc_info[80]; /* proc_info string */
66};
67
68
69/**
70 Definitions for the debug sync facility.
71 1. Global string variable to hold a "signal" ("signal post", "flag mast").
72 2. Global condition variable for signaling and waiting.
73 3. Global mutex to synchronize access to the above.
74*/
75struct st_debug_sync_globals
76{
77 String ds_signal; /* signal variable */
78 mysql_cond_t ds_cond; /* condition variable */
79 mysql_mutex_t ds_mutex; /* mutex variable */
80 ulonglong dsp_hits; /* statistics */
81 ulonglong dsp_executed; /* statistics */
82 ulonglong dsp_max_active; /* statistics */
83};
84static st_debug_sync_globals debug_sync_global; /* All globals in one object */
85
86/**
87 Callbacks from C files.
88*/
89C_MODE_START
90static void debug_sync(THD *thd, const char *sync_point_name, size_t name_len);
91static int debug_sync_qsort_cmp(const void *, const void *);
92C_MODE_END
93
94#ifdef HAVE_PSI_INTERFACE
95static PSI_mutex_key key_debug_sync_globals_ds_mutex;
96
97static PSI_mutex_info all_debug_sync_mutexes[]=
98{
99 { &key_debug_sync_globals_ds_mutex, "DEBUG_SYNC::mutex", PSI_FLAG_GLOBAL}
100};
101
102static PSI_cond_key key_debug_sync_globals_ds_cond;
103
104static PSI_cond_info all_debug_sync_conds[]=
105{
106 { &key_debug_sync_globals_ds_cond, "DEBUG_SYNC::cond", PSI_FLAG_GLOBAL}
107};
108
109static void init_debug_sync_psi_keys(void)
110{
111 const char* category= "sql";
112 int count;
113
114 count= array_elements(all_debug_sync_mutexes);
115 mysql_mutex_register(category, all_debug_sync_mutexes, count);
116
117 count= array_elements(all_debug_sync_conds);
118 mysql_cond_register(category, all_debug_sync_conds, count);
119}
120#endif /* HAVE_PSI_INTERFACE */
121
122
123/**
124 Initialize the debug sync facility at server start.
125
126 @return status
127 @retval 0 ok
128 @retval != 0 error
129*/
130
131int debug_sync_init(void)
132{
133 DBUG_ENTER("debug_sync_init");
134
135#ifdef HAVE_PSI_INTERFACE
136 init_debug_sync_psi_keys();
137#endif
138
139 if (opt_debug_sync_timeout)
140 {
141 int rc;
142
143 /* Initialize the global variables. */
144 debug_sync_global.ds_signal.length(0);
145 if ((rc= mysql_cond_init(key_debug_sync_globals_ds_cond,
146 &debug_sync_global.ds_cond, NULL)) ||
147 (rc= mysql_mutex_init(key_debug_sync_globals_ds_mutex,
148 &debug_sync_global.ds_mutex,
149 MY_MUTEX_INIT_FAST)))
150 DBUG_RETURN(rc); /* purecov: inspected */
151
152 /* Set the call back pointer in C files. */
153 debug_sync_C_callback_ptr= debug_sync;
154 }
155
156 DBUG_RETURN(0);
157}
158
159
160/**
161 End the debug sync facility.
162
163 @description
164 This is called at server shutdown or after a thread initialization error.
165*/
166
167void debug_sync_end(void)
168{
169 DBUG_ENTER("debug_sync_end");
170
171 /* End the facility only if it had been initialized. */
172 if (debug_sync_C_callback_ptr)
173 {
174 /* Clear the call back pointer in C files. */
175 debug_sync_C_callback_ptr= NULL;
176
177 /* Destroy the global variables. */
178 debug_sync_global.ds_signal.free();
179 mysql_cond_destroy(&debug_sync_global.ds_cond);
180 mysql_mutex_destroy(&debug_sync_global.ds_mutex);
181
182 /* Print statistics. */
183 {
184 char llbuff[22];
185 sql_print_information("Debug sync points hit: %22s",
186 llstr(debug_sync_global.dsp_hits, llbuff));
187 sql_print_information("Debug sync points executed: %22s",
188 llstr(debug_sync_global.dsp_executed, llbuff));
189 sql_print_information("Debug sync points max active per thread: %22s",
190 llstr(debug_sync_global.dsp_max_active, llbuff));
191 }
192 }
193
194 DBUG_VOID_RETURN;
195}
196
197
198/* purecov: begin tested */
199
200/**
201 Disable the facility after lack of memory if no error can be returned.
202
203 @note
204 Do not end the facility here because the global variables can
205 be in use by other threads.
206*/
207
208static void debug_sync_emergency_disable(void)
209{
210 DBUG_ENTER("debug_sync_emergency_disable");
211
212 opt_debug_sync_timeout= 0;
213
214 DBUG_PRINT("debug_sync",
215 ("Debug Sync Facility disabled due to lack of memory."));
216 sql_print_error("Debug Sync Facility disabled due to lack of memory.");
217
218 DBUG_VOID_RETURN;
219}
220
221/* purecov: end */
222
223
224/**
225 Initialize the debug sync facility at thread start.
226
227 @param[in] thd thread handle
228*/
229
230void debug_sync_init_thread(THD *thd)
231{
232 DBUG_ENTER("debug_sync_init_thread");
233 DBUG_ASSERT(thd);
234
235 if (opt_debug_sync_timeout)
236 {
237 thd->debug_sync_control= (st_debug_sync_control*)
238 my_malloc(sizeof(st_debug_sync_control),
239 MYF(MY_WME | MY_ZEROFILL | MY_THREAD_SPECIFIC));
240 if (!thd->debug_sync_control)
241 {
242 /*
243 Error is reported by my_malloc().
244 We must disable the facility. We have no way to return an error.
245 */
246 debug_sync_emergency_disable(); /* purecov: tested */
247 }
248 }
249
250 DBUG_VOID_RETURN;
251}
252
253
254/**
255 End the debug sync facility at thread end.
256
257 @param[in] thd thread handle
258*/
259
260void debug_sync_end_thread(THD *thd)
261{
262 DBUG_ENTER("debug_sync_end_thread");
263 DBUG_ASSERT(thd);
264
265 if (thd->debug_sync_control)
266 {
267 st_debug_sync_control *ds_control= thd->debug_sync_control;
268
269 /*
270 This synchronization point can be used to synchronize on thread end.
271 This is the latest point in a THD's life, where this can be done.
272 */
273 DEBUG_SYNC(thd, "thread_end");
274
275 if (ds_control->ds_action)
276 {
277 st_debug_sync_action *action= ds_control->ds_action;
278 st_debug_sync_action *action_end= action + ds_control->ds_allocated;
279 for (; action < action_end; action++)
280 {
281 action->signal.free();
282 action->wait_for.free();
283 action->sync_point.free();
284 }
285 my_free(ds_control->ds_action);
286 }
287
288 /* Statistics. */
289 mysql_mutex_lock(&debug_sync_global.ds_mutex);
290 debug_sync_global.dsp_hits+= ds_control->dsp_hits;
291 debug_sync_global.dsp_executed+= ds_control->dsp_executed;
292 if (debug_sync_global.dsp_max_active < ds_control->dsp_max_active)
293 debug_sync_global.dsp_max_active= ds_control->dsp_max_active;
294 mysql_mutex_unlock(&debug_sync_global.ds_mutex);
295
296 my_free(ds_control);
297 thd->debug_sync_control= NULL;
298 }
299
300 DBUG_VOID_RETURN;
301}
302
303
304/**
305 Move a string by length.
306
307 @param[out] to buffer for the resulting string
308 @param[in] to_end end of buffer
309 @param[in] from source string
310 @param[in] length number of bytes to copy
311
312 @return pointer to end of copied string
313*/
314
315static char *debug_sync_bmove_len(char *to, char *to_end,
316 const char *from, size_t length)
317{
318 DBUG_ASSERT(to);
319 DBUG_ASSERT(to_end);
320 DBUG_ASSERT(!length || from);
321 set_if_smaller(length, (size_t) (to_end - to));
322 memcpy(to, from, length);
323 return (to + length);
324}
325
326
327#if !defined(DBUG_OFF)
328
329/**
330 Create a string that describes an action.
331
332 @param[out] result buffer for the resulting string
333 @param[in] size size of result buffer
334 @param[in] action action to describe
335*/
336
337static void debug_sync_action_string(char *result, uint size,
338 st_debug_sync_action *action)
339{
340 char *wtxt= result;
341 char *wend= wtxt + size - 1; /* Allow emergency '\0'. */
342 DBUG_ASSERT(result);
343 DBUG_ASSERT(action);
344
345 /* If an execute count is present, signal or wait_for are needed too. */
346 DBUG_ASSERT(!action->execute ||
347 action->signal.length() || action->wait_for.length());
348
349 if (action->execute)
350 {
351 if (action->signal.length())
352 {
353 wtxt= debug_sync_bmove_len(wtxt, wend, STRING_WITH_LEN("SIGNAL "));
354 wtxt= debug_sync_bmove_len(wtxt, wend, action->signal.ptr(),
355 action->signal.length());
356 }
357 if (action->wait_for.length())
358 {
359 if ((wtxt == result) && (wtxt < wend))
360 *(wtxt++)= ' ';
361 wtxt= debug_sync_bmove_len(wtxt, wend, STRING_WITH_LEN(" WAIT_FOR "));
362 wtxt= debug_sync_bmove_len(wtxt, wend, action->wait_for.ptr(),
363 action->wait_for.length());
364
365 if (action->timeout != opt_debug_sync_timeout)
366 {
367 wtxt+= my_snprintf(wtxt, wend - wtxt, " TIMEOUT %lu", action->timeout);
368 }
369 }
370 if (action->execute != 1)
371 {
372 wtxt+= my_snprintf(wtxt, wend - wtxt, " EXECUTE %lu", action->execute);
373 }
374 }
375 if (action->hit_limit)
376 {
377 wtxt+= my_snprintf(wtxt, wend - wtxt, "%sHIT_LIMIT %lu",
378 (wtxt == result) ? "" : " ", action->hit_limit);
379 }
380
381 /*
382 If (wtxt == wend) string may not be terminated.
383 There is one byte left for an emergency termination.
384 */
385 *wtxt= '\0';
386}
387
388
389/**
390 Print actions.
391
392 @param[in] thd thread handle
393*/
394
395static void debug_sync_print_actions(THD *thd)
396{
397 st_debug_sync_control *ds_control= thd->debug_sync_control;
398 uint idx;
399 DBUG_ENTER("debug_sync_print_actions");
400 DBUG_ASSERT(thd);
401
402 if (!ds_control)
403 DBUG_VOID_RETURN;
404
405 for (idx= 0; idx < ds_control->ds_active; idx++)
406 {
407 const char *dsp_name= ds_control->ds_action[idx].sync_point.c_ptr();
408 char action_string[256];
409
410 debug_sync_action_string(action_string, sizeof(action_string),
411 ds_control->ds_action + idx);
412 DBUG_PRINT("debug_sync_list", ("%s %s", dsp_name, action_string));
413 }
414
415 DBUG_VOID_RETURN;
416}
417
418#endif /* !defined(DBUG_OFF) */
419
420
421/**
422 Compare two actions by sync point name length, string.
423
424 @param[in] arg1 reference to action1
425 @param[in] arg2 reference to action2
426
427 @return difference
428 @retval == 0 length1/string1 is same as length2/string2
429 @retval < 0 length1/string1 is smaller
430 @retval > 0 length1/string1 is bigger
431*/
432
433static int debug_sync_qsort_cmp(const void* arg1, const void* arg2)
434{
435 st_debug_sync_action *action1= (st_debug_sync_action*) arg1;
436 st_debug_sync_action *action2= (st_debug_sync_action*) arg2;
437 int diff;
438 DBUG_ASSERT(action1);
439 DBUG_ASSERT(action2);
440
441 if (!(diff= action1->sync_point.length() - action2->sync_point.length()))
442 diff= memcmp(action1->sync_point.ptr(), action2->sync_point.ptr(),
443 action1->sync_point.length());
444
445 return diff;
446}
447
448
449/**
450 Find a debug sync action.
451
452 @param[in] actionarr array of debug sync actions
453 @param[in] quantity number of actions in array
454 @param[in] dsp_name name of debug sync point to find
455 @param[in] name_len length of name of debug sync point
456
457 @return action
458 @retval != NULL found sync point in array
459 @retval NULL not found
460
461 @description
462 Binary search. Array needs to be sorted by length, sync point name.
463*/
464
465static st_debug_sync_action *debug_sync_find(st_debug_sync_action *actionarr,
466 int quantity,
467 const char *dsp_name,
468 size_t name_len)
469{
470 st_debug_sync_action *action;
471 int low ;
472 int high ;
473 int mid ;
474 ssize_t diff ;
475 DBUG_ASSERT(actionarr);
476 DBUG_ASSERT(dsp_name);
477 DBUG_ASSERT(name_len);
478
479 low= 0;
480 high= quantity;
481
482 while (low < high)
483 {
484 mid= (low + high) / 2;
485 action= actionarr + mid;
486 if (!(diff= name_len - action->sync_point.length()) &&
487 !(diff= memcmp(dsp_name, action->sync_point.ptr(), name_len)))
488 return action;
489 if (diff > 0)
490 low= mid + 1;
491 else
492 high= mid - 1;
493 }
494
495 if (low < quantity)
496 {
497 action= actionarr + low;
498 if ((name_len == action->sync_point.length()) &&
499 !memcmp(dsp_name, action->sync_point.ptr(), name_len))
500 return action;
501 }
502
503 return NULL;
504}
505
506
507/**
508 Reset the debug sync facility.
509
510 @param[in] thd thread handle
511
512 @description
513 Remove all actions of this thread.
514 Clear the global signal.
515*/
516
517static void debug_sync_reset(THD *thd)
518{
519 st_debug_sync_control *ds_control= thd->debug_sync_control;
520 DBUG_ENTER("debug_sync_reset");
521 DBUG_ASSERT(thd);
522 DBUG_ASSERT(ds_control);
523
524 /* Remove all actions of this thread. */
525 ds_control->ds_active= 0;
526
527 /* Clear the global signal. */
528 mysql_mutex_lock(&debug_sync_global.ds_mutex);
529 debug_sync_global.ds_signal.length(0);
530 mysql_mutex_unlock(&debug_sync_global.ds_mutex);
531
532 DBUG_VOID_RETURN;
533}
534
535
536/**
537 Remove a debug sync action.
538
539 @param[in] ds_control control object
540 @param[in] action action to be removed
541
542 @description
543 Removing an action mainly means to decrement the ds_active counter.
544 But if the action is between other active action in the array, then
545 the array needs to be shrinked. The active actions above the one to
546 be removed have to be moved down by one slot.
547*/
548
549static void debug_sync_remove_action(st_debug_sync_control *ds_control,
550 st_debug_sync_action *action)
551{
552 uint dsp_idx= (uint)(action - ds_control->ds_action);
553 DBUG_ENTER("debug_sync_remove_action");
554 DBUG_ASSERT(ds_control);
555 DBUG_ASSERT(ds_control == current_thd->debug_sync_control);
556 DBUG_ASSERT(action);
557 DBUG_ASSERT(dsp_idx < ds_control->ds_active);
558
559 /* Decrement the number of currently active actions. */
560 ds_control->ds_active--;
561
562 /*
563 If this was not the last active action in the array, we need to
564 shift remaining active actions down to keep the array gap-free.
565 Otherwise binary search might fail or take longer than necessary at
566 least. Also new actions are always put to the end of the array.
567 */
568 if (ds_control->ds_active > dsp_idx)
569 {
570 /*
571 Do not make save_action an object of class st_debug_sync_action.
572 Its destructor would tamper with the String pointers.
573 */
574 uchar save_action[sizeof(st_debug_sync_action)];
575
576 /*
577 Copy the to-be-removed action object to temporary storage before
578 the shift copies the string pointers over. Do not use assignment
579 because it would use assignment operator methods for the Strings.
580 This would copy the strings. The shift below overwrite the string
581 pointers without freeing them first. By using memmove() we save
582 the pointers, which are overwritten by the shift.
583 */
584 memmove(save_action, action, sizeof(st_debug_sync_action));
585
586 /* Move actions down. */
587 memmove(ds_control->ds_action + dsp_idx,
588 ds_control->ds_action + dsp_idx + 1,
589 (ds_control->ds_active - dsp_idx) *
590 sizeof(st_debug_sync_action));
591
592 /*
593 Copy back the saved action object to the now free array slot. This
594 replaces the double references of String pointers that have been
595 produced by the shift. Again do not use an assignment operator to
596 avoid string allocation/copy.
597 */
598 memmove(ds_control->ds_action + ds_control->ds_active, save_action,
599 sizeof(st_debug_sync_action));
600 }
601
602 DBUG_VOID_RETURN;
603}
604
605
606/**
607 Get a debug sync action.
608
609 @param[in] thd thread handle
610 @param[in] dsp_name debug sync point name
611 @param[in] name_len length of sync point name
612
613 @return action
614 @retval != NULL ok
615 @retval NULL error
616
617 @description
618 Find the debug sync action for a debug sync point or make a new one.
619*/
620
621static st_debug_sync_action *debug_sync_get_action(THD *thd,
622 const char *dsp_name,
623 uint name_len)
624{
625 st_debug_sync_control *ds_control= thd->debug_sync_control;
626 st_debug_sync_action *action;
627 DBUG_ENTER("debug_sync_get_action");
628 DBUG_ASSERT(thd);
629 DBUG_ASSERT(dsp_name);
630 DBUG_ASSERT(name_len);
631 DBUG_ASSERT(ds_control);
632 DBUG_PRINT("debug_sync", ("sync_point: '%.*s'", (int) name_len, dsp_name));
633 DBUG_PRINT("debug_sync", ("active: %u allocated: %u",
634 ds_control->ds_active, ds_control->ds_allocated));
635
636 /* There cannot be more active actions than allocated. */
637 DBUG_ASSERT(ds_control->ds_active <= ds_control->ds_allocated);
638 /* If there are active actions, the action array must be present. */
639 DBUG_ASSERT(!ds_control->ds_active || ds_control->ds_action);
640
641 /* Try to reuse existing action if there is one for this sync point. */
642 if (ds_control->ds_active &&
643 (action= debug_sync_find(ds_control->ds_action, ds_control->ds_active,
644 dsp_name, name_len)))
645 {
646 /* Reuse an already active sync point action. */
647 DBUG_ASSERT((uint)(action - ds_control->ds_action) < ds_control->ds_active);
648 DBUG_PRINT("debug_sync", ("reuse action idx: %ld",
649 (long) (action - ds_control->ds_action)));
650 }
651 else
652 {
653 /* Create a new action. */
654 int dsp_idx= ds_control->ds_active++;
655 set_if_bigger(ds_control->dsp_max_active, ds_control->ds_active);
656 if (ds_control->ds_active > ds_control->ds_allocated)
657 {
658 uint new_alloc= ds_control->ds_active + 3;
659 void *new_action= my_realloc(ds_control->ds_action,
660 new_alloc * sizeof(st_debug_sync_action),
661 MYF(MY_WME | MY_ALLOW_ZERO_PTR));
662 if (!new_action)
663 {
664 /* Error is reported by my_malloc(). */
665 goto err; /* purecov: tested */
666 }
667 ds_control->ds_action= (st_debug_sync_action*) new_action;
668 ds_control->ds_allocated= new_alloc;
669 /* Clear memory as we do not run string constructors here. */
670 bzero((uchar*) (ds_control->ds_action + dsp_idx),
671 (new_alloc - dsp_idx) * sizeof(st_debug_sync_action));
672 }
673 DBUG_PRINT("debug_sync", ("added action idx: %u", dsp_idx));
674 action= ds_control->ds_action + dsp_idx;
675 if (action->sync_point.copy(dsp_name, name_len, system_charset_info))
676 {
677 /* Error is reported by my_malloc(). */
678 goto err; /* purecov: tested */
679 }
680 action->need_sort= TRUE;
681 }
682 DBUG_ASSERT(action >= ds_control->ds_action);
683 DBUG_ASSERT(action < ds_control->ds_action + ds_control->ds_active);
684 DBUG_PRINT("debug_sync", ("action: %p array: %p count: %u",
685 action, ds_control->ds_action,
686 ds_control->ds_active));
687
688 DBUG_RETURN(action);
689
690 /* purecov: begin tested */
691 err:
692 DBUG_RETURN(NULL);
693 /* purecov: end */
694}
695
696
697/**
698 Set a debug sync action.
699
700 @param[in] thd thread handle
701 @param[in] action synchronization action
702
703 @return status
704 @retval FALSE ok
705 @retval TRUE error
706
707 @description
708 This is called from the debug sync parser. It arms the action for
709 the requested sync point. If the action parsed into an empty action,
710 it is removed instead.
711
712 Setting an action for a sync point means to make the sync point
713 active. When it is hit it will execute this action.
714
715 Before parsing, we "get" an action object. This is placed at the
716 end of the thread's action array unless the requested sync point
717 has an action already.
718
719 Then the parser fills the action object from the request string.
720
721 Finally the action is "set" for the sync point. If it was parsed
722 to be empty, it is removed from the array. If it did belong to a
723 sync point before, the sync point becomes inactive. If the action
724 became non-empty and it did not belong to a sync point before (it
725 was added at the end of the action array), the action array needs
726 to be sorted by sync point.
727
728 If the sync point name is "now", it is executed immediately.
729*/
730
731static bool debug_sync_set_action(THD *thd, st_debug_sync_action *action)
732{
733 st_debug_sync_control *ds_control= thd->debug_sync_control;
734 bool is_dsp_now= FALSE;
735 DBUG_ENTER("debug_sync_set_action");
736 DBUG_ASSERT(thd);
737 DBUG_ASSERT(action);
738 DBUG_ASSERT(ds_control);
739
740 action->activation_count= MY_MAX(action->hit_limit, action->execute);
741 if (!action->activation_count)
742 {
743 debug_sync_remove_action(ds_control, action);
744 DBUG_PRINT("debug_sync", ("action cleared"));
745 }
746 else
747 {
748 const char *dsp_name= action->sync_point.c_ptr();
749 DBUG_EXECUTE("debug_sync", {
750 /* Functions as DBUG_PRINT args can change keyword and line nr. */
751 const char *sig_emit= action->signal.c_ptr();
752 const char *sig_wait= action->wait_for.c_ptr();
753 DBUG_PRINT("debug_sync",
754 ("sync_point: '%s' activation_count: %lu hit_limit: %lu "
755 "execute: %lu timeout: %lu signal: '%s' wait_for: '%s'",
756 dsp_name, action->activation_count,
757 action->hit_limit, action->execute, action->timeout,
758 sig_emit, sig_wait));});
759
760 /* Check this before sorting the array. action may move. */
761 is_dsp_now= !my_strcasecmp(system_charset_info, dsp_name, "now");
762
763 if (action->need_sort)
764 {
765 action->need_sort= FALSE;
766 /* Sort actions by (name_len, name). */
767 my_qsort(ds_control->ds_action, ds_control->ds_active,
768 sizeof(st_debug_sync_action), debug_sync_qsort_cmp);
769 }
770 }
771 DBUG_EXECUTE("debug_sync_list", debug_sync_print_actions(thd););
772
773 /* Execute the special sync point 'now' if activated above. */
774 if (is_dsp_now)
775 {
776 DEBUG_SYNC(thd, "now");
777 /*
778 If HIT_LIMIT for sync point "now" was 1, the execution of the sync
779 point decremented it to 0. In this case the following happened:
780
781 - an error message was reported with my_error() and
782 - the statement was killed with thd->killed= THD::KILL_QUERY.
783
784 If a statement reports an error, it must not call send_ok().
785 The calling functions will not call send_ok(), if we return TRUE
786 from this function.
787
788 thd->killed is also set if the wait is interrupted from a
789 KILL or KILL QUERY statement. In this case, no error is reported
790 and shall not be reported as a result of SET DEBUG_SYNC.
791 Hence, we check for the first condition above.
792 */
793 if (unlikely(thd->is_error()))
794 DBUG_RETURN(TRUE);
795 }
796
797 DBUG_RETURN(FALSE);
798}
799
800
801/**
802 Extract a token from a string.
803
804 @param[out] token_p returns start of token
805 @param[out] token_length_p returns length of token
806 @param[in,out] ptr current string pointer, adds '\0' terminators
807
808 @return string pointer or NULL
809 @retval != NULL ptr behind token terminator or at string end
810 @retval NULL no token found in remainder of string
811
812 @note
813 This function assumes that the string is in system_charset_info,
814 that this charset is single byte for ASCII NUL ('\0'), that no
815 character except of ASCII NUL ('\0') contains a byte with value 0,
816 and that ASCII NUL ('\0') is used as the string terminator.
817
818 This function needs to return tokens that are terminated with ASCII
819 NUL ('\0'). The tokens are used in my_strcasecmp(). Unfortunately
820 there is no my_strncasecmp().
821
822 To return the last token without copying it, we require the input
823 string to be nul terminated.
824
825 @description
826 This function skips space characters at string begin.
827
828 It returns a pointer to the first non-space character in *token_p.
829
830 If no non-space character is found before the string terminator
831 ASCII NUL ('\0'), the function returns NULL. *token_p and
832 *token_length_p remain unchanged in this case (they are not set).
833
834 The function takes a space character or an ASCII NUL ('\0') as a
835 terminator of the token. The space character could be multi-byte.
836
837 It returns the length of the token in bytes, excluding the
838 terminator, in *token_length_p.
839
840 If the terminator of the token is ASCII NUL ('\0'), it returns a
841 pointer to the terminator (string end).
842
843 If the terminator is a space character, it replaces the the first
844 byte of the terminator character by ASCII NUL ('\0'), skips the (now
845 corrupted) terminator character, and skips all following space
846 characters. It returns a pointer to the next non-space character or
847 to the string terminator ASCII NUL ('\0').
848*/
849
850static char *debug_sync_token(char **token_p, uint *token_length_p,
851 char *ptr, char *ptrend)
852{
853 DBUG_ASSERT(token_p);
854 DBUG_ASSERT(token_length_p);
855 DBUG_ASSERT(ptr);
856
857 /* Skip leading space */
858 ptr+= system_charset_info->cset->scan(system_charset_info,
859 ptr, ptrend, MY_SEQ_SPACES);
860 if (!*ptr)
861 {
862 ptr= NULL;
863 goto end;
864 }
865
866 /* Get token start. */
867 *token_p= ptr;
868
869 /* Find token end. */
870 ptr+= system_charset_info->cset->scan(system_charset_info,
871 ptr, ptrend, MY_SEQ_NONSPACES);
872
873 /* Get token length. */
874 *token_length_p= (uint)(ptr - *token_p);
875
876 /* If necessary, terminate token. */
877 if (*ptr)
878 {
879 DBUG_ASSERT(ptr < ptrend);
880 /* Get terminator character length. */
881 uint mbspacelen= my_charlen_fix(system_charset_info, ptr, ptrend);
882
883 /* Terminate token. */
884 *ptr= '\0';
885
886 /* Skip the terminator. */
887 ptr+= mbspacelen;
888
889 /* Skip trailing space */
890 ptr+= system_charset_info->cset->scan(system_charset_info,
891 ptr, ptrend, MY_SEQ_SPACES);
892 }
893
894 end:
895 return ptr;
896}
897
898
899/**
900 Extract a number from a string.
901
902 @param[out] number_p returns number
903 @param[in] actstrptr current pointer in action string
904
905 @return string pointer or NULL
906 @retval != NULL ptr behind token terminator or at string end
907 @retval NULL no token found or token is not valid number
908
909 @note
910 The same assumptions about charset apply as for debug_sync_token().
911
912 @description
913 This function fetches a token from the string and converts it
914 into a number.
915
916 If there is no token left in the string, or the token is not a valid
917 decimal number, NULL is returned. The result in *number_p is
918 undefined in this case.
919*/
920
921static char *debug_sync_number(ulong *number_p, char *actstrptr,
922 char *actstrend)
923{
924 char *ptr;
925 char *ept;
926 char *token;
927 uint token_length;
928 DBUG_ASSERT(number_p);
929 DBUG_ASSERT(actstrptr);
930
931 /* Get token from string. */
932 if (!(ptr= debug_sync_token(&token, &token_length, actstrptr, actstrend)))
933 goto end;
934
935 *number_p= strtoul(token, &ept, 10);
936 if (*ept)
937 ptr= NULL;
938
939 end:
940 return ptr;
941}
942
943
944/**
945 Evaluate a debug sync action string.
946
947 @param[in] thd thread handle
948 @param[in,out] action_str action string to receive '\0' terminators
949
950 @return status
951 @retval FALSE ok
952 @retval TRUE error
953
954 @description
955 This is called when the DEBUG_SYNC system variable is set.
956 Parse action string, build a debug sync action, activate it.
957
958 Before parsing, we "get" an action object. This is placed at the
959 end of the thread's action array unless the requested sync point
960 has an action already.
961
962 Then the parser fills the action object from the request string.
963
964 Finally the action is "set" for the sync point. This means that the
965 sync point becomes active or inactive, depending on the action
966 values.
967
968 @note
969 The input string needs to be ASCII NUL ('\0') terminated. We split
970 nul-terminated tokens in it without copy.
971
972 @see the function comment of debug_sync_token() for more constraints
973 for the string.
974*/
975
976static bool debug_sync_eval_action(THD *thd, char *action_str, char *action_end)
977{
978 st_debug_sync_action *action= NULL;
979 const char *errmsg;
980 char *ptr;
981 char *token;
982 uint token_length= 0;
983 DBUG_ENTER("debug_sync_eval_action");
984 DBUG_ASSERT(thd);
985 DBUG_ASSERT(action_str);
986 DBUG_PRINT("debug_sync", ("action_str: '%s'", action_str));
987
988 /*
989 Get debug sync point name. Or a special command.
990 */
991 if (!(ptr= debug_sync_token(&token, &token_length, action_str, action_end)))
992 {
993 errmsg= "Missing synchronization point name";
994 goto err;
995 }
996
997 /*
998 If there is a second token, the first one is the sync point name.
999 */
1000 if (*ptr)
1001 {
1002 /* Get an action object to collect the requested action parameters. */
1003 action= debug_sync_get_action(thd, token, token_length);
1004 if (!action)
1005 {
1006 /* Error message is sent. */
1007 DBUG_RETURN(TRUE); /* purecov: tested */
1008 }
1009 }
1010
1011 /*
1012 Get kind of action to be taken at sync point.
1013 */
1014 if (!(ptr= debug_sync_token(&token, &token_length, ptr, action_end)))
1015 {
1016 /* No action present. Try special commands. Token unchanged. */
1017
1018 /*
1019 Try RESET.
1020 */
1021 if (!my_strcasecmp(system_charset_info, token, "RESET"))
1022 {
1023 /* It is RESET. Reset all actions and global signal. */
1024 debug_sync_reset(thd);
1025 goto end;
1026 }
1027
1028 /* Token unchanged. It still contains sync point name. */
1029 errmsg= "Missing action after synchronization point name '%.*s'";
1030 goto err;
1031 }
1032
1033 /*
1034 Check for pseudo actions first. Start with actions that work on
1035 an existing action.
1036 */
1037 DBUG_ASSERT(action);
1038
1039 /*
1040 Try TEST.
1041 */
1042 if (!my_strcasecmp(system_charset_info, token, "TEST"))
1043 {
1044 /* It is TEST. Nothing must follow it. */
1045 if (*ptr)
1046 {
1047 errmsg= "Nothing must follow action TEST";
1048 goto err;
1049 }
1050
1051 /* Execute sync point. */
1052 debug_sync(thd, action->sync_point.ptr(), action->sync_point.length());
1053 /* Fix statistics. This was not a real hit of the sync point. */
1054 thd->debug_sync_control->dsp_hits--;
1055 goto end;
1056 }
1057
1058 /*
1059 Now check for actions that define a new action.
1060 Initialize action. Do not use bzero(). Strings may have malloced.
1061 */
1062 action->activation_count= 0;
1063 action->hit_limit= 0;
1064 action->execute= 0;
1065 action->timeout= 0;
1066 action->signal.length(0);
1067 action->wait_for.length(0);
1068
1069 /*
1070 Try CLEAR.
1071 */
1072 if (!my_strcasecmp(system_charset_info, token, "CLEAR"))
1073 {
1074 /* It is CLEAR. Nothing must follow it. */
1075 if (*ptr)
1076 {
1077 errmsg= "Nothing must follow action CLEAR";
1078 goto err;
1079 }
1080
1081 /* Set (clear/remove) action. */
1082 goto set_action;
1083 }
1084
1085 /*
1086 Now check for real sync point actions.
1087 */
1088
1089 /*
1090 Try SIGNAL.
1091 */
1092 if (!my_strcasecmp(system_charset_info, token, "SIGNAL"))
1093 {
1094 /* It is SIGNAL. Signal name must follow. */
1095 if (!(ptr= debug_sync_token(&token, &token_length, ptr, action_end)))
1096 {
1097 errmsg= "Missing signal name after action SIGNAL";
1098 goto err;
1099 }
1100 if (action->signal.copy(token, token_length, system_charset_info))
1101 {
1102 /* Error is reported by my_malloc(). */
1103 /* purecov: begin tested */
1104 errmsg= NULL;
1105 goto err;
1106 /* purecov: end */
1107 }
1108
1109 /* Set default for EXECUTE option. */
1110 action->execute= 1;
1111
1112 /* Get next token. If none follows, set action. */
1113 if (!(ptr= debug_sync_token(&token, &token_length, ptr, action_end)))
1114 goto set_action;
1115 }
1116
1117 /*
1118 Try WAIT_FOR.
1119 */
1120 if (!my_strcasecmp(system_charset_info, token, "WAIT_FOR"))
1121 {
1122 /* It is WAIT_FOR. Wait_for signal name must follow. */
1123 if (!(ptr= debug_sync_token(&token, &token_length, ptr, action_end)))
1124 {
1125 errmsg= "Missing signal name after action WAIT_FOR";
1126 goto err;
1127 }
1128 if (action->wait_for.copy(token, token_length, system_charset_info))
1129 {
1130 /* Error is reported by my_malloc(). */
1131 /* purecov: begin tested */
1132 errmsg= NULL;
1133 goto err;
1134 /* purecov: end */
1135 }
1136
1137 /* Set default for EXECUTE and TIMEOUT options. */
1138 action->execute= 1;
1139 action->timeout= opt_debug_sync_timeout;
1140
1141 /* Get next token. If none follows, set action. */
1142 if (!(ptr= debug_sync_token(&token, &token_length, ptr, action_end)))
1143 goto set_action;
1144
1145 /*
1146 Try TIMEOUT.
1147 */
1148 if (!my_strcasecmp(system_charset_info, token, "TIMEOUT"))
1149 {
1150 /* It is TIMEOUT. Number must follow. */
1151 if (!(ptr= debug_sync_number(&action->timeout, ptr, action_end)))
1152 {
1153 errmsg= "Missing valid number after TIMEOUT";
1154 goto err;
1155 }
1156
1157 /* Get next token. If none follows, set action. */
1158 if (!(ptr= debug_sync_token(&token, &token_length, ptr, action_end)))
1159 goto set_action;
1160 }
1161 }
1162
1163 /*
1164 Try EXECUTE.
1165 */
1166 if (!my_strcasecmp(system_charset_info, token, "EXECUTE"))
1167 {
1168 /*
1169 EXECUTE requires either SIGNAL and/or WAIT_FOR to be present.
1170 In this case action->execute has been preset to 1.
1171 */
1172 if (!action->execute)
1173 {
1174 errmsg= "Missing action before EXECUTE";
1175 goto err;
1176 }
1177
1178 /* Number must follow. */
1179 if (!(ptr= debug_sync_number(&action->execute, ptr, action_end)))
1180 {
1181 errmsg= "Missing valid number after EXECUTE";
1182 goto err;
1183 }
1184
1185 /* Get next token. If none follows, set action. */
1186 if (!(ptr= debug_sync_token(&token, &token_length, ptr, action_end)))
1187 goto set_action;
1188 }
1189
1190 /*
1191 Try HIT_LIMIT.
1192 */
1193 if (!my_strcasecmp(system_charset_info, token, "HIT_LIMIT"))
1194 {
1195 /* Number must follow. */
1196 if (!(ptr= debug_sync_number(&action->hit_limit, ptr, action_end)))
1197 {
1198 errmsg= "Missing valid number after HIT_LIMIT";
1199 goto err;
1200 }
1201
1202 /* Get next token. If none follows, set action. */
1203 if (!(ptr= debug_sync_token(&token, &token_length, ptr, action_end)))
1204 goto set_action;
1205 }
1206
1207 errmsg= "Illegal or out of order stuff: '%.*s'";
1208
1209 err:
1210 if (errmsg)
1211 {
1212 /*
1213 NOTE: errmsg must either have %.*s or none % at all.
1214 It can be NULL if an error message is already reported
1215 (e.g. by my_malloc()).
1216 */
1217 set_if_smaller(token_length, 64); /* Limit error message length. */
1218 my_printf_error(ER_PARSE_ERROR, errmsg, MYF(0), token_length, token);
1219 }
1220 if (action)
1221 debug_sync_remove_action(thd->debug_sync_control, action);
1222 DBUG_RETURN(TRUE);
1223
1224 set_action:
1225 DBUG_RETURN(debug_sync_set_action(thd, action));
1226
1227 end:
1228 DBUG_RETURN(FALSE);
1229}
1230
1231/**
1232 Set the system variable 'debug_sync'.
1233
1234 @param[in] thd thread handle
1235 @param[in] var set variable request
1236
1237 @return status
1238 @retval FALSE ok, variable is set
1239 @retval TRUE error, variable could not be set
1240
1241 @note
1242 "Setting" of the system variable 'debug_sync' does not mean to
1243 assign a value to it as usual. Instead a debug sync action is parsed
1244 from the input string and stored apart from the variable value.
1245
1246 @note
1247 For efficiency reasons, the action string parser places '\0'
1248 terminators in the string. So we need to take a copy here.
1249*/
1250
1251bool debug_sync_update(THD *thd, char *val_str, size_t len)
1252{
1253 DBUG_ENTER("debug_sync_update");
1254 DBUG_PRINT("debug_sync", ("set action: '%s'", val_str));
1255
1256 /*
1257 debug_sync_eval_action() places '\0' in the string, which itself
1258 must be '\0' terminated.
1259 */
1260 DBUG_ASSERT(val_str[len] == '\0');
1261 DBUG_RETURN(opt_debug_sync_timeout ?
1262 debug_sync_eval_action(thd, val_str, val_str + len) :
1263 FALSE);
1264}
1265
1266
1267/**
1268 Retrieve the value of the system variable 'debug_sync'.
1269
1270 @param[in] thd thread handle
1271
1272 @return string
1273 @retval != NULL ok, string pointer
1274 @retval NULL memory allocation error
1275
1276 @note
1277 The value of the system variable 'debug_sync' reflects if
1278 the facility is enabled ("ON") or disabled (default, "OFF").
1279
1280 When "ON", the current signal is added.
1281*/
1282
1283uchar *debug_sync_value_ptr(THD *thd)
1284{
1285 char *value;
1286 DBUG_ENTER("debug_sync_value_ptr");
1287
1288 if (opt_debug_sync_timeout)
1289 {
1290 static char on[]= "ON - current signal: '";
1291
1292 // Ensure exclusive access to debug_sync_global.ds_signal
1293 mysql_mutex_lock(&debug_sync_global.ds_mutex);
1294
1295 size_t lgt= (sizeof(on) /* includes '\0' */ +
1296 debug_sync_global.ds_signal.length() + 1 /* for '\'' */);
1297 char *vend;
1298 char *vptr;
1299
1300 if ((value= (char*) alloc_root(thd->mem_root, lgt)))
1301 {
1302 vend= value + lgt - 1; /* reserve space for '\0'. */
1303 vptr= debug_sync_bmove_len(value, vend, STRING_WITH_LEN(on));
1304 vptr= debug_sync_bmove_len(vptr, vend, debug_sync_global.ds_signal.ptr(),
1305 debug_sync_global.ds_signal.length());
1306 if (vptr < vend)
1307 *(vptr++)= '\'';
1308 *vptr= '\0'; /* We have one byte reserved for the worst case. */
1309 }
1310 mysql_mutex_unlock(&debug_sync_global.ds_mutex);
1311 }
1312 else
1313 {
1314 /* purecov: begin tested */
1315 value= const_cast<char*>("OFF");
1316 /* purecov: end */
1317 }
1318
1319 DBUG_RETURN((uchar*) value);
1320}
1321
1322
1323/**
1324 Execute requested action at a synchronization point.
1325
1326 @param[in] thd thread handle
1327 @param[in] action action to be executed
1328
1329 @note
1330 This is to be called only if activation count > 0.
1331*/
1332
1333static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
1334{
1335#ifndef DBUG_OFF
1336 const char *dsp_name= action->sync_point.c_ptr();
1337 const char *sig_emit= action->signal.c_ptr();
1338 const char *sig_wait= action->wait_for.c_ptr();
1339#endif
1340 DBUG_ENTER("debug_sync_execute");
1341 DBUG_ASSERT(thd);
1342 DBUG_ASSERT(action);
1343 DBUG_PRINT("debug_sync",
1344 ("sync_point: '%s' activation_count: %lu hit_limit: %lu "
1345 "execute: %lu timeout: %lu signal: '%s' wait_for: '%s'",
1346 dsp_name, action->activation_count, action->hit_limit,
1347 action->execute, action->timeout, sig_emit, sig_wait));
1348
1349 DBUG_ASSERT(action->activation_count);
1350 action->activation_count--;
1351
1352 if (action->execute)
1353 {
1354 const char *UNINIT_VAR(old_proc_info);
1355
1356 action->execute--;
1357
1358 /*
1359 If we will be going to wait, set proc_info for the PROCESSLIST table.
1360 Do this before emitting the signal, so other threads can see it
1361 if they awake before we enter_cond() below.
1362 */
1363 if (action->wait_for.length())
1364 {
1365 st_debug_sync_control *ds_control= thd->debug_sync_control;
1366 strxnmov(ds_control->ds_proc_info, sizeof(ds_control->ds_proc_info)-1,
1367 "debug sync point: ", action->sync_point.c_ptr(), NullS);
1368 old_proc_info= thd->proc_info;
1369 thd_proc_info(thd, ds_control->ds_proc_info);
1370 }
1371
1372 /*
1373 Take mutex to ensure that only one thread access
1374 debug_sync_global.ds_signal at a time. Need to take mutex for
1375 read access too, to create a memory barrier in order to avoid that
1376 threads just reads an old cached version of the signal.
1377 */
1378 mysql_mutex_lock(&debug_sync_global.ds_mutex);
1379
1380 if (action->signal.length())
1381 {
1382 /* Copy the signal to the global variable. */
1383 if (debug_sync_global.ds_signal.copy(action->signal))
1384 {
1385 /*
1386 Error is reported by my_malloc().
1387 We must disable the facility. We have no way to return an error.
1388 */
1389 debug_sync_emergency_disable(); /* purecov: tested */
1390 }
1391 /* Wake threads waiting in a sync point. */
1392 mysql_cond_broadcast(&debug_sync_global.ds_cond);
1393 DBUG_PRINT("debug_sync_exec", ("signal '%s' at: '%s'",
1394 sig_emit, dsp_name));
1395 } /* end if (action->signal.length()) */
1396
1397 if (action->wait_for.length())
1398 {
1399 mysql_mutex_t *old_mutex= NULL;
1400 mysql_cond_t *old_cond= NULL;
1401 bool restore_current_mutex;
1402 int error= 0;
1403 struct timespec abstime;
1404
1405 /*
1406 We don't use enter_cond()/exit_cond(). They do not save old
1407 mutex and cond. This would prohibit the use of DEBUG_SYNC
1408 between other places of enter_cond() and exit_cond().
1409
1410 We need to check for existence of thd->mysys_var to also make
1411 it possible to use DEBUG_SYNC framework in scheduler when this
1412 variable has been set to NULL.
1413 */
1414 if (thd->mysys_var)
1415 {
1416 old_mutex= thd->mysys_var->current_mutex;
1417 old_cond= thd->mysys_var->current_cond;
1418 restore_current_mutex = true;
1419 thd->mysys_var->current_mutex= &debug_sync_global.ds_mutex;
1420 thd->mysys_var->current_cond= &debug_sync_global.ds_cond;
1421 }
1422 else
1423 restore_current_mutex = false;
1424
1425 set_timespec(abstime, action->timeout);
1426 DBUG_EXECUTE("debug_sync_exec", {
1427 /* Functions as DBUG_PRINT args can change keyword and line nr. */
1428 const char *sig_glob= debug_sync_global.ds_signal.c_ptr();
1429 DBUG_PRINT("debug_sync_exec",
1430 ("wait for '%s' at: '%s' curr: '%s'",
1431 sig_wait, dsp_name, sig_glob));});
1432
1433 /*
1434 Wait until global signal string matches the wait_for string.
1435 Interrupt when thread or query is killed or facility disabled.
1436 The facility can become disabled when some thread cannot get
1437 the required dynamic memory allocated.
1438 */
1439 while (stringcmp(&debug_sync_global.ds_signal, &action->wait_for) &&
1440 !thd->killed && opt_debug_sync_timeout)
1441 {
1442 error= mysql_cond_timedwait(&debug_sync_global.ds_cond,
1443 &debug_sync_global.ds_mutex,
1444 &abstime);
1445 DBUG_EXECUTE("debug_sync", {
1446 /* Functions as DBUG_PRINT args can change keyword and line nr. */
1447 const char *sig_glob= debug_sync_global.ds_signal.c_ptr();
1448 DBUG_PRINT("debug_sync",
1449 ("awoke from %s global: %s error: %d",
1450 sig_wait, sig_glob, error));});
1451 if (unlikely(error == ETIMEDOUT || error == ETIME))
1452 {
1453 // We should not make the statement fail, even if in strict mode.
1454 const bool save_abort_on_warning= thd->abort_on_warning;
1455 thd->abort_on_warning= false;
1456 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
1457 ER_DEBUG_SYNC_TIMEOUT,
1458 ER_THD(thd, ER_DEBUG_SYNC_TIMEOUT));
1459 thd->abort_on_warning= save_abort_on_warning;
1460 DBUG_EXECUTE_IF("debug_sync_abort_on_timeout", DBUG_ASSERT(0););
1461 break;
1462 }
1463 error= 0;
1464 }
1465 DBUG_EXECUTE("debug_sync_exec",
1466 if (thd->killed)
1467 DBUG_PRINT("debug_sync_exec",
1468 ("killed %d from '%s' at: '%s'",
1469 thd->killed, sig_wait, dsp_name));
1470 else
1471 DBUG_PRINT("debug_sync_exec",
1472 ("%s from '%s' at: '%s'",
1473 error ? "timeout" : "resume",
1474 sig_wait, dsp_name)););
1475
1476 /*
1477 We don't use enter_cond()/exit_cond(). They do not save old
1478 mutex and cond. This would prohibit the use of DEBUG_SYNC
1479 between other places of enter_cond() and exit_cond(). The
1480 protected mutex must always unlocked _before_ mysys_var->mutex
1481 is locked. (See comment in THD::exit_cond().)
1482 */
1483 mysql_mutex_unlock(&debug_sync_global.ds_mutex);
1484 if (restore_current_mutex)
1485 {
1486 mysql_mutex_lock(&thd->mysys_var->mutex);
1487 thd->mysys_var->current_mutex= old_mutex;
1488 thd->mysys_var->current_cond= old_cond;
1489 thd_proc_info(thd, old_proc_info);
1490 mysql_mutex_unlock(&thd->mysys_var->mutex);
1491 }
1492 else
1493 thd_proc_info(thd, old_proc_info);
1494 }
1495 else
1496 {
1497 /* In case we don't wait, we just release the mutex. */
1498 mysql_mutex_unlock(&debug_sync_global.ds_mutex);
1499 } /* end if (action->wait_for.length()) */
1500
1501 } /* end if (action->execute) */
1502
1503 /* hit_limit is zero for infinite. Don't decrement unconditionally. */
1504 if (action->hit_limit)
1505 {
1506 if (!--action->hit_limit)
1507 {
1508 thd->set_killed(KILL_QUERY);
1509 my_error(ER_DEBUG_SYNC_HIT_LIMIT, MYF(0));
1510 }
1511 DBUG_PRINT("debug_sync_exec", ("hit_limit: %lu at: '%s'",
1512 action->hit_limit, dsp_name));
1513 }
1514
1515 DBUG_VOID_RETURN;
1516}
1517
1518
1519/**
1520 Execute requested action at a synchronization point.
1521
1522 @param[in] thd thread handle
1523 @param[in] sync_point_name name of synchronization point
1524 @param[in] name_len length of sync point name
1525*/
1526
1527static void debug_sync(THD *thd, const char *sync_point_name, size_t name_len)
1528{
1529 if (!thd)
1530 {
1531 if (!(thd= current_thd))
1532 return;
1533 }
1534
1535 st_debug_sync_control *ds_control= thd->debug_sync_control;
1536 st_debug_sync_action *action;
1537 DBUG_ENTER("debug_sync");
1538 DBUG_ASSERT(sync_point_name);
1539 DBUG_ASSERT(name_len);
1540 DBUG_ASSERT(ds_control);
1541 DBUG_PRINT("debug_sync_point", ("hit: '%s'", sync_point_name));
1542
1543 /* Statistics. */
1544 ds_control->dsp_hits++;
1545
1546 if (ds_control->ds_active &&
1547 (action= debug_sync_find(ds_control->ds_action, ds_control->ds_active,
1548 sync_point_name, name_len)) &&
1549 action->activation_count)
1550 {
1551 /* Sync point is active (action exists). */
1552 debug_sync_execute(thd, action);
1553
1554 /* Statistics. */
1555 ds_control->dsp_executed++;
1556
1557 /* If action became inactive, remove it to shrink the search array. */
1558 if (!action->activation_count)
1559 debug_sync_remove_action(ds_control, action);
1560 }
1561
1562 DBUG_VOID_RETURN;
1563}
1564
1565/**
1566 Define debug sync action.
1567
1568 @param[in] thd thread handle
1569 @param[in] action_str action string
1570
1571 @return status
1572 @retval FALSE ok
1573 @retval TRUE error
1574
1575 @description
1576 The function is similar to @c debug_sync_eval_action but is
1577 to be called immediately from the server code rather than
1578 to be triggered by setting a value to DEBUG_SYNC system variable.
1579
1580 @note
1581 The input string is copied prior to be fed to
1582 @c debug_sync_eval_action to let the latter modify it.
1583
1584 Caution.
1585 The function allocates in THD::mem_root and therefore
1586 is not recommended to be deployed inside big loops.
1587*/
1588
1589bool debug_sync_set_action(THD *thd, const char *action_str, size_t len)
1590{
1591 bool rc;
1592 char *value;
1593 DBUG_ENTER("debug_sync_set_action");
1594 DBUG_ASSERT(thd);
1595 DBUG_ASSERT(action_str);
1596
1597 value= strmake_root(thd->mem_root, action_str, len);
1598 rc= debug_sync_eval_action(thd, value, value + len);
1599 DBUG_RETURN(rc);
1600}
1601
1602
1603#else /* defined(ENABLED_DEBUG_SYNC) */
1604/* prevent linker/lib warning about file without public symbols */
1605int debug_sync_dummy;
1606#endif /* defined(ENABLED_DEBUG_SYNC) */
1607