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