00001
00002
00003
00004
00005 #include "replace.h"
00006 #include "tdb.h"
00007 #include "system/time.h"
00008 #include "system/wait.h"
00009 #include "system/filesys.h"
00010
00011 #ifdef HAVE_GETOPT_H
00012 #include <getopt.h>
00013 #endif
00014
00015
00016 #define REOPEN_PROB 30
00017 #define DELETE_PROB 8
00018 #define STORE_PROB 4
00019 #define APPEND_PROB 6
00020 #define TRANSACTION_PROB 10
00021 #define LOCKSTORE_PROB 5
00022 #define TRAVERSE_PROB 20
00023 #define TRAVERSE_READ_PROB 20
00024 #define CULL_PROB 100
00025 #define KEYLEN 3
00026 #define DATALEN 100
00027
00028 static struct tdb_context *db;
00029 static int in_transaction;
00030 static int error_count;
00031
00032 #ifdef PRINTF_ATTRIBUTE
00033 static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
00034 #endif
00035 static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...)
00036 {
00037 va_list ap;
00038
00039 error_count++;
00040
00041 va_start(ap, format);
00042 vfprintf(stdout, format, ap);
00043 va_end(ap);
00044 fflush(stdout);
00045 #if 0
00046 {
00047 char *ptr;
00048 asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid());
00049 system(ptr);
00050 free(ptr);
00051 }
00052 #endif
00053 }
00054
00055 static void fatal(const char *why)
00056 {
00057 perror(why);
00058 error_count++;
00059 }
00060
00061 static char *randbuf(int len)
00062 {
00063 char *buf;
00064 int i;
00065 buf = (char *)malloc(len+1);
00066
00067 for (i=0;i<len;i++) {
00068 buf[i] = 'a' + (rand() % 26);
00069 }
00070 buf[i] = 0;
00071 return buf;
00072 }
00073
00074 static int cull_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
00075 void *state)
00076 {
00077 #if CULL_PROB
00078 if (random() % CULL_PROB == 0) {
00079 tdb_delete(tdb, key);
00080 }
00081 #endif
00082 return 0;
00083 }
00084
00085 static void addrec_db(void)
00086 {
00087 int klen, dlen;
00088 char *k, *d;
00089 TDB_DATA key, data;
00090
00091 klen = 1 + (rand() % KEYLEN);
00092 dlen = 1 + (rand() % DATALEN);
00093
00094 k = randbuf(klen);
00095 d = randbuf(dlen);
00096
00097 key.dptr = (unsigned char *)k;
00098 key.dsize = klen+1;
00099
00100 data.dptr = (unsigned char *)d;
00101 data.dsize = dlen+1;
00102
00103 #if TRANSACTION_PROB
00104 if (in_transaction == 0 && random() % TRANSACTION_PROB == 0) {
00105 if (tdb_transaction_start(db) != 0) {
00106 fatal("tdb_transaction_start failed");
00107 }
00108 in_transaction++;
00109 goto next;
00110 }
00111 if (in_transaction && random() % TRANSACTION_PROB == 0) {
00112 if (tdb_transaction_commit(db) != 0) {
00113 fatal("tdb_transaction_commit failed");
00114 }
00115 in_transaction--;
00116 goto next;
00117 }
00118 if (in_transaction && random() % TRANSACTION_PROB == 0) {
00119 if (tdb_transaction_cancel(db) != 0) {
00120 fatal("tdb_transaction_cancel failed");
00121 }
00122 in_transaction--;
00123 goto next;
00124 }
00125 #endif
00126
00127 #if REOPEN_PROB
00128 if (in_transaction == 0 && random() % REOPEN_PROB == 0) {
00129 tdb_reopen_all(0);
00130 goto next;
00131 }
00132 #endif
00133
00134 #if DELETE_PROB
00135 if (random() % DELETE_PROB == 0) {
00136 tdb_delete(db, key);
00137 goto next;
00138 }
00139 #endif
00140
00141 #if STORE_PROB
00142 if (random() % STORE_PROB == 0) {
00143 if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
00144 fatal("tdb_store failed");
00145 }
00146 goto next;
00147 }
00148 #endif
00149
00150 #if APPEND_PROB
00151 if (random() % APPEND_PROB == 0) {
00152 if (tdb_append(db, key, data) != 0) {
00153 fatal("tdb_append failed");
00154 }
00155 goto next;
00156 }
00157 #endif
00158
00159 #if LOCKSTORE_PROB
00160 if (random() % LOCKSTORE_PROB == 0) {
00161 tdb_chainlock(db, key);
00162 data = tdb_fetch(db, key);
00163 if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
00164 fatal("tdb_store failed");
00165 }
00166 if (data.dptr) free(data.dptr);
00167 tdb_chainunlock(db, key);
00168 goto next;
00169 }
00170 #endif
00171
00172 #if TRAVERSE_PROB
00173 if (random() % TRAVERSE_PROB == 0) {
00174 tdb_traverse(db, cull_traverse, NULL);
00175 goto next;
00176 }
00177 #endif
00178
00179 #if TRAVERSE_READ_PROB
00180 if (random() % TRAVERSE_READ_PROB == 0) {
00181 tdb_traverse_read(db, NULL, NULL);
00182 goto next;
00183 }
00184 #endif
00185
00186 data = tdb_fetch(db, key);
00187 if (data.dptr) free(data.dptr);
00188
00189 next:
00190 free(k);
00191 free(d);
00192 }
00193
00194 static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
00195 void *state)
00196 {
00197 tdb_delete(tdb, key);
00198 return 0;
00199 }
00200
00201 static void usage(void)
00202 {
00203 printf("Usage: tdbtorture [-n NUM_PROCS] [-l NUM_LOOPS] [-s SEED] [-H HASH_SIZE]\n");
00204 exit(0);
00205 }
00206
00207 int main(int argc, char * const *argv)
00208 {
00209 int i, seed = -1;
00210 int num_procs = 3;
00211 int num_loops = 5000;
00212 int hash_size = 2;
00213 int c;
00214 extern char *optarg;
00215 pid_t *pids;
00216
00217 struct tdb_logging_context log_ctx;
00218 log_ctx.log_fn = tdb_log;
00219
00220 while ((c = getopt(argc, argv, "n:l:s:H:h")) != -1) {
00221 switch (c) {
00222 case 'n':
00223 num_procs = strtol(optarg, NULL, 0);
00224 break;
00225 case 'l':
00226 num_loops = strtol(optarg, NULL, 0);
00227 break;
00228 case 'H':
00229 hash_size = strtol(optarg, NULL, 0);
00230 break;
00231 case 's':
00232 seed = strtol(optarg, NULL, 0);
00233 break;
00234 default:
00235 usage();
00236 }
00237 }
00238
00239 unlink("torture.tdb");
00240
00241 pids = calloc(sizeof(pid_t), num_procs);
00242 pids[0] = getpid();
00243
00244 for (i=0;i<num_procs-1;i++) {
00245 if ((pids[i+1]=fork()) == 0) break;
00246 }
00247
00248 db = tdb_open_ex("torture.tdb", hash_size, TDB_CLEAR_IF_FIRST,
00249 O_RDWR | O_CREAT, 0600, &log_ctx, NULL);
00250 if (!db) {
00251 fatal("db open failed");
00252 }
00253
00254 if (seed == -1) {
00255 seed = (getpid() + time(NULL)) & 0x7FFFFFFF;
00256 }
00257
00258 if (i == 0) {
00259 printf("testing with %d processes, %d loops, %d hash_size, seed=%d\n",
00260 num_procs, num_loops, hash_size, seed);
00261 }
00262
00263 srand(seed + i);
00264 srandom(seed + i);
00265
00266 for (i=0;i<num_loops && error_count == 0;i++) {
00267 addrec_db();
00268 }
00269
00270 if (error_count == 0) {
00271 tdb_traverse_read(db, NULL, NULL);
00272 tdb_traverse(db, traverse_fn, NULL);
00273 tdb_traverse(db, traverse_fn, NULL);
00274 }
00275
00276 tdb_close(db);
00277
00278 if (getpid() != pids[0]) {
00279 return error_count;
00280 }
00281
00282 for (i=1;i<num_procs;i++) {
00283 int status, j;
00284 pid_t pid;
00285 if (error_count != 0) {
00286
00287 for (j=1;j<num_procs;j++) {
00288 if (pids[j] != 0) {
00289 kill(pids[j], SIGTERM);
00290 }
00291 }
00292 }
00293 pid = waitpid(-1, &status, 0);
00294 if (pid == -1) {
00295 perror("failed to wait for child\n");
00296 exit(1);
00297 }
00298 for (j=1;j<num_procs;j++) {
00299 if (pids[j] == pid) break;
00300 }
00301 if (j == num_procs) {
00302 printf("unknown child %d exited!?\n", (int)pid);
00303 exit(1);
00304 }
00305 if (WEXITSTATUS(status) != 0) {
00306 printf("child %d exited with status %d\n",
00307 (int)pid, WEXITSTATUS(status));
00308 error_count++;
00309 }
00310 pids[j] = 0;
00311 }
00312
00313 if (error_count == 0) {
00314 printf("OK\n");
00315 }
00316
00317 return error_count;
00318 }