MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
debug_sync.cc
1 /* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License 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 
318 #include "debug_sync.h"
319 
320 #if defined(ENABLED_DEBUG_SYNC)
321 
322 /*
323  Due to weaknesses in our include files, we need to include
324  sql_priv.h here. To have THD declared, we need to include
325  sql_class.h. This includes log_event.h, which in turn requires
326  declarations from sql_priv.h (e.g. OPTION_AUTO_IS_NULL).
327  sql_priv.h includes almost everything, so is sufficient here.
328 */
329 #include "sql_priv.h"
330 #include "sql_parse.h"
331 
332 using std::max;
333 using std::min;
334 
335 /*
336  Action to perform at a synchronization point.
337  NOTE: This structure is moved around in memory by realloc(), qsort(),
338  and memmove(). Do not add objects with non-trivial constuctors
339  or destructors, which might prevent moving of this structure
340  with these functions.
341 */
342 struct st_debug_sync_action
343 {
344  ulong activation_count; /* max(hit_limit, execute) */
345  ulong hit_limit; /* hits before kill query */
346  ulong execute; /* executes before self-clear */
347  ulong timeout; /* wait_for timeout */
348  String signal; /* signal to emit */
349  String wait_for; /* signal to wait for */
350  String sync_point; /* sync point name */
351  bool need_sort; /* if new action, array needs sort */
352 };
353 
354 /* Debug sync control. Referenced by THD. */
355 struct st_debug_sync_control
356 {
357  st_debug_sync_action *ds_action; /* array of actions */
358  uint ds_active; /* # active actions */
359  uint ds_allocated; /* # allocated actions */
360  ulonglong dsp_hits; /* statistics */
361  ulonglong dsp_executed; /* statistics */
362  ulonglong dsp_max_active; /* statistics */
363  /*
364  thd->proc_info points at unsynchronized memory.
365  It must not go away as long as the thread exists.
366  */
367  char ds_proc_info[80]; /* proc_info string */
368 };
369 
370 
377 struct st_debug_sync_globals
378 {
379  String ds_signal; /* signal variable */
380  mysql_cond_t ds_cond; /* condition variable */
381  mysql_mutex_t ds_mutex; /* mutex variable */
382  ulonglong dsp_hits; /* statistics */
383  ulonglong dsp_executed; /* statistics */
384  ulonglong dsp_max_active; /* statistics */
385 };
386 static st_debug_sync_globals debug_sync_global; /* All globals in one object */
387 
391 extern "C" void (*debug_sync_C_callback_ptr)(const char *, size_t);
392 
396 C_MODE_START
397 static void debug_sync_C_callback(const char *, size_t);
398 static int debug_sync_qsort_cmp(const void *, const void *);
399 C_MODE_END
400 
432 static void debug_sync_C_callback(const char *sync_point_name,
433  size_t name_len)
434 {
435  if (unlikely(opt_debug_sync_timeout))
436  debug_sync(current_thd, sync_point_name, name_len);
437 }
438 
439 #ifdef HAVE_PSI_INTERFACE
440 static PSI_mutex_key key_debug_sync_globals_ds_mutex;
441 
442 static PSI_mutex_info all_debug_sync_mutexes[]=
443 {
444  { &key_debug_sync_globals_ds_mutex, "DEBUG_SYNC::mutex", PSI_FLAG_GLOBAL}
445 };
446 
447 static PSI_cond_key key_debug_sync_globals_ds_cond;
448 
449 static PSI_cond_info all_debug_sync_conds[]=
450 {
451  { &key_debug_sync_globals_ds_cond, "DEBUG_SYNC::cond", PSI_FLAG_GLOBAL}
452 };
453 
454 static void init_debug_sync_psi_keys(void)
455 {
456  const char* category= "sql";
457  int count;
458 
459  count= array_elements(all_debug_sync_mutexes);
460  mysql_mutex_register(category, all_debug_sync_mutexes, count);
461 
462  count= array_elements(all_debug_sync_conds);
463  mysql_cond_register(category, all_debug_sync_conds, count);
464 }
465 #endif /* HAVE_PSI_INTERFACE */
466 
467 
476 int debug_sync_init(void)
477 {
478  DBUG_ENTER("debug_sync_init");
479 
480 #ifdef HAVE_PSI_INTERFACE
481  init_debug_sync_psi_keys();
482 #endif
483 
484  if (opt_debug_sync_timeout)
485  {
486  int rc;
487 
488  /* Initialize the global variables. */
489  debug_sync_global.ds_signal.length(0);
490  if ((rc= mysql_cond_init(key_debug_sync_globals_ds_cond,
491  &debug_sync_global.ds_cond, NULL)) ||
492  (rc= mysql_mutex_init(key_debug_sync_globals_ds_mutex,
493  &debug_sync_global.ds_mutex,
494  MY_MUTEX_INIT_FAST)))
495  DBUG_RETURN(rc); /* purecov: inspected */
496 
497  /* Set the call back pointer in C files. */
498  debug_sync_C_callback_ptr= debug_sync_C_callback;
499  }
500 
501  DBUG_RETURN(0);
502 }
503 
504 
512 void debug_sync_end(void)
513 {
514  DBUG_ENTER("debug_sync_end");
515 
516  /* End the facility only if it had been initialized. */
517  if (debug_sync_C_callback_ptr)
518  {
519  /* Clear the call back pointer in C files. */
520  debug_sync_C_callback_ptr= NULL;
521 
522  /* Destroy the global variables. */
523  debug_sync_global.ds_signal.free();
524  mysql_cond_destroy(&debug_sync_global.ds_cond);
525  mysql_mutex_destroy(&debug_sync_global.ds_mutex);
526 
527  /* Print statistics. */
528  {
529  char llbuff[22];
530  sql_print_information("Debug sync points hit: %22s",
531  llstr(debug_sync_global.dsp_hits, llbuff));
532  sql_print_information("Debug sync points executed: %22s",
533  llstr(debug_sync_global.dsp_executed, llbuff));
534  sql_print_information("Debug sync points max active per thread: %22s",
535  llstr(debug_sync_global.dsp_max_active, llbuff));
536  }
537  }
538 
539  DBUG_VOID_RETURN;
540 }
541 
542 
543 /* purecov: begin tested */
544 
553 static void debug_sync_emergency_disable(void)
554 {
555  DBUG_ENTER("debug_sync_emergency_disable");
556 
557  opt_debug_sync_timeout= 0;
558 
559  DBUG_PRINT("debug_sync",
560  ("Debug Sync Facility disabled due to lack of memory."));
561  sql_print_error("Debug Sync Facility disabled due to lack of memory.");
562 
563  DBUG_VOID_RETURN;
564 }
565 
566 /* purecov: end */
567 
568 
575 void debug_sync_init_thread(THD *thd)
576 {
577  DBUG_ENTER("debug_sync_init_thread");
578  DBUG_ASSERT(thd);
579 
580  if (opt_debug_sync_timeout)
581  {
582  thd->debug_sync_control= (st_debug_sync_control*)
583  my_malloc(sizeof(st_debug_sync_control), MYF(MY_WME | MY_ZEROFILL));
584  if (!thd->debug_sync_control)
585  {
586  /*
587  Error is reported by my_malloc().
588  We must disable the facility. We have no way to return an error.
589  */
590  debug_sync_emergency_disable(); /* purecov: tested */
591  }
592  }
593 
594  DBUG_VOID_RETURN;
595 }
596 
597 
604 void debug_sync_end_thread(THD *thd)
605 {
606  DBUG_ENTER("debug_sync_end_thread");
607  DBUG_ASSERT(thd);
608 
609  if (thd->debug_sync_control)
610  {
611  st_debug_sync_control *ds_control= thd->debug_sync_control;
612 
613  /*
614  This synchronization point can be used to synchronize on thread end.
615  This is the latest point in a THD's life, where this can be done.
616  */
617  DEBUG_SYNC(thd, "thread_end");
618 
619  if (ds_control->ds_action)
620  {
621  st_debug_sync_action *action= ds_control->ds_action;
622  st_debug_sync_action *action_end= action + ds_control->ds_allocated;
623  for (; action < action_end; action++)
624  {
625  action->signal.free();
626  action->wait_for.free();
627  action->sync_point.free();
628  }
629  my_free(ds_control->ds_action);
630  }
631 
632  /* Statistics. */
633  mysql_mutex_lock(&debug_sync_global.ds_mutex);
634  debug_sync_global.dsp_hits+= ds_control->dsp_hits;
635  debug_sync_global.dsp_executed+= ds_control->dsp_executed;
636  if (debug_sync_global.dsp_max_active < ds_control->dsp_max_active)
637  debug_sync_global.dsp_max_active= ds_control->dsp_max_active;
638  mysql_mutex_unlock(&debug_sync_global.ds_mutex);
639 
640  my_free(ds_control);
641  thd->debug_sync_control= NULL;
642  }
643 
644  DBUG_VOID_RETURN;
645 }
646 
647 
659 static char *debug_sync_bmove_len(char *to, char *to_end,
660  const char *from, size_t length)
661 {
662  DBUG_ASSERT(to);
663  DBUG_ASSERT(to_end);
664  DBUG_ASSERT(!length || from);
665  set_if_smaller(length, (size_t) (to_end - to));
666  memcpy(to, from, length);
667  return (to + length);
668 }
669 
670 
671 #if !defined(DBUG_OFF)
672 
681 static void debug_sync_action_string(char *result, uint size,
682  st_debug_sync_action *action)
683 {
684  char *wtxt= result;
685  char *wend= wtxt + size - 1; /* Allow emergency '\0'. */
686  DBUG_ASSERT(result);
687  DBUG_ASSERT(action);
688 
689  /* If an execute count is present, signal or wait_for are needed too. */
690  DBUG_ASSERT(!action->execute ||
691  action->signal.length() || action->wait_for.length());
692 
693  if (action->execute)
694  {
695  if (action->signal.length())
696  {
697  wtxt= debug_sync_bmove_len(wtxt, wend, STRING_WITH_LEN("SIGNAL "));
698  wtxt= debug_sync_bmove_len(wtxt, wend, action->signal.ptr(),
699  action->signal.length());
700  }
701  if (action->wait_for.length())
702  {
703  if ((wtxt == result) && (wtxt < wend))
704  *(wtxt++)= ' ';
705  wtxt= debug_sync_bmove_len(wtxt, wend, STRING_WITH_LEN(" WAIT_FOR "));
706  wtxt= debug_sync_bmove_len(wtxt, wend, action->wait_for.ptr(),
707  action->wait_for.length());
708 
709  if (action->timeout != opt_debug_sync_timeout)
710  {
711  wtxt+= my_snprintf(wtxt, wend - wtxt, " TIMEOUT %lu", action->timeout);
712  }
713  }
714  if (action->execute != 1)
715  {
716  wtxt+= my_snprintf(wtxt, wend - wtxt, " EXECUTE %lu", action->execute);
717  }
718  }
719  if (action->hit_limit)
720  {
721  wtxt+= my_snprintf(wtxt, wend - wtxt, "%sHIT_LIMIT %lu",
722  (wtxt == result) ? "" : " ", action->hit_limit);
723  }
724 
725  /*
726  If (wtxt == wend) string may not be terminated.
727  There is one byte left for an emergency termination.
728  */
729  *wtxt= '\0';
730 }
731 
732 
739 static void debug_sync_print_actions(THD *thd)
740 {
741  st_debug_sync_control *ds_control= thd->debug_sync_control;
742  uint idx;
743  DBUG_ENTER("debug_sync_print_actions");
744  DBUG_ASSERT(thd);
745 
746  if (!ds_control)
747  DBUG_VOID_RETURN;
748 
749  for (idx= 0; idx < ds_control->ds_active; idx++)
750  {
751  const char *dsp_name= ds_control->ds_action[idx].sync_point.c_ptr();
752  char action_string[256];
753 
754  debug_sync_action_string(action_string, sizeof(action_string),
755  ds_control->ds_action + idx);
756  DBUG_PRINT("debug_sync_list", ("%s %s", dsp_name, action_string));
757  }
758 
759  DBUG_VOID_RETURN;
760 }
761 
762 #endif /* !defined(DBUG_OFF) */
763 
764 
777 static int debug_sync_qsort_cmp(const void* arg1, const void* arg2)
778 {
779  st_debug_sync_action *action1= (st_debug_sync_action*) arg1;
780  st_debug_sync_action *action2= (st_debug_sync_action*) arg2;
781  int diff;
782  DBUG_ASSERT(action1);
783  DBUG_ASSERT(action2);
784 
785  if (!(diff= action1->sync_point.length() - action2->sync_point.length()))
786  diff= memcmp(action1->sync_point.ptr(), action2->sync_point.ptr(),
787  action1->sync_point.length());
788 
789  return diff;
790 }
791 
792 
809 static st_debug_sync_action *debug_sync_find(st_debug_sync_action *actionarr,
810  int quantity,
811  const char *dsp_name,
812  uint name_len)
813 {
814  st_debug_sync_action *action;
815  int low ;
816  int high ;
817  int mid ;
818  int diff ;
819  DBUG_ASSERT(actionarr);
820  DBUG_ASSERT(dsp_name);
821  DBUG_ASSERT(name_len);
822 
823  low= 0;
824  high= quantity;
825 
826  while (low < high)
827  {
828  mid= (low + high) / 2;
829  action= actionarr + mid;
830  if (!(diff= name_len - action->sync_point.length()) &&
831  !(diff= memcmp(dsp_name, action->sync_point.ptr(), name_len)))
832  return action;
833  if (diff > 0)
834  low= mid + 1;
835  else
836  high= mid - 1;
837  }
838 
839  if (low < quantity)
840  {
841  action= actionarr + low;
842  if ((name_len == action->sync_point.length()) &&
843  !memcmp(dsp_name, action->sync_point.ptr(), name_len))
844  return action;
845  }
846 
847  return NULL;
848 }
849 
850 
861 static void debug_sync_reset(THD *thd)
862 {
863  st_debug_sync_control *ds_control= thd->debug_sync_control;
864  DBUG_ENTER("debug_sync_reset");
865  DBUG_ASSERT(thd);
866  DBUG_ASSERT(ds_control);
867 
868  /* Remove all actions of this thread. */
869  ds_control->ds_active= 0;
870 
871  /* Clear the global signal. */
872  mysql_mutex_lock(&debug_sync_global.ds_mutex);
873  debug_sync_global.ds_signal.length(0);
874  mysql_mutex_unlock(&debug_sync_global.ds_mutex);
875 
876  DBUG_VOID_RETURN;
877 }
878 
879 
893 static void debug_sync_remove_action(st_debug_sync_control *ds_control,
894  st_debug_sync_action *action)
895 {
896  uint dsp_idx= action - ds_control->ds_action;
897  DBUG_ENTER("debug_sync_remove_action");
898  DBUG_ASSERT(ds_control);
899  DBUG_ASSERT(ds_control == current_thd->debug_sync_control);
900  DBUG_ASSERT(action);
901  DBUG_ASSERT(dsp_idx < ds_control->ds_active);
902 
903  /* Decrement the number of currently active actions. */
904  ds_control->ds_active--;
905 
906  /*
907  If this was not the last active action in the array, we need to
908  shift remaining active actions down to keep the array gap-free.
909  Otherwise binary search might fail or take longer than necessary at
910  least. Also new actions are always put to the end of the array.
911  */
912  if (ds_control->ds_active > dsp_idx)
913  {
914  /*
915  Do not make save_action an object of class st_debug_sync_action.
916  Its destructor would tamper with the String pointers.
917  */
918  uchar save_action[sizeof(st_debug_sync_action)];
919 
920  /*
921  Copy the to-be-removed action object to temporary storage before
922  the shift copies the string pointers over. Do not use assignment
923  because it would use assignment operator methods for the Strings.
924  This would copy the strings. The shift below overwrite the string
925  pointers without freeing them first. By using memmove() we save
926  the pointers, which are overwritten by the shift.
927  */
928  memmove(save_action, action, sizeof(st_debug_sync_action));
929 
930  /* Move actions down. */
931  memmove(ds_control->ds_action + dsp_idx,
932  ds_control->ds_action + dsp_idx + 1,
933  (ds_control->ds_active - dsp_idx) *
934  sizeof(st_debug_sync_action));
935 
936  /*
937  Copy back the saved action object to the now free array slot. This
938  replaces the double references of String pointers that have been
939  produced by the shift. Again do not use an assignment operator to
940  avoid string allocation/copy.
941  */
942  memmove(ds_control->ds_action + ds_control->ds_active, save_action,
943  sizeof(st_debug_sync_action));
944  }
945 
946  DBUG_VOID_RETURN;
947 }
948 
949 
965 static st_debug_sync_action *debug_sync_get_action(THD *thd,
966  const char *dsp_name,
967  uint name_len)
968 {
969  st_debug_sync_control *ds_control= thd->debug_sync_control;
970  st_debug_sync_action *action;
971  DBUG_ENTER("debug_sync_get_action");
972  DBUG_ASSERT(thd);
973  DBUG_ASSERT(dsp_name);
974  DBUG_ASSERT(name_len);
975  DBUG_ASSERT(ds_control);
976  DBUG_PRINT("debug_sync", ("sync_point: '%.*s'", (int) name_len, dsp_name));
977  DBUG_PRINT("debug_sync", ("active: %u allocated: %u",
978  ds_control->ds_active, ds_control->ds_allocated));
979 
980  /* There cannot be more active actions than allocated. */
981  DBUG_ASSERT(ds_control->ds_active <= ds_control->ds_allocated);
982  /* If there are active actions, the action array must be present. */
983  DBUG_ASSERT(!ds_control->ds_active || ds_control->ds_action);
984 
985  /* Try to reuse existing action if there is one for this sync point. */
986  if (ds_control->ds_active &&
987  (action= debug_sync_find(ds_control->ds_action, ds_control->ds_active,
988  dsp_name, name_len)))
989  {
990  /* Reuse an already active sync point action. */
991  DBUG_ASSERT((uint)(action - ds_control->ds_action) < ds_control->ds_active);
992  DBUG_PRINT("debug_sync", ("reuse action idx: %ld",
993  (long) (action - ds_control->ds_action)));
994  }
995  else
996  {
997  /* Create a new action. */
998  int dsp_idx= ds_control->ds_active++;
999  set_if_bigger(ds_control->dsp_max_active, ds_control->ds_active);
1000  if (ds_control->ds_active > ds_control->ds_allocated)
1001  {
1002  uint new_alloc= ds_control->ds_active + 3;
1003  void *new_action= my_realloc(ds_control->ds_action,
1004  new_alloc * sizeof(st_debug_sync_action),
1005  MYF(MY_WME | MY_ALLOW_ZERO_PTR));
1006  if (!new_action)
1007  {
1008  /* Error is reported by my_malloc(). */
1009  goto err; /* purecov: tested */
1010  }
1011  ds_control->ds_action= (st_debug_sync_action*) new_action;
1012  ds_control->ds_allocated= new_alloc;
1013  /* Clear memory as we do not run string constructors here. */
1014  memset((ds_control->ds_action + dsp_idx), 0,
1015  (new_alloc - dsp_idx) * sizeof(st_debug_sync_action));
1016  }
1017  DBUG_PRINT("debug_sync", ("added action idx: %u", dsp_idx));
1018  action= ds_control->ds_action + dsp_idx;
1019  if (action->sync_point.copy(dsp_name, name_len, system_charset_info))
1020  {
1021  /* Error is reported by my_malloc(). */
1022  goto err; /* purecov: tested */
1023  }
1024  action->need_sort= TRUE;
1025  }
1026  DBUG_ASSERT(action >= ds_control->ds_action);
1027  DBUG_ASSERT(action < ds_control->ds_action + ds_control->ds_active);
1028  DBUG_PRINT("debug_sync", ("action: 0x%lx array: 0x%lx count: %u",
1029  (long) action, (long) ds_control->ds_action,
1030  ds_control->ds_active));
1031 
1032  DBUG_RETURN(action);
1033 
1034  /* purecov: begin tested */
1035  err:
1036  DBUG_RETURN(NULL);
1037  /* purecov: end */
1038 }
1039 
1040 
1075 static bool debug_sync_set_action(THD *thd, st_debug_sync_action *action)
1076 {
1077  st_debug_sync_control *ds_control= thd->debug_sync_control;
1078  bool is_dsp_now= FALSE;
1079  DBUG_ENTER("debug_sync_set_action");
1080  DBUG_ASSERT(thd);
1081  DBUG_ASSERT(action);
1082  DBUG_ASSERT(ds_control);
1083 
1084  action->activation_count= max(action->hit_limit, action->execute);
1085  if (!action->activation_count)
1086  {
1087  debug_sync_remove_action(ds_control, action);
1088  DBUG_PRINT("debug_sync", ("action cleared"));
1089  }
1090  else
1091  {
1092  const char *dsp_name= action->sync_point.c_ptr();
1093  DBUG_EXECUTE("debug_sync", {
1094  /* Functions as DBUG_PRINT args can change keyword and line nr. */
1095  const char *sig_emit= action->signal.c_ptr();
1096  const char *sig_wait= action->wait_for.c_ptr();
1097  DBUG_PRINT("debug_sync",
1098  ("sync_point: '%s' activation_count: %lu hit_limit: %lu "
1099  "execute: %lu timeout: %lu signal: '%s' wait_for: '%s'",
1100  dsp_name, action->activation_count,
1101  action->hit_limit, action->execute, action->timeout,
1102  sig_emit, sig_wait));});
1103 
1104  /* Check this before sorting the array. action may move. */
1105  is_dsp_now= !my_strcasecmp(system_charset_info, dsp_name, "now");
1106 
1107  if (action->need_sort)
1108  {
1109  action->need_sort= FALSE;
1110  /* Sort actions by (name_len, name). */
1111  my_qsort(ds_control->ds_action, ds_control->ds_active,
1112  sizeof(st_debug_sync_action), debug_sync_qsort_cmp);
1113  }
1114  }
1115  DBUG_EXECUTE("debug_sync_list", debug_sync_print_actions(thd););
1116 
1117  /* Execute the special sync point 'now' if activated above. */
1118  if (is_dsp_now)
1119  {
1120  DEBUG_SYNC(thd, "now");
1121  /*
1122  If HIT_LIMIT for sync point "now" was 1, the execution of the sync
1123  point decremented it to 0. In this case the following happened:
1124 
1125  - an error message was reported with my_error() and
1126  - the statement was killed with thd->killed= THD::KILL_QUERY.
1127 
1128  If a statement reports an error, it must not call send_ok().
1129  The calling functions will not call send_ok(), if we return TRUE
1130  from this function.
1131 
1132  thd->killed is also set if the wait is interrupted from a
1133  KILL or KILL QUERY statement. In this case, no error is reported
1134  and shall not be reported as a result of SET DEBUG_SYNC.
1135  Hence, we check for the first condition above.
1136  */
1137  if (thd->is_error())
1138  DBUG_RETURN(TRUE);
1139  }
1140 
1141  DBUG_RETURN(FALSE);
1142 }
1143 
1144 
1194 static char *debug_sync_token(char **token_p, uint *token_length_p, char *ptr)
1195 {
1196  DBUG_ASSERT(token_p);
1197  DBUG_ASSERT(token_length_p);
1198  DBUG_ASSERT(ptr);
1199 
1200  /* Skip leading space */
1201  while (my_isspace(system_charset_info, *ptr))
1202  ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
1203 
1204  if (!*ptr)
1205  {
1206  ptr= NULL;
1207  goto end;
1208  }
1209 
1210  /* Get token start. */
1211  *token_p= ptr;
1212 
1213  /* Find token end. */
1214  while (*ptr && !my_isspace(system_charset_info, *ptr))
1215  ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
1216 
1217  /* Get token length. */
1218  *token_length_p= ptr - *token_p;
1219 
1220  /* If necessary, terminate token. */
1221  if (*ptr)
1222  {
1223  /* Get terminator character length. */
1224  uint mbspacelen= my_mbcharlen(system_charset_info, (uchar) *ptr);
1225 
1226  /* Terminate token. */
1227  *ptr= '\0';
1228 
1229  /* Skip the terminator. */
1230  ptr+= mbspacelen;
1231 
1232  /* Skip trailing space */
1233  while (my_isspace(system_charset_info, *ptr))
1234  ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
1235  }
1236 
1237  end:
1238  return ptr;
1239 }
1240 
1241 
1264 static char *debug_sync_number(ulong *number_p, char *actstrptr)
1265 {
1266  char *ptr;
1267  char *ept;
1268  char *token;
1269  uint token_length;
1270  DBUG_ASSERT(number_p);
1271  DBUG_ASSERT(actstrptr);
1272 
1273  /* Get token from string. */
1274  if (!(ptr= debug_sync_token(&token, &token_length, actstrptr)))
1275  goto end;
1276 
1277  *number_p= strtoul(token, &ept, 10);
1278  if (*ept)
1279  ptr= NULL;
1280 
1281  end:
1282  return ptr;
1283 }
1284 
1285 
1318 static bool debug_sync_eval_action(THD *thd, char *action_str)
1319 {
1320  st_debug_sync_action *action= NULL;
1321  const char *errmsg;
1322  char *ptr;
1323  char *token;
1324  uint token_length= 0;
1325  DBUG_ENTER("debug_sync_eval_action");
1326  DBUG_ASSERT(thd);
1327  DBUG_ASSERT(action_str);
1328 
1329  /*
1330  Get debug sync point name. Or a special command.
1331  */
1332  if (!(ptr= debug_sync_token(&token, &token_length, action_str)))
1333  {
1334  errmsg= "Missing synchronization point name";
1335  goto err;
1336  }
1337 
1338  /*
1339  If there is a second token, the first one is the sync point name.
1340  */
1341  if (*ptr)
1342  {
1343  /* Get an action object to collect the requested action parameters. */
1344  action= debug_sync_get_action(thd, token, token_length);
1345  if (!action)
1346  {
1347  /* Error message is sent. */
1348  DBUG_RETURN(TRUE); /* purecov: tested */
1349  }
1350  }
1351 
1352  /*
1353  Get kind of action to be taken at sync point.
1354  */
1355  if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1356  {
1357  /* No action present. Try special commands. Token unchanged. */
1358 
1359  /*
1360  Try RESET.
1361  */
1362  if (!my_strcasecmp(system_charset_info, token, "RESET"))
1363  {
1364  /* It is RESET. Reset all actions and global signal. */
1365  debug_sync_reset(thd);
1366  goto end;
1367  }
1368 
1369  /* Token unchanged. It still contains sync point name. */
1370  errmsg= "Missing action after synchronization point name '%.*s'";
1371  goto err;
1372  }
1373 
1374  /*
1375  Check for pseudo actions first. Start with actions that work on
1376  an existing action.
1377  */
1378  DBUG_ASSERT(action);
1379 
1380  /*
1381  Try TEST.
1382  */
1383  if (!my_strcasecmp(system_charset_info, token, "TEST"))
1384  {
1385  /* It is TEST. Nothing must follow it. */
1386  if (*ptr)
1387  {
1388  errmsg= "Nothing must follow action TEST";
1389  goto err;
1390  }
1391 
1392  /* Execute sync point. */
1393  debug_sync(thd, action->sync_point.ptr(), action->sync_point.length());
1394  /* Fix statistics. This was not a real hit of the sync point. */
1395  thd->debug_sync_control->dsp_hits--;
1396  goto end;
1397  }
1398 
1399  /*
1400  Now check for actions that define a new action.
1401  Initialize action. Do not use memset(). Strings may have malloced.
1402  */
1403  action->activation_count= 0;
1404  action->hit_limit= 0;
1405  action->execute= 0;
1406  action->timeout= 0;
1407  action->signal.length(0);
1408  action->wait_for.length(0);
1409 
1410  /*
1411  Try CLEAR.
1412  */
1413  if (!my_strcasecmp(system_charset_info, token, "CLEAR"))
1414  {
1415  /* It is CLEAR. Nothing must follow it. */
1416  if (*ptr)
1417  {
1418  errmsg= "Nothing must follow action CLEAR";
1419  goto err;
1420  }
1421 
1422  /* Set (clear/remove) action. */
1423  goto set_action;
1424  }
1425 
1426  /*
1427  Now check for real sync point actions.
1428  */
1429 
1430  /*
1431  Try SIGNAL.
1432  */
1433  if (!my_strcasecmp(system_charset_info, token, "SIGNAL"))
1434  {
1435  /* It is SIGNAL. Signal name must follow. */
1436  if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1437  {
1438  errmsg= "Missing signal name after action SIGNAL";
1439  goto err;
1440  }
1441  if (action->signal.copy(token, token_length, system_charset_info))
1442  {
1443  /* Error is reported by my_malloc(). */
1444  /* purecov: begin tested */
1445  errmsg= NULL;
1446  goto err;
1447  /* purecov: end */
1448  }
1449 
1450  /* Set default for EXECUTE option. */
1451  action->execute= 1;
1452 
1453  /* Get next token. If none follows, set action. */
1454  if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1455  goto set_action;
1456  }
1457 
1458  /*
1459  Try WAIT_FOR.
1460  */
1461  if (!my_strcasecmp(system_charset_info, token, "WAIT_FOR"))
1462  {
1463  /* It is WAIT_FOR. Wait_for signal name must follow. */
1464  if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1465  {
1466  errmsg= "Missing signal name after action WAIT_FOR";
1467  goto err;
1468  }
1469  if (action->wait_for.copy(token, token_length, system_charset_info))
1470  {
1471  /* Error is reported by my_malloc(). */
1472  /* purecov: begin tested */
1473  errmsg= NULL;
1474  goto err;
1475  /* purecov: end */
1476  }
1477 
1478  /* Set default for EXECUTE and TIMEOUT options. */
1479  action->execute= 1;
1480  action->timeout= opt_debug_sync_timeout;
1481 
1482  /* Get next token. If none follows, set action. */
1483  if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1484  goto set_action;
1485 
1486  /*
1487  Try TIMEOUT.
1488  */
1489  if (!my_strcasecmp(system_charset_info, token, "TIMEOUT"))
1490  {
1491  /* It is TIMEOUT. Number must follow. */
1492  if (!(ptr= debug_sync_number(&action->timeout, ptr)))
1493  {
1494  errmsg= "Missing valid number after TIMEOUT";
1495  goto err;
1496  }
1497 
1498  /* Get next token. If none follows, set action. */
1499  if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1500  goto set_action;
1501  }
1502  }
1503 
1504  /*
1505  Try EXECUTE.
1506  */
1507  if (!my_strcasecmp(system_charset_info, token, "EXECUTE"))
1508  {
1509  /*
1510  EXECUTE requires either SIGNAL and/or WAIT_FOR to be present.
1511  In this case action->execute has been preset to 1.
1512  */
1513  if (!action->execute)
1514  {
1515  errmsg= "Missing action before EXECUTE";
1516  goto err;
1517  }
1518 
1519  /* Number must follow. */
1520  if (!(ptr= debug_sync_number(&action->execute, ptr)))
1521  {
1522  errmsg= "Missing valid number after EXECUTE";
1523  goto err;
1524  }
1525 
1526  /* Get next token. If none follows, set action. */
1527  if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1528  goto set_action;
1529  }
1530 
1531  /*
1532  Try HIT_LIMIT.
1533  */
1534  if (!my_strcasecmp(system_charset_info, token, "HIT_LIMIT"))
1535  {
1536  /* Number must follow. */
1537  if (!(ptr= debug_sync_number(&action->hit_limit, ptr)))
1538  {
1539  errmsg= "Missing valid number after HIT_LIMIT";
1540  goto err;
1541  }
1542 
1543  /* Get next token. If none follows, set action. */
1544  if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
1545  goto set_action;
1546  }
1547 
1548  errmsg= "Illegal or out of order stuff: '%.*s'";
1549 
1550  err:
1551  if (errmsg)
1552  {
1553  /*
1554  NOTE: errmsg must either have %.*s or none % at all.
1555  It can be NULL if an error message is already reported
1556  (e.g. by my_malloc()).
1557  */
1558  set_if_smaller(token_length, 64); /* Limit error message length. */
1559  my_printf_error(ER_PARSE_ERROR, errmsg, MYF(0), token_length, token);
1560  }
1561  if (action)
1562  debug_sync_remove_action(thd->debug_sync_control, action);
1563  DBUG_RETURN(TRUE);
1564 
1565  set_action:
1566  DBUG_RETURN(debug_sync_set_action(thd, action));
1567 
1568  end:
1569  DBUG_RETURN(FALSE);
1570 }
1571 
1592 bool debug_sync_update(THD *thd, char *val_str)
1593 {
1594  DBUG_ENTER("debug_sync_update");
1595  DBUG_PRINT("debug_sync", ("set action: '%s'", val_str));
1596 
1597  /*
1598  debug_sync_eval_action() places '\0' in the string, which itself
1599  must be '\0' terminated.
1600  */
1601  DBUG_RETURN(opt_debug_sync_timeout ?
1602  debug_sync_eval_action(thd, val_str) :
1603  FALSE);
1604 }
1605 
1606 
1623 uchar *debug_sync_value_ptr(THD *thd)
1624 {
1625  char *value;
1626  DBUG_ENTER("debug_sync_value_ptr");
1627 
1628  if (opt_debug_sync_timeout)
1629  {
1630  static char on[]= "ON - current signal: '";
1631 
1632  // Ensure exclusive access to debug_sync_global.ds_signal
1633  mysql_mutex_lock(&debug_sync_global.ds_mutex);
1634 
1635  size_t lgt= (sizeof(on) /* includes '\0' */ +
1636  debug_sync_global.ds_signal.length() + 1 /* for '\'' */);
1637  char *vend;
1638  char *vptr;
1639 
1640  if ((value= (char*) alloc_root(thd->mem_root, lgt)))
1641  {
1642  vend= value + lgt - 1; /* reserve space for '\0'. */
1643  vptr= debug_sync_bmove_len(value, vend, STRING_WITH_LEN(on));
1644  vptr= debug_sync_bmove_len(vptr, vend, debug_sync_global.ds_signal.ptr(),
1645  debug_sync_global.ds_signal.length());
1646  if (vptr < vend)
1647  *(vptr++)= '\'';
1648  *vptr= '\0'; /* We have one byte reserved for the worst case. */
1649  }
1650  mysql_mutex_unlock(&debug_sync_global.ds_mutex);
1651  }
1652  else
1653  {
1654  /* purecov: begin tested */
1655  value= const_cast<char*>("OFF");
1656  /* purecov: end */
1657  }
1658 
1659  DBUG_RETURN((uchar*) value);
1660 }
1661 
1662 
1673 static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
1674 {
1675 #ifndef DBUG_OFF
1676  const char *dsp_name= action->sync_point.c_ptr();
1677  const char *sig_emit= action->signal.c_ptr();
1678  const char *sig_wait= action->wait_for.c_ptr();
1679 #endif
1680  DBUG_ENTER("debug_sync_execute");
1681  DBUG_ASSERT(thd);
1682  DBUG_ASSERT(action);
1683  DBUG_PRINT("debug_sync",
1684  ("sync_point: '%s' activation_count: %lu hit_limit: %lu "
1685  "execute: %lu timeout: %lu signal: '%s' wait_for: '%s'",
1686  dsp_name, action->activation_count, action->hit_limit,
1687  action->execute, action->timeout, sig_emit, sig_wait));
1688 
1689  DBUG_ASSERT(action->activation_count);
1690  action->activation_count--;
1691 
1692  if (action->execute)
1693  {
1694  const char *UNINIT_VAR(old_proc_info);
1695 
1696  action->execute--;
1697 
1698  /*
1699  If we will be going to wait, set proc_info for the PROCESSLIST table.
1700  Do this before emitting the signal, so other threads can see it
1701  if they awake before we enter_cond() below.
1702  */
1703  if (action->wait_for.length())
1704  {
1705  st_debug_sync_control *ds_control= thd->debug_sync_control;
1706  strxnmov(ds_control->ds_proc_info, sizeof(ds_control->ds_proc_info)-1,
1707  "debug sync point: ", action->sync_point.c_ptr(), NullS);
1708  old_proc_info= thd->proc_info;
1709  thd_proc_info(thd, ds_control->ds_proc_info);
1710  }
1711 
1712  /*
1713  Take mutex to ensure that only one thread access
1714  debug_sync_global.ds_signal at a time. Need to take mutex for
1715  read access too, to create a memory barrier in order to avoid that
1716  threads just reads an old cached version of the signal.
1717  */
1718  mysql_mutex_lock(&debug_sync_global.ds_mutex);
1719 
1720  if (action->signal.length())
1721  {
1722  /* Copy the signal to the global variable. */
1723  if (debug_sync_global.ds_signal.copy(action->signal))
1724  {
1725  /*
1726  Error is reported by my_malloc().
1727  We must disable the facility. We have no way to return an error.
1728  */
1729  debug_sync_emergency_disable(); /* purecov: tested */
1730  }
1731  /* Wake threads waiting in a sync point. */
1732  mysql_cond_broadcast(&debug_sync_global.ds_cond);
1733  DBUG_PRINT("debug_sync_exec", ("signal '%s' at: '%s'",
1734  sig_emit, dsp_name));
1735  } /* end if (action->signal.length()) */
1736 
1737  if (action->wait_for.length())
1738  {
1739  mysql_mutex_t *old_mutex;
1740  mysql_cond_t *old_cond= 0;
1741  int error= 0;
1742  struct timespec abstime;
1743 
1744  /*
1745  We don't use enter_cond()/exit_cond(). They do not save old
1746  mutex and cond. This would prohibit the use of DEBUG_SYNC
1747  between other places of enter_cond() and exit_cond().
1748 
1749  We need to check for existence of thd->mysys_var to also make
1750  it possible to use DEBUG_SYNC framework in scheduler when this
1751  variable has been set to NULL.
1752  */
1753  if (thd->mysys_var)
1754  {
1755  old_mutex= thd->mysys_var->current_mutex;
1756  old_cond= thd->mysys_var->current_cond;
1757  thd->mysys_var->current_mutex= &debug_sync_global.ds_mutex;
1758  thd->mysys_var->current_cond= &debug_sync_global.ds_cond;
1759  }
1760  else
1761  old_mutex= NULL;
1762 
1763  set_timespec(abstime, action->timeout);
1764  DBUG_EXECUTE("debug_sync_exec", {
1765  /* Functions as DBUG_PRINT args can change keyword and line nr. */
1766  const char *sig_glob= debug_sync_global.ds_signal.c_ptr();
1767  DBUG_PRINT("debug_sync_exec",
1768  ("wait for '%s' at: '%s' curr: '%s'",
1769  sig_wait, dsp_name, sig_glob));});
1770 
1771  /*
1772  Wait until global signal string matches the wait_for string.
1773  Interrupt when thread or query is killed or facility disabled.
1774  The facility can become disabled when some thread cannot get
1775  the required dynamic memory allocated.
1776  */
1777  while (stringcmp(&debug_sync_global.ds_signal, &action->wait_for) &&
1778  !thd->killed && opt_debug_sync_timeout)
1779  {
1780  error= mysql_cond_timedwait(&debug_sync_global.ds_cond,
1781  &debug_sync_global.ds_mutex,
1782  &abstime);
1783  DBUG_EXECUTE("debug_sync", {
1784  /* Functions as DBUG_PRINT args can change keyword and line nr. */
1785  const char *sig_glob= debug_sync_global.ds_signal.c_ptr();
1786  DBUG_PRINT("debug_sync",
1787  ("awoke from %s global: %s error: %d",
1788  sig_wait, sig_glob, error));});
1789  if (error == ETIMEDOUT || error == ETIME)
1790  {
1791  // We should not make the statement fail, even if in strict mode.
1792  const bool save_abort_on_warning= thd->abort_on_warning;
1793  thd->abort_on_warning= false;
1794  push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
1795  ER_DEBUG_SYNC_TIMEOUT, ER(ER_DEBUG_SYNC_TIMEOUT));
1796  thd->abort_on_warning= save_abort_on_warning;
1797  DBUG_EXECUTE_IF("debug_sync_abort_on_timeout", DBUG_ABORT(););
1798  break;
1799  }
1800  error= 0;
1801  }
1802  DBUG_EXECUTE("debug_sync_exec",
1803  if (thd->killed)
1804  DBUG_PRINT("debug_sync_exec",
1805  ("killed %d from '%s' at: '%s'",
1806  thd->killed, sig_wait, dsp_name));
1807  else
1808  DBUG_PRINT("debug_sync_exec",
1809  ("%s from '%s' at: '%s'",
1810  error ? "timeout" : "resume",
1811  sig_wait, dsp_name)););
1812 
1813  /*
1814  We don't use enter_cond()/exit_cond(). They do not save old
1815  mutex and cond. This would prohibit the use of DEBUG_SYNC
1816  between other places of enter_cond() and exit_cond(). The
1817  protected mutex must always unlocked _before_ mysys_var->mutex
1818  is locked. (See comment in THD::exit_cond().)
1819  */
1820  mysql_mutex_unlock(&debug_sync_global.ds_mutex);
1821  if (old_mutex)
1822  {
1823  mysql_mutex_lock(&thd->mysys_var->mutex);
1824  thd->mysys_var->current_mutex= old_mutex;
1825  thd->mysys_var->current_cond= old_cond;
1826  thd_proc_info(thd, old_proc_info);
1827  mysql_mutex_unlock(&thd->mysys_var->mutex);
1828  }
1829  else
1830  thd_proc_info(thd, old_proc_info);
1831  }
1832  else
1833  {
1834  /* In case we don't wait, we just release the mutex. */
1835  mysql_mutex_unlock(&debug_sync_global.ds_mutex);
1836  } /* end if (action->wait_for.length()) */
1837 
1838  } /* end if (action->execute) */
1839 
1840  /* hit_limit is zero for infinite. Don't decrement unconditionally. */
1841  if (action->hit_limit)
1842  {
1843  if (!--action->hit_limit)
1844  {
1845  thd->killed= THD::KILL_QUERY;
1846  my_error(ER_DEBUG_SYNC_HIT_LIMIT, MYF(0));
1847  }
1848  DBUG_PRINT("debug_sync_exec", ("hit_limit: %lu at: '%s'",
1849  action->hit_limit, dsp_name));
1850  }
1851 
1852  DBUG_VOID_RETURN;
1853 }
1854 
1855 
1864 void debug_sync(THD *thd, const char *sync_point_name, size_t name_len)
1865 {
1866  if(!thd)
1867  {
1868  return;
1869  }
1870 
1871  st_debug_sync_control *ds_control= thd->debug_sync_control;
1872  st_debug_sync_action *action;
1873  DBUG_ENTER("debug_sync");
1874  DBUG_ASSERT(thd);
1875  DBUG_ASSERT(sync_point_name);
1876  DBUG_ASSERT(name_len);
1877  DBUG_ASSERT(ds_control);
1878  DBUG_PRINT("debug_sync_point", ("hit: '%s'", sync_point_name));
1879 
1880  /* Statistics. */
1881  ds_control->dsp_hits++;
1882 
1883  if (ds_control->ds_active &&
1884  (action= debug_sync_find(ds_control->ds_action, ds_control->ds_active,
1885  sync_point_name, name_len)) &&
1886  action->activation_count)
1887  {
1888  /* Sync point is active (action exists). */
1889  debug_sync_execute(thd, action);
1890 
1891  /* Statistics. */
1892  ds_control->dsp_executed++;
1893 
1894  /* If action became inactive, remove it to shrink the search array. */
1895  if (!action->activation_count)
1896  debug_sync_remove_action(ds_control, action);
1897  }
1898 
1899  DBUG_VOID_RETURN;
1900 }
1901 
1926 bool debug_sync_set_action(THD *thd, const char *action_str, size_t len)
1927 {
1928  bool rc;
1929  char *value;
1930  DBUG_ENTER("debug_sync_set_action");
1931  DBUG_ASSERT(thd);
1932  DBUG_ASSERT(action_str);
1933 
1934  value= strmake_root(thd->mem_root, action_str, len);
1935  rc= debug_sync_eval_action(thd, value);
1936  DBUG_RETURN(rc);
1937 }
1938 
1939 
1940 #endif /* defined(ENABLED_DEBUG_SYNC) */