~ubuntu-branches/ubuntu/precise/mysql-5.1/precise

« back to all changes in this revision

Viewing changes to sql/debug_sync.cc

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2010-03-17 14:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20100317145602-x7e30l1b2sb5s6w6
Tags: upstream-5.1.45
ImportĀ upstreamĀ versionĀ 5.1.45

Show diffs side-by-side

added added

removed removed

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