/* -*- mode: C; c-basic-offset: 4 -*- */ #ifndef __TEST_H #define __TEST_H #if defined(__cilkplusplus) || defined(__cplusplus) extern "C" { #endif #ident "Copyright (c) 2007 Tokutek Inc. All rights reserved." #include <toku_portability.h> #include <string.h> #include <stdlib.h> #include <toku_stdint.h> #include <stdio.h> #include <db.h> #include <limits.h> #include <errno.h> #include "toku_htonl.h" #include "toku_assert.h" #include <signal.h> #include <time.h> #if defined(USE_TDB) #include "ydb.h" //TDB uses DB_NOTFOUND for c_del and DB_CURRENT errors. #ifdef DB_KEYEMPTY #error #endif #define DB_KEYEMPTY DB_NOTFOUND #endif #ifndef DB_DELETE_ANY #define DB_DELETE_ANY 0 #endif int verbose=0; #define UU(x) x __attribute__((__unused__)) /* * Note that these evaluate the argument 'r' multiple times, so you cannot * do CKERR(function_call(args)). I've added CHK macros below that are * safer and allow this usage. */ #define CKERR(r) do { if (r!=0) fprintf(stderr, "%s:%d error %d %s\n", __FILE__, __LINE__, r, db_strerror(r)); assert(r==0); } while (0) #define CKERR2(r,r2) do { if (r!=r2) fprintf(stderr, "%s:%d error %d %s, expected %d\n", __FILE__, __LINE__, r, db_strerror(r), r2); assert(r==r2); } while (0) #define CKERR2s(r,r2,r3) do { if (r!=r2 && r!=r3) fprintf(stderr, "%s:%d error %d %s, expected %d or %d\n", __FILE__, __LINE__, r, db_strerror(r), r2,r3); assert(r==r2||r==r3); } while (0) /* * Helpers for defining pseudo-hygienic macros using a (gensym)-like * technique. */ #define _CONCAT(x, y) x ## y #define CONCAT(x, y) _CONCAT(x, y) #define GS(symbol) CONCAT(CONCAT(__gensym_, __LINE__), CONCAT(_, symbol)) /* * Check macros which use CKERR* underneath, but evaluate their arguments * only once. They return the result of `expr' so they can be used like a * transparent function call like `r = CHK(db->get(db, ...))'. */ #define CHK(expr) ({ \ int (GS(r)) = (expr); \ CKERR(GS(r)); \ (GS(r)); \ }) #define CHK2(expr, expected) ({ \ int (GS(r)) = (expr); \ int (GS(r2)) = (expected); \ CKERR2((GS(r)), (GS(r2))); \ (GS(r)); \ }) #define CHK2s(expr, expected1, expected2) ({ \ int (GS(r)) = (expr); \ int (GS(r2)) = (expected1); \ int (GS(r3)) = (expected2); \ CKERR2((GS(r)), (GS(r2)), (GS(r3))); \ (GS(r)); \ }) #define DEBUG_LINE do { \ fprintf(stderr, "%s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__); \ fflush(stderr); \ } while (0) // If the error code depends on BDB vs TDB use this #ifdef USE_TDB #define CKERR_depending(r,tdbexpect,bdbexpect) CKERR2(r,tdbexpect) #else #define CKERR_depending(r,tdbexpect,bdbexpect) CKERR2(r,bdbexpect) #endif static __attribute__((__unused__)) void parse_args (int argc, char * const argv[]) { const char *argv0=argv[0]; while (argc>1) { int resultcode=0; if (strcmp(argv[1], "-v")==0) { verbose++; } else if (strcmp(argv[1],"-q")==0) { verbose--; if (verbose<0) verbose=0; } else if (strcmp(argv[1], "-h")==0) { do_usage: fprintf(stderr, "Usage:\n%s [-v|-q] [-h]\n", argv0); exit(resultcode); } else { resultcode=1; goto do_usage; } argc--; argv++; } } static __attribute__((__unused__)) void print_engine_status(DB_ENV * UU(env)) { #ifdef USE_TDB if (verbose) { // verbose declared statically in this file uint64_t nrows; env->get_engine_status_num_rows(env, &nrows); int bufsiz = nrows * 128; // assume 128 characters per row char buff[bufsiz]; env->get_engine_status_text(env, buff, bufsiz); printf("Engine status:\n"); printf("%s", buff); } #endif } static __attribute__((__unused__)) uint64_t get_engine_status_val(DB_ENV * UU(env), char * keyname) { #ifdef USE_TDB uint64_t nrows; env->get_engine_status_num_rows(env, &nrows); TOKU_ENGINE_STATUS_ROW_S mystat[nrows]; fs_redzone_state redzone_state; uint64_t panic; uint32_t panic_string_len = 1024; char panic_string[panic_string_len]; int r = env->get_engine_status (env, mystat, nrows, &redzone_state, &panic, panic_string, panic_string_len); CKERR(r); int found = 0; uint64_t rval = 0; for (uint64_t i = 0; i < nrows && !found; i++) { if (strcmp(keyname, mystat[i].keyname) == 0) { found++; rval = mystat[i].value.num; } } CKERR2(found, 1); return rval; #endif } static __attribute__((__unused__)) DBT * dbt_init(DBT *dbt, const void *data, u_int32_t size) { memset(dbt, 0, sizeof *dbt); dbt->data = (void*)data; dbt->size = size; return dbt; } static __attribute__((__unused__)) DBT * dbt_init_malloc (DBT *dbt) { memset(dbt, 0, sizeof *dbt); dbt->flags = DB_DBT_MALLOC; return dbt; } static __attribute__((__unused__)) DBT * dbt_init_realloc (DBT *dbt) { memset(dbt, 0, sizeof *dbt); dbt->flags = DB_DBT_REALLOC; return dbt; } // Simple LCG random number generator. Not high quality, but good enough. static u_int32_t rstate=1; static inline void mysrandom (int s) { rstate=s; } static inline u_int32_t myrandom (void) { rstate = (279470275ull*(u_int64_t)rstate)%4294967291ull; return rstate; } static __attribute__((__unused__)) int int64_dbt_cmp (DB *db UU(), const DBT *a, const DBT *b) { // assert(db && a && b); assert(a); assert(b); // assert(db); assert(a->size == sizeof(int64_t)); assert(b->size == sizeof(int64_t)); int64_t x = *(int64_t *) a->data; int64_t y = *(int64_t *) b->data; if (x<y) return -1; if (x>y) return 1; return 0; } static __attribute__((__unused__)) int int_dbt_cmp (DB *db, const DBT *a, const DBT *b) { assert(db && a && b); assert(a->size == sizeof(int)); assert(b->size == sizeof(int)); int x = *(int *) a->data; int y = *(int *) b->data; if (x<y) return -1; if (x>y) return 1; return 0; } static __attribute__((__unused__)) int uint_dbt_cmp (DB *db, const DBT *a, const DBT *b) { assert(db && a && b); assert(a->size == sizeof(unsigned int)); assert(b->size == sizeof(unsigned int)); unsigned int x = *(unsigned int *) a->data; unsigned int y = *(unsigned int *) b->data; if (x<y) return -1; if (x>y) return 1; return 0; } #include <stdbool.h> #ifndef TRUE // typedef enum __toku_bool { FALSE=0, TRUE=1} BOOL; #define TRUE true #define FALSE false typedef bool BOOL; #endif #ifdef USE_TDB #define SET_TRACE_FILE(x) toku_set_trace_file(x) #define CLOSE_TRACE_FILE(x) toku_close_trace_file() #else #define SET_TRACE_FILE(x) ((void)0) #define CLOSE_TRACE_FILE(x) ((void)0) #endif #include <memory.h> unsigned int seed = 0xFEEDFACE; static u_int64_t __attribute__((__unused__)) random64(void) { static int seeded = 0; if (!seeded) { seeded = 1; srandom(seed); } //random() generates 31 bits of randomness (low order) u_int64_t low = random(); u_int64_t high = random(); u_int64_t twobits = random(); u_int64_t ret = low | (high<<31) | (twobits<<62); return ret; } static __attribute__((__unused__)) double get_tdiff(void) { static struct timeval prev={0,0}; if (prev.tv_sec==0) { gettimeofday(&prev, 0); return 0.0; } else { struct timeval now; gettimeofday(&now, 0); double diff = now.tv_sec - prev.tv_sec + 1e-6*(now.tv_usec - prev.tv_usec); prev = now; return diff; } } static __attribute__((__unused__)) void format_time(const time_t *timer, char *buf) { ctime_r(timer, buf); size_t len = strlen(buf); assert(len < 26); char end; assert(len>=1); end = buf[len-1]; while (end == '\n' || end == '\r') { buf[len-1] = '\0'; len--; assert(len>=1); end = buf[len-1]; } } static __attribute__((__unused__)) void print_time_now(void) { char timestr[80]; time_t now = time(NULL); format_time(&now, timestr); printf("%s", timestr); } //Simulate as hard a crash as possible. //Choices: // raise(SIGABRT) // kill -SIGKILL $pid // divide by 0 // null dereference // abort() // assert(FALSE) (from <assert.h>) // assert(FALSE) (from <toku_assert.h>) // //Linux: // abort() and both assert(FALSE) cause FILE buffers to be flushed and written to disk: Unacceptable //Windows: // None of them cause file buffers to be flushed/written to disk, however // abort(), assert(FALSE) <assert.h>, null dereference, and divide by 0 cause popups requiring user intervention during tests: Unacceptable // //kill -SIGKILL $pid is annoying (and so far untested) // //raise(SIGABRT) has the downside that perhaps it could be caught? //I'm choosing raise(SIGABRT), followed by divide by 0, followed by null dereference, followed by all the others just in case one gets caught. static void UU() toku_hard_crash_on_purpose(void) { #if TOKU_WINDOWS TerminateProcess(GetCurrentProcess(), 137); #else raise(SIGKILL); //Does not flush buffers on linux; cannot be caught. #endif { int zero = 0; int infinity = 1/zero; fprintf(stderr, "Force use of %d\n", infinity); fflush(stderr); //Make certain the string is calculated. } { void * intothevoid = NULL; (*(int*)intothevoid)++; fprintf(stderr, "Force use of *(%p) = %d\n", intothevoid, *(int*)intothevoid); fflush(stderr); } abort(); fprintf(stderr, "This line should never be printed\n"); fflush(stderr); } static void UU() multiply_locks_for_n_dbs(DB_ENV *env, int num_dbs) { int r; uint32_t current_max_locks; r = env->get_lk_max_locks(env, ¤t_max_locks); CKERR(r); r = env->set_lk_max_locks(env, current_max_locks * num_dbs); CKERR(r); #if defined(USE_TDB) uint64_t current_max_lock_memory; r = env->get_lk_max_memory(env, ¤t_max_lock_memory); CKERR(r); r = env->set_lk_max_memory(env, current_max_lock_memory * num_dbs); CKERR(r); #endif } static inline void default_parse_args (int argc, char * const argv[]) { const char *progname=argv[0]; argc--; argv++; while (argc>0) { if (strcmp(argv[0],"-v")==0) { verbose=1; } else if (strcmp(argv[0],"-q")==0) { verbose=0; } else { fprintf(stderr, "Usage:\n %s [-v] [-q]\n", progname); exit(1); } argc--; argv++; } } /* Some macros for evaluating blocks or functions within the scope of a * transaction. */ #define IN_TXN_COMMIT(env, parent, txn, flags, expr) ({ \ DB_TXN *(txn); \ CHK((env)->txn_begin((env), (parent), &(txn), (flags))); \ (expr); \ CHK((txn)->commit((txn), 0)); \ }) #define IN_TXN_ABORT(env, parent, txn, flags, expr) ({ \ DB_TXN *(txn); \ CHK((env)->txn_begin((env), (parent), &(txn), (flags))); \ (expr); \ CHK((txn)->abort(txn)); \ }) #if defined(__cilkplusplus) || defined(__cplusplus) } #endif int test_main (int argc, char * const argv[]); int #if defined(__cilkplusplus) cilk_main(int argc, char *argv[]) #else main(int argc, char * const argv[]) #endif { int r; #if IS_TDB && TOKU_WINDOWS int rinit = toku_ydb_init(); CKERR(rinit); #endif #if !IS_TDB && DB_VERSION_MINOR==4 && DB_VERSION_MINOR == 7 r = db_env_set_func_malloc(toku_malloc); assert(r==0); r = db_env_set_func_free(toku_free); assert(r==0); r = db_env_set_func_realloc(toku_realloc); assert(r==0); #endif toku_os_initialize_settings(1); r = test_main(argc, argv); #if IS_TDB && TOKU_WINDOWS int rdestroy = toku_ydb_destroy(); CKERR(rdestroy); #endif return r; } #endif // __TEST_H