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