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