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