service_debug_sync.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. #ifndef MYSQL_SERVICE_DEBUG_SYNC_INCLUDED
  2. /* Copyright (c) 2009, 2010, Oracle and/or its affiliates.
  3. Copyright (c) 2012, Monty Program Ab
  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. 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. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
  14. /**
  15. @file
  16. == Debug Sync Facility ==
  17. The Debug Sync Facility allows placement of synchronization points in
  18. the server code by using the DEBUG_SYNC macro:
  19. open_tables(...)
  20. DEBUG_SYNC(thd, "after_open_tables");
  21. lock_tables(...)
  22. When activated, a sync point can
  23. - Emit a signal and/or
  24. - Wait for a signal
  25. Nomenclature:
  26. - signal: A value of a global variable that persists
  27. until overwritten by a new signal. The global
  28. variable can also be seen as a "signal post"
  29. or "flag mast". Then the signal is what is
  30. attached to the "signal post" or "flag mast".
  31. - emit a signal: Assign the value (the signal) to the global
  32. variable ("set a flag") and broadcast a
  33. global condition to wake those waiting for
  34. a signal.
  35. - wait for a signal: Loop over waiting for the global condition until
  36. the global value matches the wait-for signal.
  37. By default, all sync points are inactive. They do nothing (except to
  38. burn a couple of CPU cycles for checking if they are active).
  39. A sync point becomes active when an action is requested for it.
  40. To do so, put a line like this in the test case file:
  41. SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
  42. This activates the sync point 'after_open_tables'. It requests it to
  43. emit the signal 'opened' and wait for another thread to emit the signal
  44. 'flushed' when the thread's execution runs through the sync point.
  45. For every sync point there can be one action per thread only. Every
  46. thread can request multiple actions, but only one per sync point. In
  47. other words, a thread can activate multiple sync points.
  48. Here is an example how to activate and use the sync points:
  49. --connection conn1
  50. SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
  51. send INSERT INTO t1 VALUES(1);
  52. --connection conn2
  53. SET DEBUG_SYNC= 'now WAIT_FOR opened';
  54. SET DEBUG_SYNC= 'after_abort_locks SIGNAL flushed';
  55. FLUSH TABLE t1;
  56. When conn1 runs through the INSERT statement, it hits the sync point
  57. 'after_open_tables'. It notices that it is active and executes its
  58. action. It emits the signal 'opened' and waits for another thread to
  59. emit the signal 'flushed'.
  60. conn2 waits immediately at the special sync point 'now' for another
  61. thread to emit the 'opened' signal.
  62. A signal remains in effect until it is overwritten. If conn1 signals
  63. 'opened' before conn2 reaches 'now', conn2 will still find the 'opened'
  64. signal. It does not wait in this case.
  65. When conn2 reaches 'after_abort_locks', it signals 'flushed', which lets
  66. conn1 awake.
  67. Normally the activation of a sync point is cleared when it has been
  68. executed. Sometimes it is necessary to keep the sync point active for
  69. another execution. You can add an execute count to the action:
  70. SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 3';
  71. This sets the signal point's activation counter to 3. Each execution
  72. decrements the counter. After the third execution the sync point
  73. becomes inactive.
  74. One of the primary goals of this facility is to eliminate sleeps from
  75. the test suite. In most cases it should be possible to rewrite test
  76. cases so that they do not need to sleep. (But this facility cannot
  77. synchronize multiple processes.) However, to support test development,
  78. and as a last resort, sync point waiting times out. There is a default
  79. timeout, but it can be overridden:
  80. SET DEBUG_SYNC= 'name WAIT_FOR sig TIMEOUT 10 EXECUTE 2';
  81. TIMEOUT 0 is special: If the signal is not present, the wait times out
  82. immediately.
  83. When a wait timed out (even on TIMEOUT 0), a warning is generated so
  84. that it shows up in the test result.
  85. You can throw an error message and kill the query when a synchronization
  86. point is hit a certain number of times:
  87. SET DEBUG_SYNC= 'name HIT_LIMIT 3';
  88. Or combine it with signal and/or wait:
  89. SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 2 HIT_LIMIT 3';
  90. Here the first two hits emit the signal, the third hit returns the error
  91. message and kills the query.
  92. For cases where you are not sure that an action is taken and thus
  93. cleared in any case, you can force to clear (deactivate) a sync point:
  94. SET DEBUG_SYNC= 'name CLEAR';
  95. If you want to clear all actions and clear the global signal, use:
  96. SET DEBUG_SYNC= 'RESET';
  97. This is the only way to reset the global signal to an empty string.
  98. For testing of the facility itself you can execute a sync point just
  99. as if it had been hit:
  100. SET DEBUG_SYNC= 'name TEST';
  101. === Formal Syntax ===
  102. The string to "assign" to the DEBUG_SYNC variable can contain:
  103. {RESET |
  104. <sync point name> TEST |
  105. <sync point name> CLEAR |
  106. <sync point name> {{SIGNAL <signal name> |
  107. WAIT_FOR <signal name> [TIMEOUT <seconds>]}
  108. [EXECUTE <count>] &| HIT_LIMIT <count>}
  109. Here '&|' means 'and/or'. This means that one of the sections
  110. separated by '&|' must be present or both of them.
  111. === Activation/Deactivation ===
  112. The facility is an optional part of the MySQL server.
  113. It is enabled in a debug server by default.
  114. ./configure --enable-debug-sync
  115. The Debug Sync Facility, when compiled in, is disabled by default. It
  116. can be enabled by a mysqld command line option:
  117. --debug-sync-timeout[=default_wait_timeout_value_in_seconds]
  118. 'default_wait_timeout_value_in_seconds' is the default timeout for the
  119. WAIT_FOR action. If set to zero, the facility stays disabled.
  120. The facility is enabled by default in the test suite, but can be
  121. disabled with:
  122. mysql-test-run.pl ... --debug-sync-timeout=0 ...
  123. Likewise the default wait timeout can be set:
  124. mysql-test-run.pl ... --debug-sync-timeout=10 ...
  125. The command line option influences the readable value of the system
  126. variable 'debug_sync'.
  127. * If the facility is not compiled in, the system variable does not exist.
  128. * If --debug-sync-timeout=0 the value of the variable reads as "OFF".
  129. * Otherwise the value reads as "ON - current signal: " followed by the
  130. current signal string, which can be empty.
  131. The readable variable value is the same, regardless if read as global
  132. or session value.
  133. Setting the 'debug-sync' system variable requires 'SUPER' privilege.
  134. You can never read back the string that you assigned to the variable,
  135. unless you assign the value that the variable does already have. But
  136. that would give a parse error. A syntactically correct string is
  137. parsed into a debug sync action and stored apart from the variable value.
  138. === Implementation ===
  139. Pseudo code for a sync point:
  140. #define DEBUG_SYNC(thd, sync_point_name)
  141. if (unlikely(opt_debug_sync_timeout))
  142. debug_sync(thd, STRING_WITH_LEN(sync_point_name))
  143. The sync point performs a binary search in a sorted array of actions
  144. for this thread.
  145. The SET DEBUG_SYNC statement adds a requested action to the array or
  146. overwrites an existing action for the same sync point. When it adds a
  147. new action, the array is sorted again.
  148. === A typical synchronization pattern ===
  149. There are quite a few places in MySQL, where we use a synchronization
  150. pattern like this:
  151. mysql_mutex_lock(&mutex);
  152. thd->enter_cond(&condition_variable, &mutex, new_message);
  153. #if defined(ENABLE_DEBUG_SYNC)
  154. if (!thd->killed && !end_of_wait_condition)
  155. DEBUG_SYNC(thd, "sync_point_name");
  156. #endif
  157. while (!thd->killed && !end_of_wait_condition)
  158. mysql_cond_wait(&condition_variable, &mutex);
  159. thd->exit_cond(old_message);
  160. Here some explanations:
  161. thd->enter_cond() is used to register the condition variable and the
  162. mutex in thd->mysys_var. This is done to allow the thread to be
  163. interrupted (killed) from its sleep. Another thread can find the
  164. condition variable to signal and mutex to use for synchronization in
  165. this thread's THD::mysys_var.
  166. thd->enter_cond() requires the mutex to be acquired in advance.
  167. thd->exit_cond() unregisters the condition variable and mutex and
  168. releases the mutex.
  169. If you want to have a Debug Sync point with the wait, please place it
  170. behind enter_cond(). Only then you can safely decide, if the wait will
  171. be taken. Also you will have THD::proc_info correct when the sync
  172. point emits a signal. DEBUG_SYNC sets its own proc_info, but restores
  173. the previous one before releasing its internal mutex. As soon as
  174. another thread sees the signal, it does also see the proc_info from
  175. before entering the sync point. In this case it will be "new_message",
  176. which is associated with the wait that is to be synchronized.
  177. In the example above, the wait condition is repeated before the sync
  178. point. This is done to skip the sync point, if no wait takes place.
  179. The sync point is before the loop (not inside the loop) to have it hit
  180. once only. It is possible that the condition variable is signaled
  181. multiple times without the wait condition to be true.
  182. A bit off-topic: At some places, the loop is taken around the whole
  183. synchronization pattern:
  184. while (!thd->killed && !end_of_wait_condition)
  185. {
  186. mysql_mutex_lock(&mutex);
  187. thd->enter_cond(&condition_variable, &mutex, new_message);
  188. if (!thd->killed [&& !end_of_wait_condition])
  189. {
  190. [DEBUG_SYNC(thd, "sync_point_name");]
  191. mysql_cond_wait(&condition_variable, &mutex);
  192. }
  193. thd->exit_cond(old_message);
  194. }
  195. Note that it is important to repeat the test for thd->killed after
  196. enter_cond(). Otherwise the killing thread may kill this thread after
  197. it tested thd->killed in the loop condition and before it registered
  198. the condition variable and mutex in enter_cond(). In this case, the
  199. killing thread does not know that this thread is going to wait on a
  200. condition variable. It would just set THD::killed. But if we would not
  201. test it again, we would go asleep though we are killed. If the killing
  202. thread would kill us when we are after the second test, but still
  203. before sleeping, we hold the mutex, which is registered in mysys_var.
  204. The killing thread would try to acquire the mutex before signaling
  205. the condition variable. Since the mutex is only released implicitly in
  206. mysql_cond_wait(), the signaling happens at the right place. We
  207. have a safe synchronization.
  208. === Co-work with the DBUG facility ===
  209. When running the MySQL test suite with the --debug-dbug command line
  210. option, the Debug Sync Facility writes trace messages to the DBUG
  211. trace. The following shell commands proved very useful in extracting
  212. relevant information:
  213. egrep 'query:|debug_sync_exec:' mysql-test/var/log/mysqld.1.trace
  214. It shows all executed SQL statements and all actions executed by
  215. synchronization points.
  216. Sometimes it is also useful to see, which synchronization points have
  217. been run through (hit) with or without executing actions. Then add
  218. "|debug_sync_point:" to the egrep pattern.
  219. === Further reading ===
  220. For a discussion of other methods to synchronize threads see
  221. http://forge.mysql.com/wiki/MySQL_Internals_Test_Synchronization
  222. For complete syntax tests, functional tests, and examples see the test
  223. case debug_sync.test.
  224. See also http://forge.mysql.com/worklog/task.php?id=4259
  225. */
  226. #ifndef MYSQL_ABI_CHECK
  227. #include <stdlib.h>
  228. #endif
  229. #ifdef __cplusplus
  230. extern "C" {
  231. #endif
  232. #ifdef MYSQL_DYNAMIC_PLUGIN
  233. extern void (*debug_sync_service)(MYSQL_THD, const char *, size_t);
  234. #else
  235. #define debug_sync_service debug_sync_C_callback_ptr
  236. extern void (*debug_sync_C_callback_ptr)(MYSQL_THD, const char *, size_t);
  237. #endif
  238. #ifdef ENABLED_DEBUG_SYNC
  239. #define DEBUG_SYNC(thd, name) \
  240. do { \
  241. if (debug_sync_service) \
  242. debug_sync_service(thd, STRING_WITH_LEN(name)); \
  243. } while(0)
  244. #define DEBUG_SYNC_C_IF_THD(thd, name) \
  245. do { \
  246. if (debug_sync_service && thd) \
  247. debug_sync_service((MYSQL_THD) thd, STRING_WITH_LEN(name)); \
  248. } while(0)
  249. #else
  250. #define DEBUG_SYNC(thd,name) do { } while(0)
  251. #define DEBUG_SYNC_C_IF_THD(thd, _sync_point_name_) do { } while(0)
  252. #endif /* defined(ENABLED_DEBUG_SYNC) */
  253. /* compatibility macro */
  254. #define DEBUG_SYNC_C(name) DEBUG_SYNC(NULL, name)
  255. #ifdef __cplusplus
  256. }
  257. #endif
  258. #define MYSQL_SERVICE_DEBUG_SYNC_INCLUDED
  259. #endif