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 "tdb.h" 7#include "system/time.h" 8#include "system/wait.h" 9#include "system/filesys.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 28static struct tdb_context *db; 29static int in_transaction; 30static int error_count; 31 32#ifdef PRINTF_ATTRIBUTE 33static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4); 34#endif 35static 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 55static void fatal(const char *why) 56{ 57 perror(why); 58 error_count++; 59} 60 61static 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 74static 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 85static 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 189next: 190 free(k); 191 free(d); 192} 193 194static 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 201static 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 = 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} 319