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