Commit 81defbc3 authored by Rusty Russell's avatar Rusty Russell

tdb2: tools/

As per-tdb's tools.
parent 43d881a2
LDLIBS:=../../tdb2.o ../../hash.o ../../tally.o
CFLAGS:=-I../../.. -Wall -g #-g -O3 #-g -pg
LDFLAGS:=-L../../..
default: tdbtorture tdbtool mktdb
clean:
rm -f tdbtorture tdbtool mktdb
#include <ccan/tdb2/tdb2.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <err.h>
int main(int argc, char *argv[])
{
unsigned int i, num_recs;
struct tdb_context *tdb;
if (argc != 3 || (num_recs = atoi(argv[2])) == 0)
errx(1, "Usage: mktdb <tdbfile> <numrecords>");
tdb = tdb_open(argv[1], TDB_DEFAULT, O_CREAT|O_TRUNC|O_RDWR, 0600,NULL);
if (!tdb)
err(1, "Opening %s", argv[1]);
for (i = 0; i < num_recs; i++) {
TDB_DATA d;
d.dptr = (void *)&i;
d.dsize = sizeof(i);
if (tdb_store(tdb, d, d, TDB_INSERT) != 0)
err(1, "Failed to store record %i", i);
}
printf("Done\n");
return 0;
}
/*
Unix SMB/CIFS implementation.
Samba database functions
Copyright (C) Andrew Tridgell 1999-2000
Copyright (C) Paul `Rusty' Russell 2000
Copyright (C) Jeremy Allison 2000
Copyright (C) Andrew Esh 2001
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ccan/tdb2/tdb2.h>
#include <ccan/hash/hash.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
static int do_command(void);
const char *cmdname;
char *arg1, *arg2;
size_t arg1len, arg2len;
int bIterate = 0;
char *line;
TDB_DATA iterate_kbuf;
char cmdline[1024];
static int disable_mmap;
enum commands {
CMD_CREATE_TDB,
CMD_OPEN_TDB,
CMD_OPENJH_TDB,
#if 0
CMD_TRANSACTION_START,
CMD_TRANSACTION_COMMIT,
CMD_TRANSACTION_CANCEL,
#endif
CMD_ERASE,
CMD_DUMP,
CMD_INSERT,
CMD_MOVE,
CMD_STORE,
CMD_SHOW,
CMD_KEYS,
CMD_HEXKEYS,
CMD_DELETE,
#if 0
CMD_LIST_HASH_FREE,
CMD_LIST_FREE,
#endif
CMD_INFO,
CMD_MMAP,
CMD_SPEED,
CMD_FIRST,
CMD_NEXT,
CMD_SYSTEM,
CMD_CHECK,
CMD_QUIT,
CMD_HELP
};
typedef struct {
const char *name;
enum commands cmd;
} COMMAND_TABLE;
COMMAND_TABLE cmd_table[] = {
{"create", CMD_CREATE_TDB},
{"open", CMD_OPEN_TDB},
{"openjh", CMD_OPENJH_TDB},
#if 0
{"transaction_start", CMD_TRANSACTION_START},
{"transaction_commit", CMD_TRANSACTION_COMMIT},
{"transaction_cancel", CMD_TRANSACTION_CANCEL},
#endif
{"erase", CMD_ERASE},
{"dump", CMD_DUMP},
{"insert", CMD_INSERT},
{"move", CMD_MOVE},
{"store", CMD_STORE},
{"show", CMD_SHOW},
{"keys", CMD_KEYS},
{"hexkeys", CMD_HEXKEYS},
{"delete", CMD_DELETE},
#if 0
{"list", CMD_LIST_HASH_FREE},
{"free", CMD_LIST_FREE},
#endif
{"info", CMD_INFO},
{"speed", CMD_SPEED},
{"mmap", CMD_MMAP},
{"first", CMD_FIRST},
{"1", CMD_FIRST},
{"next", CMD_NEXT},
{"n", CMD_NEXT},
{"check", CMD_CHECK},
{"quit", CMD_QUIT},
{"q", CMD_QUIT},
{"!", CMD_SYSTEM},
{NULL, CMD_HELP}
};
struct timeval tp1,tp2;
static void _start_timer(void)
{
gettimeofday(&tp1,NULL);
}
static double _end_timer(void)
{
gettimeofday(&tp2,NULL);
return((tp2.tv_sec - tp1.tv_sec) +
(tp2.tv_usec - tp1.tv_usec)*1.0e-6);
}
static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, void *priv, const char *format, ...)
{
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
}
/* a tdb tool for manipulating a tdb database */
static struct tdb_context *tdb;
static int print_rec(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
static int print_key(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
static int print_hexkey(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
static void print_asc(const char *buf,int len)
{
int i;
/* We're probably printing ASCII strings so don't try to display
the trailing NULL character. */
if (buf[len - 1] == 0)
len--;
for (i=0;i<len;i++)
printf("%c",isprint(buf[i])?buf[i]:'.');
}
static void print_data(const char *buf,int len)
{
int i=0;
if (len<=0) return;
printf("[%03X] ",i);
for (i=0;i<len;) {
printf("%02X ",(int)((unsigned char)buf[i]));
i++;
if (i%8 == 0) printf(" ");
if (i%16 == 0) {
print_asc(&buf[i-16],8); printf(" ");
print_asc(&buf[i-8],8); printf("\n");
if (i<len) printf("[%03X] ",i);
}
}
if (i%16) {
int n;
n = 16 - (i%16);
printf(" ");
if (n>8) printf(" ");
while (n--) printf(" ");
n = i%16;
if (n > 8) n = 8;
print_asc(&buf[i-(i%16)],n); printf(" ");
n = (i%16) - n;
if (n>0) print_asc(&buf[i-n],n);
printf("\n");
}
}
static void help(void)
{
printf("\n"
"tdbtool: \n"
" create dbname : create a database\n"
" open dbname : open an existing database\n"
" openjh dbname : open an existing database (jenkins hash)\n"
" transaction_start : start a transaction\n"
" transaction_commit : commit a transaction\n"
" transaction_cancel : cancel a transaction\n"
" erase : erase the database\n"
" dump : dump the database as strings\n"
" keys : dump the database keys as strings\n"
" hexkeys : dump the database keys as hex values\n"
" info : print summary info about the database\n"
" insert key data : insert a record\n"
" move key file : move a record to a destination tdb\n"
" store key data : store a record (replace)\n"
" show key : show a record by key\n"
" delete key : delete a record by key\n"
" list : print the database hash table and freelist\n"
" free : print the database freelist\n"
" check : check the integrity of an opened database\n"
" speed : perform speed tests on the database\n"
" ! command : execute system command\n"
" 1 | first : print the first record\n"
" n | next : print the next record\n"
" q | quit : terminate\n"
" \\n : repeat 'next' command\n"
"\n");
}
static void terror(const char *why)
{
printf("%s\n", why);
}
static void create_tdb(const char *tdbname)
{
union tdb_attribute log_attr;
log_attr.base.attr = TDB_ATTRIBUTE_LOG;
log_attr.base.next = NULL;
log_attr.log.log_fn = tdb_log;
if (tdb) tdb_close(tdb);
tdb = tdb_open(tdbname, (disable_mmap?TDB_NOMMAP:0),
O_RDWR | O_CREAT | O_TRUNC, 0600, &log_attr);
if (!tdb) {
printf("Could not create %s: %s\n", tdbname, strerror(errno));
}
}
static uint64_t jenkins_hash(const void *key, size_t len, uint64_t seed,
void *priv)
{
return hash_any(key, len, seed);
}
static void open_tdb(const char *tdbname, tdb_hashfn_t hashfn)
{
union tdb_attribute log_attr, hash_attr;
log_attr.base.attr = TDB_ATTRIBUTE_LOG;
log_attr.base.next = NULL;
log_attr.log.log_fn = tdb_log;
hash_attr.base.attr = TDB_ATTRIBUTE_HASH;
hash_attr.base.next = &log_attr;
hash_attr.hash.hash_fn = hashfn;
if (tdb) tdb_close(tdb);
tdb = tdb_open(tdbname, disable_mmap?TDB_NOMMAP:0, O_RDWR, 0600,
hashfn ? &hash_attr : &log_attr);
if (!tdb) {
printf("Could not open %s: %s\n", tdbname, strerror(errno));
}
}
static void insert_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
{
TDB_DATA key, dbuf;
if ((keyname == NULL) || (keylen == 0)) {
terror("need key");
return;
}
key.dptr = (unsigned char *)keyname;
key.dsize = keylen;
dbuf.dptr = (unsigned char *)data;
dbuf.dsize = datalen;
if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
terror("insert failed");
}
}
static void store_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
{
TDB_DATA key, dbuf;
if ((keyname == NULL) || (keylen == 0)) {
terror("need key");
return;
}
if ((data == NULL) || (datalen == 0)) {
terror("need data");
return;
}
key.dptr = (unsigned char *)keyname;
key.dsize = keylen;
dbuf.dptr = (unsigned char *)data;
dbuf.dsize = datalen;
printf("Storing key:\n");
print_rec(tdb, key, dbuf, NULL);
if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
terror("store failed");
}
}
static void show_tdb(char *keyname, size_t keylen)
{
TDB_DATA key, dbuf;
if ((keyname == NULL) || (keylen == 0)) {
terror("need key");
return;
}
key.dptr = (unsigned char *)keyname;
key.dsize = keylen;
dbuf = tdb_fetch(tdb, key);
if (!dbuf.dptr) {
terror("fetch failed");
return;
}
print_rec(tdb, key, dbuf, NULL);
free( dbuf.dptr );
return;
}
static void delete_tdb(char *keyname, size_t keylen)
{
TDB_DATA key;
if ((keyname == NULL) || (keylen == 0)) {
terror("need key");
return;
}
key.dptr = (unsigned char *)keyname;
key.dsize = keylen;
if (tdb_delete(tdb, key) != 0) {
terror("delete failed");
}
}
static void move_rec(char *keyname, size_t keylen, char* tdbname)
{
TDB_DATA key, dbuf;
struct tdb_context *dst_tdb;
if ((keyname == NULL) || (keylen == 0)) {
terror("need key");
return;
}
if ( !tdbname ) {
terror("need destination tdb name");
return;
}
key.dptr = (unsigned char *)keyname;
key.dsize = keylen;
dbuf = tdb_fetch(tdb, key);
if (!dbuf.dptr) {
terror("fetch failed");
return;
}
print_rec(tdb, key, dbuf, NULL);
dst_tdb = tdb_open(tdbname, 0, O_RDWR, 0600, NULL);
if ( !dst_tdb ) {
terror("unable to open destination tdb");
return;
}
if ( tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) == -1 ) {
terror("failed to move record");
}
else
printf("record moved\n");
tdb_close( dst_tdb );
return;
}
static int print_rec(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
{
printf("\nkey %d bytes\n", (int)key.dsize);
print_asc((const char *)key.dptr, key.dsize);
printf("\ndata %d bytes\n", (int)dbuf.dsize);
print_data((const char *)dbuf.dptr, dbuf.dsize);
return 0;
}
static int print_key(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
{
printf("key %d bytes: ", (int)key.dsize);
print_asc((const char *)key.dptr, key.dsize);
printf("\n");
return 0;
}
static int print_hexkey(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
{
printf("key %d bytes\n", (int)key.dsize);
print_data((const char *)key.dptr, key.dsize);
printf("\n");
return 0;
}
static int total_bytes;
static int traverse_fn(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
{
total_bytes += dbuf.dsize;
return 0;
}
static void info_tdb(void)
{
char *summary = tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS);
if (!summary) {
printf("Error = %s\n", tdb_errorstr(tdb));
} else {
printf("%s", summary);
free(summary);
}
}
static void speed_tdb(const char *tlimit)
{
unsigned timelimit = tlimit?atoi(tlimit):0;
double t;
int ops;
if (timelimit == 0) timelimit = 5;
ops = 0;
printf("Testing store speed for %u seconds\n", timelimit);
_start_timer();
do {
long int r = random();
TDB_DATA key, dbuf;
key.dptr = (unsigned char *)"store test";
key.dsize = strlen((char *)key.dptr);
dbuf.dptr = (unsigned char *)&r;
dbuf.dsize = sizeof(r);
tdb_store(tdb, key, dbuf, TDB_REPLACE);
t = _end_timer();
ops++;
} while (t < timelimit);
printf("%10.3f ops/sec\n", ops/t);
ops = 0;
printf("Testing fetch speed for %u seconds\n", timelimit);
_start_timer();
do {
long int r = random();
TDB_DATA key, dbuf;
key.dptr = (unsigned char *)"store test";
key.dsize = strlen((char *)key.dptr);
dbuf.dptr = (unsigned char *)&r;
dbuf.dsize = sizeof(r);
tdb_fetch(tdb, key);
t = _end_timer();
ops++;
} while (t < timelimit);
printf("%10.3f ops/sec\n", ops/t);
#if 0 /* FIXME */
ops = 0;
printf("Testing transaction speed for %u seconds\n", timelimit);
_start_timer();
do {
long int r = random();
TDB_DATA key, dbuf;
key.dptr = (unsigned char *)"transaction test";
key.dsize = strlen((char *)key.dptr);
dbuf.dptr = (unsigned char *)&r;
dbuf.dsize = sizeof(r);
tdb_transaction_start(tdb);
tdb_store(tdb, key, dbuf, TDB_REPLACE);
tdb_transaction_commit(tdb);
t = _end_timer();
ops++;
} while (t < timelimit);
printf("%10.3f ops/sec\n", ops/t);
#endif
ops = 0;
printf("Testing traverse speed for %u seconds\n", timelimit);
_start_timer();
do {
tdb_traverse(tdb, traverse_fn, NULL);
t = _end_timer();
ops++;
} while (t < timelimit);
printf("%10.3f ops/sec\n", ops/t);
}
static void toggle_mmap(void)
{
disable_mmap = !disable_mmap;
if (disable_mmap) {
printf("mmap is disabled\n");
} else {
printf("mmap is enabled\n");
}
}
static char *tdb_getline(const char *prompt)
{
static char thisline[1024];
char *p;
fputs(prompt, stdout);
thisline[0] = 0;
p = fgets(thisline, sizeof(thisline)-1, stdin);
if (p) p = strchr(p, '\n');
if (p) *p = 0;
return p?thisline:NULL;
}
static int do_delete_fn(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf,
void *state)
{
return tdb_delete(the_tdb, key);
}
static void first_record(struct tdb_context *the_tdb, TDB_DATA *pkey)
{
TDB_DATA dbuf;
*pkey = tdb_firstkey(the_tdb);
dbuf = tdb_fetch(the_tdb, *pkey);
if (!dbuf.dptr) terror("fetch failed");
else {
print_rec(the_tdb, *pkey, dbuf, NULL);
}
}
static void next_record(struct tdb_context *the_tdb, TDB_DATA *pkey)
{
TDB_DATA dbuf;
*pkey = tdb_nextkey(the_tdb, *pkey);
dbuf = tdb_fetch(the_tdb, *pkey);
if (!dbuf.dptr)
terror("fetch failed");
else
print_rec(the_tdb, *pkey, dbuf, NULL);
}
static void check_db(struct tdb_context *the_tdb)
{
if (!the_tdb) {
printf("Error: No database opened!\n");
} else {
if (tdb_check(the_tdb, NULL, NULL) == -1)
printf("Integrity check for the opened database failed.\n");
else
printf("Database integrity is OK.\n");
}
}
static int do_command(void)
{
COMMAND_TABLE *ctp = cmd_table;
enum commands mycmd = CMD_HELP;
int cmd_len;
if (cmdname && strlen(cmdname) == 0) {
mycmd = CMD_NEXT;
} else {
while (ctp->name) {
cmd_len = strlen(ctp->name);
if (strncmp(ctp->name,cmdname,cmd_len) == 0) {
mycmd = ctp->cmd;
break;
}
ctp++;
}
}
switch (mycmd) {
case CMD_CREATE_TDB:
bIterate = 0;
create_tdb(arg1);
return 0;
case CMD_OPEN_TDB:
bIterate = 0;
open_tdb(arg1, NULL);
return 0;
case CMD_OPENJH_TDB:
bIterate = 0;
open_tdb(arg1, jenkins_hash);
return 0;
case CMD_SYSTEM:
/* Shell command */
if (system(arg1) == -1) {
terror("system() call failed\n");
}
return 0;
case CMD_QUIT:
return 1;
default:
/* all the rest require a open database */
if (!tdb) {
bIterate = 0;
terror("database not open");
help();
return 0;
}
switch (mycmd) {
#if 0
case CMD_TRANSACTION_START:
bIterate = 0;
tdb_transaction_start(tdb);
return 0;
case CMD_TRANSACTION_COMMIT:
bIterate = 0;
tdb_transaction_commit(tdb);
return 0;
case CMD_TRANSACTION_CANCEL:
bIterate = 0;
tdb_transaction_cancel(tdb);
return 0;
#endif
case CMD_ERASE:
bIterate = 0;
tdb_traverse(tdb, do_delete_fn, NULL);
return 0;
case CMD_DUMP:
bIterate = 0;
tdb_traverse(tdb, print_rec, NULL);
return 0;
case CMD_INSERT:
bIterate = 0;
insert_tdb(arg1, arg1len,arg2,arg2len);
return 0;
case CMD_MOVE:
bIterate = 0;
move_rec(arg1,arg1len,arg2);
return 0;
case CMD_STORE:
bIterate = 0;
store_tdb(arg1,arg1len,arg2,arg2len);
return 0;
case CMD_SHOW:
bIterate = 0;
show_tdb(arg1, arg1len);
return 0;
case CMD_KEYS:
tdb_traverse(tdb, print_key, NULL);
return 0;
case CMD_HEXKEYS:
tdb_traverse(tdb, print_hexkey, NULL);
return 0;
case CMD_DELETE:
bIterate = 0;
delete_tdb(arg1,arg1len);
return 0;
#if 0
case CMD_LIST_HASH_FREE:
tdb_dump_all(tdb);
return 0;
case CMD_LIST_FREE:
tdb_printfreelist(tdb);
return 0;
#endif
case CMD_INFO:
info_tdb();
return 0;
case CMD_SPEED:
speed_tdb(arg1);
return 0;
case CMD_MMAP:
toggle_mmap();
return 0;
case CMD_FIRST:
bIterate = 1;
first_record(tdb, &iterate_kbuf);
return 0;
case CMD_NEXT:
if (bIterate)
next_record(tdb, &iterate_kbuf);
return 0;
case CMD_CHECK:
check_db(tdb);
return 0;
case CMD_HELP:
help();
return 0;
case CMD_CREATE_TDB:
case CMD_OPEN_TDB:
case CMD_OPENJH_TDB:
case CMD_SYSTEM:
case CMD_QUIT:
/*
* unhandled commands. cases included here to avoid compiler
* warnings.
*/
return 0;
}
}
return 0;
}
static char *convert_string(char *instring, size_t *sizep)
{
size_t length = 0;
char *outp, *inp;
char temp[3];
outp = inp = instring;
while (*inp) {
if (*inp == '\\') {
inp++;
if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
temp[0] = *inp++;
temp[1] = '\0';
if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
temp[1] = *inp++;
temp[2] = '\0';
}
*outp++ = (char)strtol((const char *)temp,NULL,16);
} else {
*outp++ = *inp++;
}
} else {
*outp++ = *inp++;
}
length++;
}
*sizep = length;
return instring;
}
int main(int argc, char *argv[])
{
cmdname = "";
arg1 = NULL;
arg1len = 0;
arg2 = NULL;
arg2len = 0;
if (argv[1]) {
cmdname = "open";
arg1 = argv[1];
do_command();
cmdname = "";
arg1 = NULL;
}
switch (argc) {
case 1:
case 2:
/* Interactive mode */
while ((cmdname = tdb_getline("tdb> "))) {
arg2 = arg1 = NULL;
if ((arg1 = strchr((const char *)cmdname,' ')) != NULL) {
arg1++;
arg2 = arg1;
while (*arg2) {
if (*arg2 == ' ') {
*arg2++ = '\0';
break;
}
if ((*arg2++ == '\\') && (*arg2 == ' ')) {
arg2++;
}
}
}
if (arg1) arg1 = convert_string(arg1,&arg1len);
if (arg2) arg2 = convert_string(arg2,&arg2len);
if (do_command()) break;
}
break;
case 5:
arg2 = convert_string(argv[4],&arg2len);
case 4:
arg1 = convert_string(argv[3],&arg1len);
case 3:
cmdname = argv[2];
default:
do_command();
break;
}
if (tdb) tdb_close(tdb);
return 0;
}
/* this tests tdb by doing lots of ops from several simultaneous
writers - that stresses the locking code.
*/
#include <ccan/tdb2/tdb2.h>
#include <stdlib.h>
#include <err.h>
#include <getopt.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
#include <sys/wait.h>
//#define REOPEN_PROB 30
#define DELETE_PROB 8
#define STORE_PROB 4
#define APPEND_PROB 6
//#define TRANSACTION_PROB 10
//#define TRANSACTION_PREPARE_PROB 2
#define LOCKSTORE_PROB 5
#define TRAVERSE_PROB 20
#define TRAVERSE_READ_PROB 20
#define TRAVERSE_MOD_PROB 100
#define TRAVERSE_ABORT_PROB 500
#define CULL_PROB 100
#define KEYLEN 3
#define DATALEN 100
static struct tdb_context *db;
static int in_transaction;
static int in_traverse;
static int error_count;
#if TRANSACTION_PROB
static int always_transaction = 0;
#endif
static int loopnum;
static int count_pipe;
static union tdb_attribute log_attr;
#ifdef PRINTF_ATTRIBUTE
static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, void *private, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
#endif
static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, void *private, const char *format, ...)
{
va_list ap;
if (level != TDB_DEBUG_TRACE)
error_count++;
va_start(ap, format);
vfprintf(stdout, format, ap);
va_end(ap);
fflush(stdout);
#if 0
if (level != TDB_DEBUG_TRACE) {
char *ptr;
signal(SIGUSR1, SIG_IGN);
asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid());
system(ptr);
free(ptr);
}
#endif
}
#include "../private.h"
static void segv_handler(int signal, siginfo_t *info, void *p)
{
char string[100];
sprintf(string, "%u: death at %p (map_ptr %p, map_size %llu)\n",
getpid(), info->si_addr, db->map_ptr, db->map_size);
write(2, string, strlen(string));
sleep(60);
_exit(11);
}
static void fatal(const char *why)
{
perror(why);
error_count++;
}
static char *randbuf(int len)
{
char *buf;
int i;
buf = (char *)malloc(len+1);
for (i=0;i<len;i++) {
buf[i] = 'a' + (rand() % 26);
}
buf[i] = 0;
return buf;
}
static void addrec_db(void);
static int modify_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
void *state)
{
#if CULL_PROB
if (random() % CULL_PROB == 0) {
tdb_delete(tdb, key);
}
#endif
#if TRAVERSE_MOD_PROB
if (random() % TRAVERSE_MOD_PROB == 0) {
addrec_db();
}
#endif
#if TRAVERSE_ABORT_PROB
if (random() % TRAVERSE_ABORT_PROB == 0)
return 1;
#endif
return 0;
}
static void addrec_db(void)
{
int klen, dlen;
char *k, *d;
TDB_DATA key, data;
klen = 1 + (rand() % KEYLEN);
dlen = 1 + (rand() % DATALEN);
k = randbuf(klen);
d = randbuf(dlen);
key.dptr = (unsigned char *)k;
key.dsize = klen+1;
data.dptr = (unsigned char *)d;
data.dsize = dlen+1;
#if REOPEN_PROB
if (in_traverse == 0 && in_transaction == 0 && random() % REOPEN_PROB == 0) {
tdb_reopen_all(0);
goto next;
}
#endif
#if TRANSACTION_PROB
if (in_traverse == 0 && in_transaction == 0 && (always_transaction || random() % TRANSACTION_PROB == 0)) {
if (tdb_transaction_start(db) != 0) {
fatal("tdb_transaction_start failed");
}
in_transaction++;
goto next;
}
if (in_traverse == 0 && in_transaction && random() % TRANSACTION_PROB == 0) {
if (random() % TRANSACTION_PREPARE_PROB == 0) {
if (tdb_transaction_prepare_commit(db) != 0) {
fatal("tdb_transaction_prepare_commit failed");
}
}
if (tdb_transaction_commit(db) != 0) {
fatal("tdb_transaction_commit failed");
}
in_transaction--;
goto next;
}
if (in_traverse == 0 && in_transaction && random() % TRANSACTION_PROB == 0) {
if (tdb_transaction_cancel(db) != 0) {
fatal("tdb_transaction_cancel failed");
}
in_transaction--;
goto next;
}
#endif
#if DELETE_PROB
if (random() % DELETE_PROB == 0) {
tdb_delete(db, key);
goto next;
}
#endif
#if STORE_PROB
if (random() % STORE_PROB == 0) {
if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
fatal("tdb_store failed");
}
goto next;
}
#endif
#if APPEND_PROB
if (random() % APPEND_PROB == 0) {
if (tdb_append(db, key, data) != 0) {
fatal("tdb_append failed");
}
goto next;
}
#endif
#if LOCKSTORE_PROB
if (random() % LOCKSTORE_PROB == 0) {
tdb_chainlock(db, key);
data = tdb_fetch(db, key);
if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
fatal("tdb_store failed");
}
if (data.dptr) free(data.dptr);
tdb_chainunlock(db, key);
goto next;
}
#endif
#if TRAVERSE_PROB
/* FIXME: recursive traverses break transactions? */
if (in_traverse == 0 && random() % TRAVERSE_PROB == 0) {
in_traverse++;
tdb_traverse(db, modify_traverse, NULL);
in_traverse--;
goto next;
}
#endif
#if TRAVERSE_READ_PROB
if (in_traverse == 0 && random() % TRAVERSE_READ_PROB == 0) {
in_traverse++;
tdb_traverse_read(db, NULL, NULL);
in_traverse--;
goto next;
}
#endif
data = tdb_fetch(db, key);
if (data.dptr) free(data.dptr);
next:
free(k);
free(d);
}
static int traverse_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
void *state)
{
tdb_delete(tdb, key);
return 0;
}
static void usage(void)
{
printf("Usage: tdbtorture"
#if TRANSACTION_PROB
" [-t]"
#endif
" [-k] [-n NUM_PROCS] [-l NUM_LOOPS] [-s SEED]\n");
exit(0);
}
static void send_count_and_suicide(int sig)
{
/* This ensures our successor can continue where we left off. */
write(count_pipe, &loopnum, sizeof(loopnum));
/* This gives a unique signature. */
kill(getpid(), SIGUSR2);
}
static int run_child(int i, int seed, unsigned num_loops, unsigned start)
{
struct sigaction act = { .sa_sigaction = segv_handler,
.sa_flags = SA_SIGINFO };
sigaction(11, &act, NULL);
db = tdb_open("torture.tdb", TDB_DEFAULT, O_RDWR | O_CREAT, 0600,
&log_attr);
if (!db) {
fatal("db open failed");
}
#if 0
if (i == 0) {
printf("pid %i\n", getpid());
sleep(9);
} else
sleep(10);
#endif
srand(seed + i);
srandom(seed + i);
/* Set global, then we're ready to handle being killed. */
loopnum = start;
signal(SIGUSR1, send_count_and_suicide);
for (;loopnum<num_loops && error_count == 0;loopnum++) {
addrec_db();
}
if (error_count == 0) {
tdb_traverse_read(db, NULL, NULL);
#if TRANSACTION_PROB
if (always_transaction) {
while (in_transaction) {
tdb_transaction_cancel(db);
in_transaction--;
}
if (tdb_transaction_start(db) != 0)
fatal("tdb_transaction_start failed");
}
#endif
tdb_traverse(db, traverse_fn, NULL);
tdb_traverse(db, traverse_fn, NULL);
#if TRANSACTION_PROB
if (always_transaction) {
if (tdb_transaction_commit(db) != 0)
fatal("tdb_transaction_commit failed");
}
#endif
}
tdb_close(db);
return (error_count < 100 ? error_count : 100);
}
int main(int argc, char * const *argv)
{
int i, seed = -1;
int num_loops = 5000;
int num_procs = 3;
int c, pfds[2];
extern char *optarg;
pid_t *pids;
int kill_random = 0;
int *done;
log_attr.log.log_fn = tdb_log;
while ((c = getopt(argc, argv, "n:l:s:thk")) != -1) {
switch (c) {
case 'n':
num_procs = strtol(optarg, NULL, 0);
break;
case 'l':
num_loops = strtol(optarg, NULL, 0);
break;
case 's':
seed = strtol(optarg, NULL, 0);
break;
case 't':
#if TRANSACTION_PROB
always_transaction = 1;
#else
fprintf(stderr, "Transactions not supported\n");
usage();
#endif
break;
case 'k':
kill_random = 1;
break;
default:
usage();
}
}
unlink("torture.tdb");
if (seed == -1) {
seed = (getpid() + time(NULL)) & 0x7FFFFFFF;
}
if (num_procs == 1 && !kill_random) {
/* Don't fork for this case, makes debugging easier. */
error_count = run_child(0, seed, num_loops, 0);
goto done;
}
pids = (pid_t *)calloc(sizeof(pid_t), num_procs);
done = (int *)calloc(sizeof(int), num_procs);
if (pipe(pfds) != 0) {
perror("Creating pipe");
exit(1);
}
count_pipe = pfds[1];
for (i=0;i<num_procs;i++) {
if ((pids[i]=fork()) == 0) {
close(pfds[0]);
if (i == 0) {
printf("testing with %d processes, %d loops, seed=%d%s\n",
num_procs, num_loops, seed,
#if TRANSACTION_PROB
always_transaction ? " (all within transactions)" : ""
#else
""
#endif
);
}
exit(run_child(i, seed, num_loops, 0));
}
}
while (num_procs) {
int status, j;
pid_t pid;
if (error_count != 0) {
/* try and stop the test on any failure */
for (j=0;j<num_procs;j++) {
if (pids[j] != 0) {
kill(pids[j], SIGTERM);
}
}
}
pid = waitpid(-1, &status, kill_random ? WNOHANG : 0);
if (pid == 0) {
struct timespec ts;
/* Sleep for 1/10 second. */
ts.tv_sec = 0;
ts.tv_nsec = 100000000;
nanosleep(&ts, NULL);
/* Kill someone. */
kill(pids[random() % num_procs], SIGUSR1);
continue;
}
if (pid == -1) {
perror("failed to wait for child\n");
exit(1);
}
for (j=0;j<num_procs;j++) {
if (pids[j] == pid) break;
}
if (j == num_procs) {
printf("unknown child %d exited!?\n", (int)pid);
exit(1);
}
if (WIFSIGNALED(status)) {
if (WTERMSIG(status) == SIGUSR2
|| WTERMSIG(status) == SIGUSR1) {
/* SIGUSR2 means they wrote to pipe. */
if (WTERMSIG(status) == SIGUSR2) {
read(pfds[0], &done[j],
sizeof(done[j]));
}
pids[j] = fork();
if (pids[j] == 0)
exit(run_child(j, seed, num_loops,
done[j]));
printf("Restarting child %i for %u-%u\n",
j, done[j], num_loops);
continue;
}
printf("child %d exited with signal %d\n",
(int)pid, WTERMSIG(status));
error_count++;
} else {
if (WEXITSTATUS(status) != 0) {
printf("child %d exited with status %d\n",
(int)pid, WEXITSTATUS(status));
error_count++;
}
}
memmove(&pids[j], &pids[j+1],
(num_procs - j - 1)*sizeof(pids[0]));
num_procs--;
}
free(pids);
done:
if (error_count == 0) {
db = tdb_open("torture.tdb", TDB_DEFAULT, O_RDWR | O_CREAT,
0600, &log_attr);
if (!db) {
fatal("db open failed");
}
if (tdb_check(db, NULL, NULL) == -1) {
printf("db check failed");
exit(1);
}
tdb_close(db);
printf("OK\n");
}
return error_count;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment