source: trunk/server/lib/tdb/tools/tdbtorture.c

Last change on this file was 987, checked in by Silvan Scherrer, 9 years ago

samba server: fix crlf in tdb trunk code

File size: 9.2 KB
Line 
1/* this tests tdb by doing lots of ops from several simultaneous
2 writers - that stresses the locking code.
3*/
4
5#include "replace.h"
6#include "system/time.h"
7#include "system/wait.h"
8#include "system/filesys.h"
9#include "tdb.h"
10
11#ifdef HAVE_GETOPT_H
12#include <getopt.h>
13#endif
14
15#ifdef __OS2__
16#include <sys/socket.h>
17#define pipe(A) socketpair(AF_UNIX, SOCK_STREAM, 0, A)
18#endif
19
20#define REOPEN_PROB 30
21#define DELETE_PROB 8
22#define STORE_PROB 4
23#define APPEND_PROB 6
24#define TRANSACTION_PROB 10
25#define TRANSACTION_PREPARE_PROB 2
26#define LOCKSTORE_PROB 5
27#define TRAVERSE_PROB 20
28#define TRAVERSE_READ_PROB 20
29#define CULL_PROB 100
30#define KEYLEN 3
31#define DATALEN 100
32
33static struct tdb_context *db;
34static int in_transaction;
35static int error_count;
36static int always_transaction = 0;
37static int hash_size = 2;
38static int loopnum;
39static int count_pipe;
40static struct tdb_logging_context log_ctx;
41
42#ifdef PRINTF_ATTRIBUTE
43static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
44#endif
45static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...)
46{
47 va_list ap;
48
49 /* trace level messages do not indicate an error */
50 if (level != TDB_DEBUG_TRACE) {
51 error_count++;
52 }
53
54 va_start(ap, format);
55 vfprintf(stdout, format, ap);
56 va_end(ap);
57 fflush(stdout);
58#if 0
59 if (level != TDB_DEBUG_TRACE) {
60 char *ptr;
61 signal(SIGUSR1, SIG_IGN);
62 asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid());
63 system(ptr);
64 free(ptr);
65 }
66#endif
67}
68
69static void fatal(const char *why)
70{
71 perror(why);
72 error_count++;
73}
74
75static char *randbuf(int len)
76{
77 char *buf;
78 int i;
79 buf = (char *)malloc(len+1);
80
81 for (i=0;i<len;i++) {
82 buf[i] = 'a' + (rand() % 26);
83 }
84 buf[i] = 0;
85 return buf;
86}
87
88static int cull_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
89 void *state)
90{
91#if CULL_PROB
92 if (random() % CULL_PROB == 0) {
93 tdb_delete(tdb, key);
94 }
95#endif
96 return 0;
97}
98
99static void addrec_db(void)
100{
101 int klen, dlen;
102 char *k, *d;
103 TDB_DATA key, data;
104
105 klen = 1 + (rand() % KEYLEN);
106 dlen = 1 + (rand() % DATALEN);
107
108 k = randbuf(klen);
109 d = randbuf(dlen);
110
111 key.dptr = (unsigned char *)k;
112 key.dsize = klen+1;
113
114 data.dptr = (unsigned char *)d;
115 data.dsize = dlen+1;
116
117#if REOPEN_PROB
118 if (in_transaction == 0 && random() % REOPEN_PROB == 0) {
119 tdb_reopen_all(0);
120 goto next;
121 }
122#endif
123
124#if TRANSACTION_PROB
125 if (in_transaction == 0 &&
126 (always_transaction || random() % TRANSACTION_PROB == 0)) {
127 if (tdb_transaction_start(db) != 0) {
128 fatal("tdb_transaction_start failed");
129 }
130 in_transaction++;
131 goto next;
132 }
133 if (in_transaction && random() % TRANSACTION_PROB == 0) {
134 if (random() % TRANSACTION_PREPARE_PROB == 0) {
135 if (tdb_transaction_prepare_commit(db) != 0) {
136 fatal("tdb_transaction_prepare_commit failed");
137 }
138 }
139 if (tdb_transaction_commit(db) != 0) {
140 fatal("tdb_transaction_commit failed");
141 }
142 in_transaction--;
143 goto next;
144 }
145 if (in_transaction && random() % TRANSACTION_PROB == 0) {
146 if (tdb_transaction_cancel(db) != 0) {
147 fatal("tdb_transaction_cancel failed");
148 }
149 in_transaction--;
150 goto next;
151 }
152#endif
153
154#if DELETE_PROB
155 if (random() % DELETE_PROB == 0) {
156 tdb_delete(db, key);
157 goto next;
158 }
159#endif
160
161#if STORE_PROB
162 if (random() % STORE_PROB == 0) {
163 if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
164 fatal("tdb_store failed");
165 }
166 goto next;
167 }
168#endif
169
170#if APPEND_PROB
171 if (random() % APPEND_PROB == 0) {
172 if (tdb_append(db, key, data) != 0) {
173 fatal("tdb_append failed");
174 }
175 goto next;
176 }
177#endif
178
179#if LOCKSTORE_PROB
180 if (random() % LOCKSTORE_PROB == 0) {
181 tdb_chainlock(db, key);
182 data = tdb_fetch(db, key);
183 if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
184 fatal("tdb_store failed");
185 }
186 if (data.dptr) free(data.dptr);
187 tdb_chainunlock(db, key);
188 goto next;
189 }
190#endif
191
192#if TRAVERSE_PROB
193 if (random() % TRAVERSE_PROB == 0) {
194 tdb_traverse(db, cull_traverse, NULL);
195 goto next;
196 }
197#endif
198
199#if TRAVERSE_READ_PROB
200 if (random() % TRAVERSE_READ_PROB == 0) {
201 tdb_traverse_read(db, NULL, NULL);
202 goto next;
203 }
204#endif
205
206 data = tdb_fetch(db, key);
207 if (data.dptr) free(data.dptr);
208
209next:
210 free(k);
211 free(d);
212}
213
214static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
215 void *state)
216{
217 tdb_delete(tdb, key);
218 return 0;
219}
220
221static void usage(void)
222{
223 printf("Usage: tdbtorture [-t] [-k] [-n NUM_PROCS] [-l NUM_LOOPS] [-s SEED] [-H HASH_SIZE]\n");
224 exit(0);
225}
226
227static void send_count_and_suicide(int sig)
228{
229 /* This ensures our successor can continue where we left off. */
230 write(count_pipe, &loopnum, sizeof(loopnum));
231 /* This gives a unique signature. */
232 kill(getpid(), SIGUSR2);
233}
234
235static int run_child(const char *filename, int i, int seed, unsigned num_loops, unsigned start)
236{
237 db = tdb_open_ex(filename, hash_size, TDB_DEFAULT,
238 O_RDWR | O_CREAT, 0600, &log_ctx, NULL);
239 if (!db) {
240 fatal("db open failed");
241 }
242
243 srand(seed + i);
244 srandom(seed + i);
245
246 /* Set global, then we're ready to handle being killed. */
247 loopnum = start;
248 signal(SIGUSR1, send_count_and_suicide);
249
250 for (;loopnum<num_loops && error_count == 0;loopnum++) {
251 addrec_db();
252 }
253
254 if (error_count == 0) {
255 tdb_traverse_read(db, NULL, NULL);
256 if (always_transaction) {
257 while (in_transaction) {
258 tdb_transaction_cancel(db);
259 in_transaction--;
260 }
261 if (tdb_transaction_start(db) != 0)
262 fatal("tdb_transaction_start failed");
263 }
264 tdb_traverse(db, traverse_fn, NULL);
265 tdb_traverse(db, traverse_fn, NULL);
266 if (always_transaction) {
267 if (tdb_transaction_commit(db) != 0)
268 fatal("tdb_transaction_commit failed");
269 }
270 }
271
272 tdb_close(db);
273
274 return (error_count < 100 ? error_count : 100);
275}
276
277static char *test_path(const char *filename)
278{
279 const char *prefix = getenv("TEST_DATA_PREFIX");
280
281 if (prefix) {
282 char *path = NULL;
283 int ret;
284
285 ret = asprintf(&path, "%s/%s", prefix, filename);
286 if (ret == -1) {
287 return NULL;
288 }
289 return path;
290 }
291
292 return strdup(filename);
293}
294
295int main(int argc, char * const *argv)
296{
297 int i, seed = -1;
298 int num_loops = 5000;
299 int num_procs = 3;
300 int c, pfds[2];
301 extern char *optarg;
302 pid_t *pids;
303 int kill_random = 0;
304 int *done;
305 char *test_tdb;
306
307 log_ctx.log_fn = tdb_log;
308
309 while ((c = getopt(argc, argv, "n:l:s:H:thk")) != -1) {
310 switch (c) {
311 case 'n':
312 num_procs = strtol(optarg, NULL, 0);
313 break;
314 case 'l':
315 num_loops = strtol(optarg, NULL, 0);
316 break;
317 case 'H':
318 hash_size = strtol(optarg, NULL, 0);
319 break;
320 case 's':
321 seed = strtol(optarg, NULL, 0);
322 break;
323 case 't':
324 always_transaction = 1;
325 break;
326 case 'k':
327 kill_random = 1;
328 break;
329 default:
330 usage();
331 }
332 }
333
334 test_tdb = test_path("torture.tdb");
335
336 unlink(test_tdb);
337
338 if (seed == -1) {
339 seed = (getpid() + time(NULL)) & 0x7FFFFFFF;
340 }
341
342 if (num_procs == 1 && !kill_random) {
343 /* Don't fork for this case, makes debugging easier. */
344 error_count = run_child(test_tdb, 0, seed, num_loops, 0);
345 goto done;
346 }
347
348 pids = (pid_t *)calloc(sizeof(pid_t), num_procs);
349 done = (int *)calloc(sizeof(int), num_procs);
350
351 if (pipe(pfds) != 0) {
352 perror("Creating pipe");
353 exit(1);
354 }
355 count_pipe = pfds[1];
356
357 for (i=0;i<num_procs;i++) {
358 if ((pids[i]=fork()) == 0) {
359 close(pfds[0]);
360 if (i == 0) {
361 printf("Testing with %d processes, %d loops, %d hash_size, seed=%d%s\n",
362 num_procs, num_loops, hash_size, seed, always_transaction ? " (all within transactions)" : "");
363 }
364 exit(run_child(test_tdb, i, seed, num_loops, 0));
365 }
366 }
367
368 while (num_procs) {
369 int status, j;
370 pid_t pid;
371
372 if (error_count != 0) {
373 /* try and stop the test on any failure */
374 for (j=0;j<num_procs;j++) {
375 if (pids[j] != 0) {
376 kill(pids[j], SIGTERM);
377 }
378 }
379 }
380
381 pid = waitpid(-1, &status, kill_random ? WNOHANG : 0);
382 if (pid == 0) {
383 struct timeval tv;
384
385 /* Sleep for 1/10 second. */
386 tv.tv_sec = 0;
387 tv.tv_usec = 100000;
388 select(0, NULL, NULL, NULL, &tv);
389
390 /* Kill someone. */
391 kill(pids[random() % num_procs], SIGUSR1);
392 continue;
393 }
394
395 if (pid == -1) {
396 perror("failed to wait for child\n");
397 exit(1);
398 }
399
400 for (j=0;j<num_procs;j++) {
401 if (pids[j] == pid) break;
402 }
403 if (j == num_procs) {
404 printf("unknown child %d exited!?\n", (int)pid);
405 exit(1);
406 }
407 if (WIFSIGNALED(status)) {
408 if (WTERMSIG(status) == SIGUSR2
409 || WTERMSIG(status) == SIGUSR1) {
410 /* SIGUSR2 means they wrote to pipe. */
411 if (WTERMSIG(status) == SIGUSR2) {
412 read(pfds[0], &done[j],
413 sizeof(done[j]));
414 }
415 pids[j] = fork();
416 if (pids[j] == 0)
417 exit(run_child(test_tdb, j, seed,
418 num_loops, done[j]));
419 printf("Restarting child %i for %u-%u\n",
420 j, done[j], num_loops);
421 continue;
422 }
423 printf("child %d exited with signal %d\n",
424 (int)pid, WTERMSIG(status));
425 error_count++;
426 } else {
427 if (WEXITSTATUS(status) != 0) {
428 printf("child %d exited with status %d\n",
429 (int)pid, WEXITSTATUS(status));
430 error_count++;
431 }
432 }
433 memmove(&pids[j], &pids[j+1],
434 (num_procs - j - 1)*sizeof(pids[0]));
435 num_procs--;
436 }
437
438 free(pids);
439
440done:
441 if (error_count == 0) {
442 db = tdb_open_ex(test_tdb, hash_size, TDB_DEFAULT,
443 O_RDWR, 0, &log_ctx, NULL);
444 if (!db) {
445 fatal("db open failed");
446 }
447 if (tdb_check(db, NULL, NULL) == -1) {
448 printf("db check failed");
449 exit(1);
450 }
451 tdb_close(db);
452 printf("OK\n");
453 }
454
455 free(test_tdb);
456 return error_count;
457}
Note: See TracBrowser for help on using the repository browser.