9b0a60348c8d41bfbee7626b1bd174f966be6d10
[samba.git] / source4 / lib / tdb / tools / tdbtorture.c
1 /* this tests tdb by doing lots of ops from several simultaneous
2    writers - that stresses the locking code. 
3 */
4
5 #ifndef _SAMBA_BUILD_
6 #define _GNU_SOURCE
7 #include <stdlib.h>
8 #include <time.h>
9 #include <stdio.h>
10 #include <fcntl.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <fcntl.h>
14 #include <signal.h>
15 #include <stdarg.h>
16 #include <sys/mman.h>
17 #include <sys/stat.h>
18 #include <sys/time.h>
19 #include <sys/wait.h>
20 #include "tdb.h"
21
22 #else
23
24 #include "includes.h"
25 #include "lib/tdb/include/tdb.h"
26 #include "system/time.h"
27 #include "system/wait.h"
28 #include "system/filesys.h"
29
30 #endif
31
32 #ifdef HAVE_GETOPT_H
33 #include <getopt.h>
34 #endif
35
36
37 #define REOPEN_PROB 30
38 #define DELETE_PROB 8
39 #define STORE_PROB 4
40 #define APPEND_PROB 6
41 #define TRANSACTION_PROB 10
42 #define LOCKSTORE_PROB 5
43 #define TRAVERSE_PROB 20
44 #define TRAVERSE_READ_PROB 20
45 #define CULL_PROB 100
46 #define KEYLEN 3
47 #define DATALEN 100
48
49 static struct tdb_context *db;
50 static int in_transaction;
51 static int error_count;
52
53 #ifdef PRINTF_ATTRIBUTE
54 static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
55 #endif
56 static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...)
57 {
58         va_list ap;
59     
60         error_count++;
61
62         va_start(ap, format);
63         vfprintf(stdout, format, ap);
64         va_end(ap);
65         fflush(stdout);
66 #if 0
67         {
68                 char *ptr;
69                 asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid());
70                 system(ptr);
71                 free(ptr);
72         }
73 #endif  
74 }
75
76 static void fatal(const char *why)
77 {
78         perror(why);
79         error_count++;
80 }
81
82 static char *randbuf(int len)
83 {
84         char *buf;
85         int i;
86         buf = (char *)malloc(len+1);
87
88         for (i=0;i<len;i++) {
89                 buf[i] = 'a' + (rand() % 26);
90         }
91         buf[i] = 0;
92         return buf;
93 }
94
95 static int cull_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
96                          void *state)
97 {
98 #if CULL_PROB
99         if (random() % CULL_PROB == 0) {
100                 tdb_delete(tdb, key);
101         }
102 #endif
103         return 0;
104 }
105
106 static void addrec_db(void)
107 {
108         int klen, dlen;
109         char *k, *d;
110         TDB_DATA key, data;
111
112         klen = 1 + (rand() % KEYLEN);
113         dlen = 1 + (rand() % DATALEN);
114
115         k = randbuf(klen);
116         d = randbuf(dlen);
117
118         key.dptr = (unsigned char *)k;
119         key.dsize = klen+1;
120
121         data.dptr = (unsigned char *)d;
122         data.dsize = dlen+1;
123
124 #if TRANSACTION_PROB
125         if (in_transaction == 0 && random() % TRANSACTION_PROB == 0) {
126                 if (tdb_transaction_start(db) != 0) {
127                         fatal("tdb_transaction_start failed");
128                 }
129                 in_transaction++;
130                 goto next;
131         }
132         if (in_transaction && random() % TRANSACTION_PROB == 0) {
133                 if (tdb_transaction_commit(db) != 0) {
134                         fatal("tdb_transaction_commit failed");
135                 }
136                 in_transaction--;
137                 goto next;
138         }
139         if (in_transaction && random() % TRANSACTION_PROB == 0) {
140                 if (tdb_transaction_cancel(db) != 0) {
141                         fatal("tdb_transaction_cancel failed");
142                 }
143                 in_transaction--;
144                 goto next;
145         }
146 #endif
147
148 #if REOPEN_PROB
149         if (in_transaction == 0 && random() % REOPEN_PROB == 0) {
150                 tdb_reopen_all(0);
151                 goto next;
152         } 
153 #endif
154
155 #if DELETE_PROB
156         if (random() % DELETE_PROB == 0) {
157                 tdb_delete(db, key);
158                 goto next;
159         }
160 #endif
161
162 #if STORE_PROB
163         if (random() % STORE_PROB == 0) {
164                 if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
165                         fatal("tdb_store failed");
166                 }
167                 goto next;
168         }
169 #endif
170
171 #if APPEND_PROB
172         if (random() % APPEND_PROB == 0) {
173                 if (tdb_append(db, key, data) != 0) {
174                         fatal("tdb_append failed");
175                 }
176                 goto next;
177         }
178 #endif
179
180 #if LOCKSTORE_PROB
181         if (random() % LOCKSTORE_PROB == 0) {
182                 tdb_chainlock(db, key);
183                 data = tdb_fetch(db, key);
184                 if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
185                         fatal("tdb_store failed");
186                 }
187                 if (data.dptr) free(data.dptr);
188                 tdb_chainunlock(db, key);
189                 goto next;
190         } 
191 #endif
192
193 #if TRAVERSE_PROB
194         if (random() % TRAVERSE_PROB == 0) {
195                 tdb_traverse(db, cull_traverse, NULL);
196                 goto next;
197         }
198 #endif
199
200 #if TRAVERSE_READ_PROB
201         if (random() % TRAVERSE_READ_PROB == 0) {
202                 tdb_traverse_read(db, NULL, NULL);
203                 goto next;
204         }
205 #endif
206
207         data = tdb_fetch(db, key);
208         if (data.dptr) free(data.dptr);
209
210 next:
211         free(k);
212         free(d);
213 }
214
215 static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
216                        void *state)
217 {
218         tdb_delete(tdb, key);
219         return 0;
220 }
221
222 static void usage(void)
223 {
224         printf("Usage: tdbtorture [-n NUM_PROCS] [-l NUM_LOOPS] [-s SEED] [-H HASH_SIZE]\n");
225         exit(0);
226 }
227
228  int main(int argc, char * const *argv)
229 {
230         int i, seed = -1;
231         int num_procs = 3;
232         int num_loops = 5000;
233         int hash_size = 2;
234         int c;
235         extern char *optarg;
236         pid_t *pids;
237
238         struct tdb_logging_context log_ctx;
239         log_ctx.log_fn = tdb_log;
240
241         while ((c = getopt(argc, argv, "n:l:s:H:h")) != -1) {
242                 switch (c) {
243                 case 'n':
244                         num_procs = strtol(optarg, NULL, 0);
245                         break;
246                 case 'l':
247                         num_loops = strtol(optarg, NULL, 0);
248                         break;
249                 case 'H':
250                         hash_size = strtol(optarg, NULL, 0);
251                         break;
252                 case 's':
253                         seed = strtol(optarg, NULL, 0);
254                         break;
255                 default:
256                         usage();
257                 }
258         }
259
260         unlink("torture.tdb");
261
262         pids = calloc(sizeof(pid_t), num_procs);
263         pids[0] = getpid();
264
265         for (i=0;i<num_procs-1;i++) {
266                 if ((pids[i+1]=fork()) == 0) break;
267         }
268
269         db = tdb_open_ex("torture.tdb", hash_size, TDB_CLEAR_IF_FIRST, 
270                          O_RDWR | O_CREAT, 0600, &log_ctx, NULL);
271         if (!db) {
272                 fatal("db open failed");
273         }
274
275         if (seed == -1) {
276                 seed = (getpid() + time(NULL)) & 0x7FFFFFFF;
277         }
278
279         if (i == 0) {
280                 printf("testing with %d processes, %d loops, %d hash_size, seed=%d\n", 
281                        num_procs, num_loops, hash_size, seed);
282         }
283
284         srand(seed + i);
285         srandom(seed + i);
286
287         for (i=0;i<num_loops && error_count == 0;i++) {
288                 addrec_db();
289         }
290
291         if (error_count == 0) {
292                 tdb_traverse_read(db, NULL, NULL);
293                 tdb_traverse(db, traverse_fn, NULL);
294                 tdb_traverse(db, traverse_fn, NULL);
295         }
296
297         tdb_close(db);
298
299         if (getpid() != pids[0]) {
300                 return error_count;
301         }
302
303         for (i=1;i<num_procs;i++) {
304                 int status, j;
305                 pid_t pid;
306                 if (error_count != 0) {
307                         /* try and stop the test on any failure */
308                         for (j=1;j<num_procs;j++) {
309                                 if (pids[j] != 0) {
310                                         kill(pids[j], SIGTERM);
311                                 }
312                         }
313                 }
314                 pid = waitpid(-1, &status, 0);
315                 if (pid == -1) {
316                         perror("failed to wait for child\n");
317                         exit(1);
318                 }
319                 for (j=1;j<num_procs;j++) {
320                         if (pids[j] == pid) break;
321                 }
322                 if (j == num_procs) {
323                         printf("unknown child %d exited!?\n", (int)pid);
324                         exit(1);
325                 }
326                 if (WEXITSTATUS(status) != 0) {
327                         printf("child %d exited with status %d\n",
328                                (int)pid, WEXITSTATUS(status));
329                         error_count++;
330                 }
331                 pids[j] = 0;
332         }
333
334         if (error_count == 0) {
335                 printf("OK\n");
336         }
337
338         return error_count;
339 }