run-57-die-during-transaction.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. #include "../private.h"
  2. #include <unistd.h>
  3. #include "lock-tracking.h"
  4. #include "tap-interface.h"
  5. #include <stdlib.h>
  6. #include <assert.h>
  7. static ssize_t pwrite_check(int fd, const void *buf, size_t count, off_t offset);
  8. static ssize_t write_check(int fd, const void *buf, size_t count);
  9. static int ftruncate_check(int fd, off_t length);
  10. #define pwrite pwrite_check
  11. #define write write_check
  12. #define fcntl fcntl_with_lockcheck
  13. #define ftruncate ftruncate_check
  14. /* There's a malloc inside transaction_setup_recovery, and valgrind complains
  15. * when we longjmp and leak it. */
  16. #define MAX_ALLOCATIONS 10
  17. static void *allocated[MAX_ALLOCATIONS];
  18. static unsigned max_alloc = 0;
  19. static void *malloc_noleak(size_t len)
  20. {
  21. unsigned int i;
  22. for (i = 0; i < MAX_ALLOCATIONS; i++)
  23. if (!allocated[i]) {
  24. allocated[i] = malloc(len);
  25. if (i > max_alloc) {
  26. max_alloc = i;
  27. diag("max_alloc: %i", max_alloc);
  28. }
  29. return allocated[i];
  30. }
  31. diag("Too many allocations!");
  32. abort();
  33. }
  34. static void *realloc_noleak(void *p, size_t size)
  35. {
  36. unsigned int i;
  37. for (i = 0; i < MAX_ALLOCATIONS; i++) {
  38. if (allocated[i] == p) {
  39. if (i > max_alloc) {
  40. max_alloc = i;
  41. diag("max_alloc: %i", max_alloc);
  42. }
  43. return allocated[i] = realloc(p, size);
  44. }
  45. }
  46. diag("Untracked realloc!");
  47. abort();
  48. }
  49. static void free_noleak(void *p)
  50. {
  51. unsigned int i;
  52. /* We don't catch asprintf, so don't complain if we miss one. */
  53. for (i = 0; i < MAX_ALLOCATIONS; i++) {
  54. if (allocated[i] == p) {
  55. allocated[i] = NULL;
  56. break;
  57. }
  58. }
  59. free(p);
  60. }
  61. static void free_all(void)
  62. {
  63. unsigned int i;
  64. for (i = 0; i < MAX_ALLOCATIONS; i++) {
  65. free(allocated[i]);
  66. allocated[i] = NULL;
  67. }
  68. }
  69. #define malloc malloc_noleak
  70. #define free(x) free_noleak(x)
  71. #define realloc realloc_noleak
  72. #include "ntdb-source.h"
  73. #undef malloc
  74. #undef free
  75. #undef realloc
  76. #undef write
  77. #undef pwrite
  78. #undef fcntl
  79. #undef ftruncate
  80. #include <stdbool.h>
  81. #include <stdarg.h>
  82. #include <ccan/err/err.h>
  83. #include <setjmp.h>
  84. #include "external-agent.h"
  85. #include "logging.h"
  86. #include "helprun-external-agent.h"
  87. static bool in_transaction;
  88. static int target, current;
  89. static jmp_buf jmpbuf;
  90. #define TEST_DBNAME "run-57-die-during-transaction.ntdb"
  91. #define KEY_STRING "helloworld"
  92. #define DATA_STRING "Helloworld"
  93. static void maybe_die(int fd)
  94. {
  95. if (in_transaction && current++ == target) {
  96. longjmp(jmpbuf, 1);
  97. }
  98. }
  99. static ssize_t pwrite_check(int fd,
  100. const void *buf, size_t count, off_t offset)
  101. {
  102. ssize_t ret;
  103. maybe_die(fd);
  104. ret = pwrite(fd, buf, count, offset);
  105. if (ret != count)
  106. return ret;
  107. maybe_die(fd);
  108. return ret;
  109. }
  110. static ssize_t write_check(int fd, const void *buf, size_t count)
  111. {
  112. ssize_t ret;
  113. maybe_die(fd);
  114. ret = write(fd, buf, count);
  115. if (ret != count)
  116. return ret;
  117. maybe_die(fd);
  118. return ret;
  119. }
  120. static int ftruncate_check(int fd, off_t length)
  121. {
  122. int ret;
  123. maybe_die(fd);
  124. ret = ftruncate(fd, length);
  125. maybe_die(fd);
  126. return ret;
  127. }
  128. static bool test_death(enum operation op, struct agent *agent,
  129. bool pre_create_recovery)
  130. {
  131. struct ntdb_context *ntdb = NULL;
  132. NTDB_DATA key, data;
  133. enum agent_return ret;
  134. int needed_recovery = 0;
  135. current = target = 0;
  136. /* Big long data to force a change. */
  137. data = ntdb_mkdata(DATA_STRING, strlen(DATA_STRING));
  138. reset:
  139. unlink(TEST_DBNAME);
  140. ntdb = ntdb_open(TEST_DBNAME, NTDB_NOMMAP|MAYBE_NOSYNC,
  141. O_CREAT|O_TRUNC|O_RDWR, 0600, &tap_log_attr);
  142. if (!ntdb) {
  143. diag("Failed opening NTDB: %s", strerror(errno));
  144. return false;
  145. }
  146. if (setjmp(jmpbuf) != 0) {
  147. /* We're partway through. Simulate our death. */
  148. close(ntdb->file->fd);
  149. forget_locking();
  150. in_transaction = false;
  151. ret = external_agent_operation(agent, NEEDS_RECOVERY, "");
  152. if (ret == SUCCESS)
  153. needed_recovery++;
  154. else if (ret != FAILED) {
  155. diag("Step %u agent NEEDS_RECOVERY = %s", current,
  156. agent_return_name(ret));
  157. return false;
  158. }
  159. /* Could be key, or data. */
  160. ret = external_agent_operation(agent, op,
  161. KEY_STRING "=" KEY_STRING);
  162. if (ret != SUCCESS) {
  163. ret = external_agent_operation(agent, op,
  164. KEY_STRING
  165. "=" DATA_STRING);
  166. }
  167. if (ret != SUCCESS) {
  168. diag("Step %u op %s failed = %s", current,
  169. operation_name(op),
  170. agent_return_name(ret));
  171. return false;
  172. }
  173. ret = external_agent_operation(agent, NEEDS_RECOVERY, "");
  174. if (ret != FAILED) {
  175. diag("Still needs recovery after step %u = %s",
  176. current, agent_return_name(ret));
  177. return false;
  178. }
  179. ret = external_agent_operation(agent, CHECK, "");
  180. if (ret != SUCCESS) {
  181. diag("Step %u check failed = %s", current,
  182. agent_return_name(ret));
  183. return false;
  184. }
  185. ret = external_agent_operation(agent, CLOSE, "");
  186. if (ret != SUCCESS) {
  187. diag("Step %u close failed = %s", current,
  188. agent_return_name(ret));
  189. return false;
  190. }
  191. /* Suppress logging as this tries to use closed fd. */
  192. suppress_logging = true;
  193. suppress_lockcheck = true;
  194. ntdb_close(ntdb);
  195. suppress_logging = false;
  196. suppress_lockcheck = false;
  197. target++;
  198. current = 0;
  199. free_all();
  200. goto reset;
  201. }
  202. /* Put key for agent to fetch. */
  203. key = ntdb_mkdata(KEY_STRING, strlen(KEY_STRING));
  204. if (pre_create_recovery) {
  205. /* Using a transaction now means we allocate the recovery
  206. * area immediately. That makes the later transaction smaller
  207. * and thus tickles a bug we had. */
  208. if (ntdb_transaction_start(ntdb) != 0)
  209. return false;
  210. }
  211. if (ntdb_store(ntdb, key, key, NTDB_INSERT) != 0)
  212. return false;
  213. if (pre_create_recovery) {
  214. if (ntdb_transaction_commit(ntdb) != 0)
  215. return false;
  216. }
  217. /* This is the key we insert in transaction. */
  218. key.dsize--;
  219. ret = external_agent_operation(agent, OPEN, TEST_DBNAME);
  220. if (ret != SUCCESS)
  221. errx(1, "Agent failed to open: %s", agent_return_name(ret));
  222. ret = external_agent_operation(agent, FETCH, KEY_STRING "=" KEY_STRING);
  223. if (ret != SUCCESS)
  224. errx(1, "Agent failed find key: %s", agent_return_name(ret));
  225. in_transaction = true;
  226. if (ntdb_transaction_start(ntdb) != 0)
  227. return false;
  228. if (ntdb_store(ntdb, key, data, NTDB_INSERT) != 0)
  229. return false;
  230. if (ntdb_transaction_commit(ntdb) != 0)
  231. return false;
  232. in_transaction = false;
  233. /* We made it! */
  234. diag("Completed %u runs", current);
  235. ntdb_close(ntdb);
  236. ret = external_agent_operation(agent, CLOSE, "");
  237. if (ret != SUCCESS) {
  238. diag("Step %u close failed = %s", current,
  239. agent_return_name(ret));
  240. return false;
  241. }
  242. ok1(needed_recovery);
  243. ok1(locking_errors == 0);
  244. ok1(forget_locking() == 0);
  245. locking_errors = 0;
  246. return true;
  247. }
  248. int main(int argc, char *argv[])
  249. {
  250. enum operation ops[] = { FETCH, STORE, TRANSACTION_START };
  251. struct agent *agent;
  252. int i, j;
  253. plan_tests(24);
  254. unlock_callback = maybe_die;
  255. external_agent_free = free_noleak;
  256. agent = prepare_external_agent();
  257. if (!agent)
  258. err(1, "preparing agent");
  259. for (j = 0; j < 2; j++) {
  260. for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) {
  261. diag("Testing %s after death (%s recovery area)",
  262. operation_name(ops[i]), j ? "with" : "without");
  263. ok1(test_death(ops[i], agent, j));
  264. }
  265. }
  266. free_external_agent(agent);
  267. return exit_status();
  268. }