LCOV - code coverage report
Current view: top level - lib/tdb/tools - tdbtorture.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 147 255 57.6 %
Date: 2024-04-21 15:09:00 Functions: 8 12 66.7 %

          Line data    Source code
       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 TRANSACTION_PREPARE_PROB 2
      22             : #define LOCKSTORE_PROB 5
      23             : #define TRAVERSE_PROB 20
      24             : #define TRAVERSE_READ_PROB 20
      25             : #define CULL_PROB 100
      26             : #define KEYLEN 3
      27             : #define DATALEN 100
      28             : 
      29             : static struct tdb_context *db;
      30             : static int in_transaction;
      31             : static int error_count;
      32             : static int always_transaction = 0;
      33             : static int hash_size = 2;
      34             : static unsigned loopnum;
      35             : static int count_pipe;
      36             : static bool mutex = false;
      37             : static struct tdb_logging_context log_ctx;
      38             : 
      39             : #ifdef PRINTF_ATTRIBUTE
      40             : static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
      41             : #endif
      42           0 : static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...)
      43             : {
      44             :         va_list ap;
      45             : 
      46             :         /* trace level messages do not indicate an error */
      47           0 :         if (level != TDB_DEBUG_TRACE) {
      48           0 :                 error_count++;
      49             :         }
      50             : 
      51           0 :         va_start(ap, format);
      52           0 :         vfprintf(stdout, format, ap);
      53           0 :         va_end(ap);
      54           0 :         fflush(stdout);
      55             : #if 0
      56             :         if (level != TDB_DEBUG_TRACE) {
      57             :                 char *ptr;
      58             :                 signal(SIGUSR1, SIG_IGN);
      59             :                 asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid());
      60             :                 system(ptr);
      61             :                 free(ptr);
      62             :         }
      63             : #endif
      64           0 : }
      65             : 
      66           0 : static void fatal(const char *why)
      67             : {
      68           0 :         perror(why);
      69           0 :         error_count++;
      70           0 : }
      71             : 
      72       90000 : static char *randbuf(int len)
      73             : {
      74             :         char *buf;
      75             :         int i;
      76       90000 :         buf = (char *)malloc(len+1);
      77             : 
      78     2451609 :         for (i=0;i<len;i++) {
      79     2361609 :                 buf[i] = 'a' + (rand() % 26);
      80             :         }
      81       90000 :         buf[i] = 0;
      82       90000 :         return buf;
      83             : }
      84             : 
      85      512352 : static int cull_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
      86             :                          void *state)
      87             : {
      88             : #if CULL_PROB
      89      512352 :         if (random() % CULL_PROB == 0) {
      90        5184 :                 tdb_delete(tdb, key);
      91             :         }
      92             : #endif
      93      512352 :         return 0;
      94             : }
      95             : 
      96       57408 : static bool do_transaction(void)
      97             : {
      98             : #if TRANSACTION_PROB
      99       57408 :         if (mutex) {
     100           0 :                 return false;
     101             :         }
     102       57408 :         if (random() % TRANSACTION_PROB == 0) {
     103        5782 :                 return true;
     104             :         }
     105             : #endif
     106       51626 :         return false;
     107             : }
     108             : 
     109       45000 : static void addrec_db(void)
     110             : {
     111             :         int klen, dlen;
     112             :         char *k, *d;
     113             :         TDB_DATA key, data;
     114             : 
     115       45000 :         klen = 1 + (rand() % KEYLEN);
     116       45000 :         dlen = 1 + (rand() % DATALEN);
     117             : 
     118       45000 :         k = randbuf(klen);
     119       45000 :         d = randbuf(dlen);
     120             : 
     121       45000 :         key.dptr = (unsigned char *)k;
     122       45000 :         key.dsize = klen+1;
     123             : 
     124       45000 :         data.dptr = (unsigned char *)d;
     125       45000 :         data.dsize = dlen+1;
     126             : 
     127             : #if REOPEN_PROB
     128       45000 :         if (in_transaction == 0 && random() % REOPEN_PROB == 0) {
     129         980 :                 tdb_reopen_all(0);
     130         980 :                 goto next;
     131             :         }
     132             : #endif
     133             : 
     134       44020 :         if (in_transaction == 0 &&
     135       29130 :             (always_transaction || do_transaction())) {
     136        2893 :                 if (tdb_transaction_start(db) != 0) {
     137           0 :                         fatal("tdb_transaction_start failed");
     138             :                 }
     139        2893 :                 in_transaction++;
     140        2893 :                 goto next;
     141             :         }
     142       41127 :         if (in_transaction && do_transaction()) {
     143        1502 :                 if (random() % TRANSACTION_PREPARE_PROB == 0) {
     144         733 :                         if (tdb_transaction_prepare_commit(db) != 0) {
     145           0 :                                 fatal("tdb_transaction_prepare_commit failed");
     146             :                         }
     147             :                 }
     148        1502 :                 if (tdb_transaction_commit(db) != 0) {
     149           0 :                         fatal("tdb_transaction_commit failed");
     150             :                 }
     151        1502 :                 in_transaction--;
     152        1502 :                 goto next;
     153             :         }
     154       39625 :         if (in_transaction && do_transaction()) {
     155        1387 :                 if (tdb_transaction_cancel(db) != 0) {
     156           0 :                         fatal("tdb_transaction_cancel failed");
     157             :                 }
     158        1387 :                 in_transaction--;
     159        1387 :                 goto next;
     160             :         }
     161             : 
     162             : #if DELETE_PROB
     163       38238 :         if (random() % DELETE_PROB == 0) {
     164        4682 :                 tdb_delete(db, key);
     165        4682 :                 goto next;
     166             :         }
     167             : #endif
     168             : 
     169             : #if STORE_PROB
     170       33556 :         if (random() % STORE_PROB == 0) {
     171        8329 :                 if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
     172           0 :                         fatal("tdb_store failed");
     173             :                 }
     174        8329 :                 goto next;
     175             :         }
     176             : #endif
     177             : 
     178             : #if APPEND_PROB
     179       25227 :         if (random() % APPEND_PROB == 0) {
     180        4227 :                 if (tdb_append(db, key, data) != 0) {
     181           0 :                         fatal("tdb_append failed");
     182             :                 }
     183        4227 :                 goto next;
     184             :         }
     185             : #endif
     186             : 
     187             : #if LOCKSTORE_PROB
     188       21000 :         if (random() % LOCKSTORE_PROB == 0) {
     189        4163 :                 tdb_chainlock(db, key);
     190        4163 :                 data = tdb_fetch(db, key);
     191        4163 :                 if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
     192           0 :                         fatal("tdb_store failed");
     193             :                 }
     194        4163 :                 if (data.dptr) free(data.dptr);
     195        4163 :                 tdb_chainunlock(db, key);
     196        4163 :                 goto next;
     197             :         } 
     198             : #endif
     199             : 
     200             : #if TRAVERSE_PROB
     201       16837 :         if (random() % TRAVERSE_PROB == 0) {
     202         845 :                 tdb_traverse(db, cull_traverse, NULL);
     203         845 :                 goto next;
     204             :         }
     205             : #endif
     206             : 
     207             : #if TRAVERSE_READ_PROB
     208       15992 :         if (random() % TRAVERSE_READ_PROB == 0) {
     209         781 :                 tdb_traverse_read(db, NULL, NULL);
     210         781 :                 goto next;
     211             :         }
     212             : #endif
     213             : 
     214       15211 :         data = tdb_fetch(db, key);
     215       15211 :         if (data.dptr) free(data.dptr);
     216             : 
     217        9752 : next:
     218       45000 :         free(k);
     219       45000 :         free(d);
     220       45000 : }
     221             : 
     222        4045 : static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
     223             :                        void *state)
     224             : {
     225        4045 :         tdb_delete(tdb, key);
     226        4045 :         return 0;
     227             : }
     228             : 
     229           0 : static void usage(void)
     230             : {
     231           0 :         printf("Usage: tdbtorture [-t] [-k] [-m] [-n NUM_PROCS] [-l NUM_LOOPS] [-s SEED] [-H HASH_SIZE]\n");
     232           0 :         exit(0);
     233             : }
     234             : 
     235           0 : static void send_count_and_suicide(int sig)
     236             : {
     237             :         ssize_t ret;
     238             : 
     239             :         /* This ensures our successor can continue where we left off. */
     240             :         do {
     241           0 :                 ret = write(count_pipe, &loopnum, sizeof(loopnum));
     242           0 :         } while (ret == -1 && errno == EINTR);
     243             :         /* This gives a unique signature. */
     244           0 :         kill(getpid(), SIGUSR2);
     245           0 : }
     246             : 
     247           9 : static int run_child(const char *filename, int i, int seed, unsigned num_loops, unsigned start)
     248             : {
     249           9 :         int tdb_flags = TDB_DEFAULT|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH;
     250             : 
     251           9 :         if (mutex) {
     252           0 :                 tdb_flags |= TDB_MUTEX_LOCKING;
     253             :         }
     254             : 
     255           9 :         db = tdb_open_ex(filename, hash_size, tdb_flags,
     256             :                          O_RDWR | O_CREAT, 0600, &log_ctx, NULL);
     257           9 :         if (!db) {
     258           0 :                 fatal("db open failed");
     259             :         }
     260             : 
     261           9 :         srand(seed + i);
     262           9 :         srandom(seed + i);
     263             : 
     264             :         /* Set global, then we're ready to handle being killed. */
     265           9 :         loopnum = start;
     266           9 :         signal(SIGUSR1, send_count_and_suicide);
     267             : 
     268       45009 :         for (;loopnum<num_loops && error_count == 0;loopnum++) {
     269       45000 :                 addrec_db();
     270             :         }
     271             : 
     272           9 :         if (error_count == 0) {
     273           9 :                 tdb_traverse_read(db, NULL, NULL);
     274           9 :                 if (always_transaction) {
     275           0 :                         while (in_transaction) {
     276           0 :                                 tdb_transaction_cancel(db);
     277           0 :                                 in_transaction--;
     278             :                         }
     279           0 :                         if (tdb_transaction_start(db) != 0)
     280           0 :                                 fatal("tdb_transaction_start failed");
     281             :                 }
     282           9 :                 tdb_traverse(db, traverse_fn, NULL);
     283           9 :                 tdb_traverse(db, traverse_fn, NULL);
     284           9 :                 if (always_transaction) {
     285           0 :                         if (tdb_transaction_commit(db) != 0)
     286           0 :                                 fatal("tdb_transaction_commit failed");
     287             :                 }
     288             :         }
     289             : 
     290           9 :         tdb_close(db);
     291             : 
     292           9 :         return (error_count < 100 ? error_count : 100);
     293             : }
     294             : 
     295           3 : static char *test_path(const char *filename)
     296             : {
     297           3 :         const char *prefix = getenv("TEST_DATA_PREFIX");
     298             : 
     299           3 :         if (prefix) {
     300           3 :                 char *path = NULL;
     301             :                 int ret;
     302             : 
     303           3 :                 ret = asprintf(&path, "%s/%s", prefix, filename);
     304           3 :                 if (ret == -1) {
     305           0 :                         return NULL;
     306             :                 }
     307           3 :                 return path;
     308             :         }
     309             : 
     310           0 :         return strdup(filename);
     311             : }
     312             : 
     313           3 : int main(int argc, char * const *argv)
     314             : {
     315           3 :         int i, seed = -1;
     316           3 :         int num_loops = 5000;
     317           3 :         int num_procs = 3;
     318             :         int c, pfds[2];
     319             :         extern char *optarg;
     320             :         pid_t *pids;
     321           3 :         int kill_random = 0;
     322             :         int *done;
     323             :         char *test_tdb;
     324             : 
     325           3 :         log_ctx.log_fn = tdb_log;
     326             : 
     327           3 :         while ((c = getopt(argc, argv, "n:l:s:H:thkm")) != -1) {
     328           0 :                 switch (c) {
     329           0 :                 case 'n':
     330           0 :                         num_procs = strtol(optarg, NULL, 0);
     331           0 :                         break;
     332           0 :                 case 'l':
     333           0 :                         num_loops = strtol(optarg, NULL, 0);
     334           0 :                         break;
     335           0 :                 case 'H':
     336           0 :                         hash_size = strtol(optarg, NULL, 0);
     337           0 :                         break;
     338           0 :                 case 's':
     339           0 :                         seed = strtol(optarg, NULL, 0);
     340           0 :                         break;
     341           0 :                 case 't':
     342           0 :                         always_transaction = 1;
     343           0 :                         break;
     344           0 :                 case 'k':
     345           0 :                         kill_random = 1;
     346           0 :                         break;
     347           0 :                 case 'm':
     348           0 :                         mutex = tdb_runtime_check_for_robust_mutexes();
     349           0 :                         if (!mutex) {
     350           0 :                                 printf("tdb_runtime_check_for_robust_mutexes() returned false\n");
     351           0 :                                 exit(1);
     352             :                         }
     353           0 :                         break;
     354           0 :                 default:
     355           0 :                         usage();
     356             :                 }
     357             :         }
     358             : 
     359           3 :         test_tdb = test_path("torture.tdb");
     360             : 
     361           3 :         unlink(test_tdb);
     362             : 
     363           3 :         if (seed == -1) {
     364           3 :                 seed = (getpid() + time(NULL)) & 0x7FFFFFFF;
     365             :         }
     366             : 
     367           3 :         printf("Testing with %d processes, %d loops, %d hash_size, seed=%d%s\n",
     368             :                num_procs, num_loops, hash_size, seed,
     369           3 :                (always_transaction ? " (all within transactions)" : ""));
     370             : 
     371           3 :         if (num_procs == 1 && !kill_random) {
     372             :                 /* Don't fork for this case, makes debugging easier. */
     373           0 :                 error_count = run_child(test_tdb, 0, seed, num_loops, 0);
     374           0 :                 goto done;
     375             :         }
     376             : 
     377           3 :         pids = (pid_t *)calloc(sizeof(pid_t), num_procs);
     378           3 :         if (pids == NULL) {
     379           0 :                 perror("Unable to allocate memory for pids");
     380           0 :                 exit(1);
     381             :         }
     382           3 :         done = (int *)calloc(sizeof(int), num_procs);
     383           3 :         if (done == NULL) {
     384           0 :                 perror("Unable to allocate memory for done");
     385           0 :                 exit(1);
     386             :         }
     387             : 
     388           3 :         if (pipe(pfds) != 0) {
     389           0 :                 perror("Creating pipe");
     390           0 :                 exit(1);
     391             :         }
     392           3 :         count_pipe = pfds[1];
     393             : 
     394          12 :         for (i=0;i<num_procs;i++) {
     395           9 :                 if ((pids[i]=fork()) == 0) {
     396           9 :                         close(pfds[0]);
     397           9 :                         exit(run_child(test_tdb, i, seed, num_loops, 0));
     398             :                 }
     399             :         }
     400             : 
     401          12 :         while (num_procs) {
     402             :                 int status, j;
     403             :                 pid_t pid;
     404             : 
     405           9 :                 if (error_count != 0) {
     406             :                         /* try and stop the test on any failure */
     407           0 :                         for (j=0;j<num_procs;j++) {
     408           0 :                                 if (pids[j] != 0) {
     409           0 :                                         kill(pids[j], SIGTERM);
     410             :                                 }
     411             :                         }
     412             :                 }
     413             : 
     414           9 :                 pid = waitpid(-1, &status, kill_random ? WNOHANG : 0);
     415           9 :                 if (pid == 0) {
     416             :                         struct timeval tv;
     417             : 
     418             :                         /* Sleep for 1/10 second. */
     419           0 :                         tv.tv_sec = 0;
     420           0 :                         tv.tv_usec = 100000;
     421           0 :                         select(0, NULL, NULL, NULL, &tv);
     422             : 
     423             :                         /* Kill someone. */
     424           0 :                         kill(pids[random() % num_procs], SIGUSR1);
     425           0 :                         continue;
     426             :                 }
     427             : 
     428           9 :                 if (pid == -1) {
     429           0 :                         perror("failed to wait for child\n");
     430           0 :                         exit(1);
     431             :                 }
     432             : 
     433          16 :                 for (j=0;j<num_procs;j++) {
     434          16 :                         if (pids[j] == pid) break;
     435             :                 }
     436           9 :                 if (j == num_procs) {
     437           0 :                         printf("unknown child %d exited!?\n", (int)pid);
     438           0 :                         exit(1);
     439             :                 }
     440           9 :                 if (WIFSIGNALED(status)) {
     441           0 :                         if (WTERMSIG(status) == SIGUSR2
     442           0 :                             || WTERMSIG(status) == SIGUSR1) {
     443             :                                 /* SIGUSR2 means they wrote to pipe. */
     444           0 :                                 if (WTERMSIG(status) == SIGUSR2) {
     445             :                                         ssize_t ret;
     446             : 
     447             :                                         do {
     448           0 :                                                 ret = read(pfds[0], &done[j],
     449             :                                                            sizeof(done[j]));
     450           0 :                                         } while (ret == -1 && errno == EINTR);
     451             :                                 }
     452           0 :                                 pids[j] = fork();
     453           0 :                                 if (pids[j] == 0)
     454           0 :                                         exit(run_child(test_tdb, j, seed,
     455           0 :                                                        num_loops, done[j]));
     456           0 :                                 printf("Restarting child %i for %u-%u\n",
     457           0 :                                        j, done[j], num_loops);
     458           0 :                                 continue;
     459             :                         }
     460           0 :                         printf("child %d exited with signal %d\n",
     461             :                                (int)pid, WTERMSIG(status));
     462           0 :                         error_count++;
     463             :                 } else {
     464           9 :                         if (WEXITSTATUS(status) != 0) {
     465           0 :                                 printf("child %d exited with status %d\n",
     466           0 :                                        (int)pid, WEXITSTATUS(status));
     467           0 :                                 error_count++;
     468             :                         }
     469             :                 }
     470           9 :                 ARRAY_DEL_ELEMENT(pids, j, num_procs);
     471           9 :                 num_procs--;
     472             :         }
     473             : 
     474           3 :         free(pids);
     475             : 
     476           3 : done:
     477           3 :         if (error_count == 0) {
     478           3 :                 int tdb_flags = TDB_DEFAULT;
     479             : 
     480           3 :                 if (mutex) {
     481           0 :                         tdb_flags |= TDB_NOLOCK;
     482             :                 }
     483             : 
     484           3 :                 db = tdb_open_ex(test_tdb, hash_size, tdb_flags,
     485             :                                  O_RDWR, 0, &log_ctx, NULL);
     486           3 :                 if (!db) {
     487           0 :                         fatal("db open failed\n");
     488           0 :                         exit(1);
     489             :                 }
     490           3 :                 if (tdb_check(db, NULL, NULL) == -1) {
     491           0 :                         printf("db check failed\n");
     492           0 :                         exit(1);
     493             :                 }
     494           3 :                 tdb_close(db);
     495           3 :                 printf("OK\n");
     496             :         }
     497             : 
     498           3 :         free(test_tdb);
     499           3 :         return error_count;
     500             : }

Generated by: LCOV version 1.14