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