1999-12-24 08:46:02 +00:00
/* this tests tdb by doing lots of ops from several simultaneous
2006-10-20 09:55:47 +00:00
writers - that stresses the locking code .
*/
1999-12-24 08:46:02 +00:00
2006-10-20 09:55:47 +00:00
# include "replace.h"
# include "system/time.h"
# include "system/wait.h"
# include "system/filesys.h"
2007-11-06 23:13:11 -08:00
# include "tdb.h"
2006-10-20 09:55:47 +00:00
# ifdef HAVE_GETOPT_H
# include <getopt.h>
# endif
1999-12-24 08:46:02 +00:00
2001-09-24 04:56:56 +00:00
# define REOPEN_PROB 30
# define DELETE_PROB 8
# define STORE_PROB 4
2003-01-11 00:07:44 +00:00
# define APPEND_PROB 6
2006-10-20 09:55:47 +00:00
# define TRANSACTION_PROB 10
2009-03-31 13:59:03 +11:00
# define TRANSACTION_PREPARE_PROB 2
2006-10-20 09:55:47 +00:00
# define LOCKSTORE_PROB 5
2001-09-24 04:56:56 +00:00
# define TRAVERSE_PROB 20
2006-10-20 09:55:47 +00:00
# define TRAVERSE_READ_PROB 20
2001-09-24 04:56:56 +00:00
# define CULL_PROB 100
1999-12-24 08:46:02 +00:00
# define KEYLEN 3
# define DATALEN 100
2006-10-20 09:55:47 +00:00
static struct tdb_context * db ;
static int in_transaction ;
static int error_count ;
2009-10-22 00:10:54 +10:30
static int always_transaction = 0 ;
2010-02-24 10:53:05 +10:30
static int hash_size = 2 ;
static int loopnum ;
static int count_pipe ;
static struct tdb_logging_context log_ctx ;
1999-12-24 08:46:02 +00:00
2006-10-20 09:55:47 +00:00
# ifdef PRINTF_ATTRIBUTE
static void tdb_log ( struct tdb_context * tdb , enum tdb_debug_level level , const char * format , . . . ) PRINTF_ATTRIBUTE ( 3 , 4 ) ;
# endif
static void tdb_log ( struct tdb_context * tdb , enum tdb_debug_level level , const char * format , . . . )
2001-05-28 13:29:06 +00:00
{
va_list ap ;
2009-10-20 10:59:40 +11:00
/* trace level messages do not indicate an error */
if ( level ! = TDB_DEBUG_TRACE ) {
error_count + + ;
}
2006-10-20 09:55:47 +00:00
2001-05-28 13:29:06 +00:00
va_start ( ap , format ) ;
vfprintf ( stdout , format , ap ) ;
va_end ( ap ) ;
2001-09-06 05:59:32 +00:00
fflush ( stdout ) ;
2001-05-30 05:40:52 +00:00
#if 0
2010-02-24 10:53:05 +10:30
if ( level ! = TDB_DEBUG_TRACE ) {
2001-05-30 05:40:52 +00:00
char * ptr ;
2010-02-24 10:53:05 +10:30
signal ( SIGUSR1 , SIG_IGN ) ;
2001-05-30 05:40:52 +00:00
asprintf ( & ptr , " xterm -e gdb /proc/%d/exe %d " , getpid ( ) , getpid ( ) ) ;
system ( ptr ) ;
free ( ptr ) ;
}
# endif
2001-05-28 13:29:06 +00:00
}
2006-10-20 09:55:47 +00:00
static void fatal ( const char * why )
1999-12-24 08:46:02 +00:00
{
perror ( why ) ;
2006-10-20 09:55:47 +00:00
error_count + + ;
1999-12-24 08:46:02 +00:00
}
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 ;
}
2006-10-20 09:55:47 +00:00
static int cull_traverse ( struct tdb_context * tdb , TDB_DATA key , TDB_DATA dbuf ,
2001-05-30 05:40:52 +00:00
void * state )
{
2006-10-20 09:55:47 +00:00
# if CULL_PROB
2001-05-30 05:40:52 +00:00
if ( random ( ) % CULL_PROB = = 0 ) {
2001-12-11 08:31:58 +00:00
tdb_delete ( tdb , key ) ;
2001-05-30 05:40:52 +00:00
}
2006-10-20 09:55:47 +00:00
# endif
2001-05-30 05:40:52 +00:00
return 0 ;
}
1999-12-24 08:46:02 +00:00
static void addrec_db ( void )
{
2006-10-20 09:55:47 +00:00
int klen , dlen ;
char * k , * d ;
TDB_DATA key , data ;
1999-12-24 08:46:02 +00:00
klen = 1 + ( rand ( ) % KEYLEN ) ;
dlen = 1 + ( rand ( ) % DATALEN ) ;
k = randbuf ( klen ) ;
d = randbuf ( dlen ) ;
2006-10-20 09:55:47 +00:00
key . dptr = ( unsigned char * ) k ;
1999-12-24 08:46:02 +00:00
key . dsize = klen + 1 ;
2006-10-20 09:55:47 +00:00
data . dptr = ( unsigned char * ) d ;
1999-12-24 08:46:02 +00:00
data . dsize = dlen + 1 ;
2009-10-22 00:10:54 +10:30
# if REOPEN_PROB
if ( in_transaction = = 0 & & random ( ) % REOPEN_PROB = = 0 ) {
tdb_reopen_all ( 0 ) ;
goto next ;
}
# endif
2006-10-20 09:55:47 +00:00
# if TRANSACTION_PROB
2009-10-22 00:10:54 +10:30
if ( in_transaction = = 0 & &
( always_transaction | | random ( ) % TRANSACTION_PROB = = 0 ) ) {
2006-10-20 09:55:47 +00:00
if ( tdb_transaction_start ( db ) ! = 0 ) {
fatal ( " tdb_transaction_start failed " ) ;
}
in_transaction + + ;
goto next ;
}
if ( in_transaction & & random ( ) % TRANSACTION_PROB = = 0 ) {
2009-03-31 13:59:03 +11:00
if ( random ( ) % TRANSACTION_PREPARE_PROB = = 0 ) {
if ( tdb_transaction_prepare_commit ( db ) ! = 0 ) {
fatal ( " tdb_transaction_prepare_commit failed " ) ;
}
}
2006-10-20 09:55:47 +00:00
if ( tdb_transaction_commit ( db ) ! = 0 ) {
fatal ( " tdb_transaction_commit failed " ) ;
}
in_transaction - - ;
goto next ;
}
if ( in_transaction & & random ( ) % TRANSACTION_PROB = = 0 ) {
if ( tdb_transaction_cancel ( db ) ! = 0 ) {
fatal ( " tdb_transaction_cancel failed " ) ;
}
in_transaction - - ;
goto next ;
}
# endif
2001-09-24 04:56:56 +00:00
# if DELETE_PROB
2001-05-30 05:40:52 +00:00
if ( random ( ) % DELETE_PROB = = 0 ) {
1999-12-24 08:46:02 +00:00
tdb_delete ( db , key ) ;
2001-09-27 01:57:02 +00:00
goto next ;
2001-09-24 04:56:56 +00:00
}
# endif
# if STORE_PROB
if ( random ( ) % STORE_PROB = = 0 ) {
1999-12-24 08:46:02 +00:00
if ( tdb_store ( db , key , data , TDB_REPLACE ) ! = 0 ) {
fatal ( " tdb_store failed " ) ;
}
2001-09-27 01:57:02 +00:00
goto next ;
2001-09-24 04:56:56 +00:00
}
# endif
2003-01-11 00:07:44 +00:00
# if APPEND_PROB
if ( random ( ) % APPEND_PROB = = 0 ) {
if ( tdb_append ( db , key , data ) ! = 0 ) {
fatal ( " tdb_append failed " ) ;
}
goto next ;
}
# endif
2001-09-24 04:56:56 +00:00
# if LOCKSTORE_PROB
if ( random ( ) % LOCKSTORE_PROB = = 0 ) {
2006-10-20 09:55:47 +00:00
tdb_chainlock ( db , key ) ;
1999-12-24 08:46:02 +00:00
data = tdb_fetch ( db , key ) ;
2001-09-24 04:56:56 +00:00
if ( tdb_store ( db , key , data , TDB_REPLACE ) ! = 0 ) {
fatal ( " tdb_store failed " ) ;
}
1999-12-24 08:46:02 +00:00
if ( data . dptr ) free ( data . dptr ) ;
2006-10-20 09:55:47 +00:00
tdb_chainunlock ( db , key ) ;
2001-09-27 01:57:02 +00:00
goto next ;
2001-09-24 04:56:56 +00:00
}
# endif
# if TRAVERSE_PROB
if ( random ( ) % TRAVERSE_PROB = = 0 ) {
tdb_traverse ( db , cull_traverse , NULL ) ;
2001-09-27 01:57:02 +00:00
goto next ;
1999-12-24 08:46:02 +00:00
}
2001-09-24 04:56:56 +00:00
# endif
2006-10-20 09:55:47 +00:00
# if TRAVERSE_READ_PROB
if ( random ( ) % TRAVERSE_READ_PROB = = 0 ) {
tdb_traverse_read ( db , NULL , NULL ) ;
goto next ;
}
# endif
2001-09-24 04:56:56 +00:00
data = tdb_fetch ( db , key ) ;
if ( data . dptr ) free ( data . dptr ) ;
1999-12-24 08:46:02 +00:00
2001-09-27 01:57:02 +00:00
next :
1999-12-24 08:46:02 +00:00
free ( k ) ;
free ( d ) ;
}
2006-10-20 09:55:47 +00:00
static int traverse_fn ( struct tdb_context * tdb , TDB_DATA key , TDB_DATA dbuf ,
2000-02-28 00:37:13 +00:00
void * state )
1999-12-24 08:46:02 +00:00
{
2001-12-11 08:31:58 +00:00
tdb_delete ( tdb , key ) ;
1999-12-24 08:46:02 +00:00
return 0 ;
}
2006-10-20 09:55:47 +00:00
static void usage ( void )
{
2010-02-24 10:53:05 +10:30
printf ( " Usage: tdbtorture [-t] [-k] [-n NUM_PROCS] [-l NUM_LOOPS] [-s SEED] [-H HASH_SIZE] \n " ) ;
2006-10-20 09:55:47 +00:00
exit ( 0 ) ;
}
1999-12-24 08:46:02 +00:00
2010-02-24 10:53:05 +10:30
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 ) ;
}
2010-12-24 11:54:51 +01:00
static int run_child ( const char * filename , int i , int seed , unsigned num_loops , unsigned start )
2010-02-24 10:53:05 +10:30
{
2010-12-24 11:54:51 +01:00
db = tdb_open_ex ( filename , hash_size , TDB_DEFAULT ,
2010-02-24 10:53:05 +10:30
O_RDWR | O_CREAT , 0600 , & log_ctx , NULL ) ;
if ( ! db ) {
fatal ( " db open failed " ) ;
}
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 ( always_transaction ) {
while ( in_transaction ) {
tdb_transaction_cancel ( db ) ;
in_transaction - - ;
}
if ( tdb_transaction_start ( db ) ! = 0 )
fatal ( " tdb_transaction_start failed " ) ;
}
tdb_traverse ( db , traverse_fn , NULL ) ;
tdb_traverse ( db , traverse_fn , NULL ) ;
if ( always_transaction ) {
if ( tdb_transaction_commit ( db ) ! = 0 )
fatal ( " tdb_transaction_commit failed " ) ;
}
}
tdb_close ( db ) ;
return ( error_count < 100 ? error_count : 100 ) ;
}
2010-12-24 11:54:51 +01:00
static char * test_path ( const char * filename )
{
const char * prefix = getenv ( " TEST_DATA_PREFIX " ) ;
if ( prefix ) {
char * path = NULL ;
int ret ;
ret = asprintf ( & path , " %s/%s " , prefix , filename ) ;
if ( ret = = - 1 ) {
return NULL ;
}
return path ;
}
return strdup ( filename ) ;
}
2010-02-24 10:53:05 +10:30
int main ( int argc , char * const * argv )
1999-12-24 08:46:02 +00:00
{
2006-10-20 09:55:47 +00:00
int i , seed = - 1 ;
int num_loops = 5000 ;
2010-02-24 10:53:05 +10:30
int num_procs = 3 ;
int c , pfds [ 2 ] ;
2006-10-20 09:55:47 +00:00
extern char * optarg ;
pid_t * pids ;
2010-02-24 10:53:05 +10:30
int kill_random = 0 ;
int * done ;
2010-12-24 11:54:51 +01:00
char * test_tdb ;
2006-10-20 09:55:47 +00:00
log_ctx . log_fn = tdb_log ;
2010-02-24 10:53:05 +10:30
while ( ( c = getopt ( argc , argv , " n:l:s:H:thk " ) ) ! = - 1 ) {
2006-10-20 09:55:47 +00:00
switch ( c ) {
case ' n ' :
num_procs = strtol ( optarg , NULL , 0 ) ;
break ;
case ' l ' :
num_loops = strtol ( optarg , NULL , 0 ) ;
break ;
case ' H ' :
hash_size = strtol ( optarg , NULL , 0 ) ;
break ;
case ' s ' :
seed = strtol ( optarg , NULL , 0 ) ;
break ;
2009-10-22 00:10:54 +10:30
case ' t ' :
always_transaction = 1 ;
break ;
2010-02-24 10:53:05 +10:30
case ' k ' :
kill_random = 1 ;
break ;
2006-10-20 09:55:47 +00:00
default :
usage ( ) ;
}
}
2010-12-24 11:54:51 +01:00
test_tdb = test_path ( " torture.tdb " ) ;
unlink ( test_tdb ) ;
2006-10-20 09:55:47 +00:00
if ( seed = = - 1 ) {
seed = ( getpid ( ) + time ( NULL ) ) & 0x7FFFFFFF ;
}
2005-08-29 13:45:13 +00:00
2010-02-24 10:53:05 +10:30
if ( num_procs = = 1 & & ! kill_random ) {
/* Don't fork for this case, makes debugging easier. */
2010-12-24 11:54:51 +01:00
error_count = run_child ( test_tdb , 0 , seed , num_loops , 0 ) ;
2010-02-24 10:53:05 +10:30
goto done ;
2006-10-20 09:55:47 +00:00
}
2005-08-29 13:45:13 +00:00
2010-02-24 10:53:05 +10:30
pids = ( pid_t * ) calloc ( sizeof ( pid_t ) , num_procs ) ;
done = ( int * ) calloc ( sizeof ( int ) , num_procs ) ;
2005-08-29 13:45:13 +00:00
2010-02-24 10:53:05 +10:30
if ( pipe ( pfds ) ! = 0 ) {
perror ( " Creating pipe " ) ;
exit ( 1 ) ;
2006-10-20 09:55:47 +00:00
}
2010-02-24 10:53:05 +10:30
count_pipe = pfds [ 1 ] ;
for ( i = 0 ; i < num_procs ; i + + ) {
if ( ( pids [ i ] = fork ( ) ) = = 0 ) {
close ( pfds [ 0 ] ) ;
if ( i = = 0 ) {
2010-04-11 01:39:06 +02:00
printf ( " Testing with %d processes, %d loops, %d hash_size, seed=%d%s \n " ,
2010-02-24 10:53:05 +10:30
num_procs , num_loops , hash_size , seed , always_transaction ? " (all within transactions) " : " " ) ;
2009-10-22 00:10:54 +10:30
}
2010-12-24 11:54:51 +01:00
exit ( run_child ( test_tdb , i , seed , num_loops , 0 ) ) ;
2009-10-22 00:10:54 +10:30
}
2001-05-30 05:40:52 +00:00
}
2010-02-24 10:53:05 +10:30
while ( num_procs ) {
2006-10-20 09:55:47 +00:00
int status , j ;
pid_t pid ;
2010-02-24 10:53:05 +10:30
2006-10-20 09:55:47 +00:00
if ( error_count ! = 0 ) {
/* try and stop the test on any failure */
2010-02-24 10:53:05 +10:30
for ( j = 0 ; j < num_procs ; j + + ) {
2006-10-20 09:55:47 +00:00
if ( pids [ j ] ! = 0 ) {
kill ( pids [ j ] , SIGTERM ) ;
}
}
}
2010-02-24 10:53:05 +10:30
pid = waitpid ( - 1 , & status , kill_random ? WNOHANG : 0 ) ;
if ( pid = = 0 ) {
2010-03-14 20:07:58 +01:00
struct timeval tv ;
2010-02-24 10:53:05 +10:30
/* Sleep for 1/10 second. */
2010-03-14 20:07:58 +01:00
tv . tv_sec = 0 ;
tv . tv_usec = 100000 ;
select ( 0 , NULL , NULL , NULL , & tv ) ;
2010-02-24 10:53:05 +10:30
/* Kill someone. */
kill ( pids [ random ( ) % num_procs ] , SIGUSR1 ) ;
continue ;
}
2006-10-20 09:55:47 +00:00
if ( pid = = - 1 ) {
perror ( " failed to wait for child \n " ) ;
exit ( 1 ) ;
}
2010-02-24 10:53:05 +10:30
for ( j = 0 ; j < num_procs ; j + + ) {
2006-10-20 09:55:47 +00:00
if ( pids [ j ] = = pid ) break ;
}
if ( j = = num_procs ) {
printf ( " unknown child %d exited!? \n " , ( int ) pid ) ;
2005-08-29 13:45:13 +00:00
exit ( 1 ) ;
}
2010-02-24 10:53:05 +10:30
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 )
2010-12-24 11:54:51 +01:00
exit ( run_child ( test_tdb , j , seed ,
num_loops , done [ j ] ) ) ;
2010-02-24 10:53:05 +10:30
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 ) ) ;
2006-10-20 09:55:47 +00:00
error_count + + ;
2010-02-24 10:53:05 +10:30
} else {
if ( WEXITSTATUS ( status ) ! = 0 ) {
printf ( " child %d exited with status %d \n " ,
( int ) pid , WEXITSTATUS ( status ) ) ;
error_count + + ;
}
2005-08-29 13:45:13 +00:00
}
2010-02-24 10:53:05 +10:30
memmove ( & pids [ j ] , & pids [ j + 1 ] ,
( num_procs - j - 1 ) * sizeof ( pids [ 0 ] ) ) ;
num_procs - - ;
2005-08-29 13:45:13 +00:00
}
2006-10-20 09:55:47 +00:00
2009-06-07 00:06:04 +07:00
free ( pids ) ;
2010-02-24 10:53:05 +10:30
done :
2006-10-20 09:55:47 +00:00
if ( error_count = = 0 ) {
2010-12-24 11:54:51 +01:00
db = tdb_open_ex ( test_tdb , hash_size , TDB_DEFAULT ,
2010-02-24 10:53:05 +10:30
O_RDWR , 0 , & log_ctx , NULL ) ;
if ( ! db ) {
fatal ( " db open failed " ) ;
}
if ( tdb_check ( db , NULL , NULL ) = = - 1 ) {
printf ( " db check failed " ) ;
exit ( 1 ) ;
}
tdb_close ( db ) ;
2006-10-20 09:55:47 +00:00
printf ( " OK \n " ) ;
}
2010-12-24 11:54:51 +01:00
free ( test_tdb ) ;
2006-10-20 09:55:47 +00:00
return error_count ;
1999-12-24 08:46:02 +00:00
}