modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/lsm1/lsm-test/lsmtest_main.c (about)

     1  
     2  #include "lsmtest.h"
     3  #include <sqlite3.h>
     4  
     5  void test_failed(){ 
     6    assert( 0 );
     7    return; 
     8  }
     9  
    10  #define testSetError(rc) testSetErrorFunc(rc, pRc, __FILE__, __LINE__)
    11  static void testSetErrorFunc(int rc, int *pRc, const char *zFile, int iLine){
    12    if( rc ){
    13      *pRc = rc;
    14      fprintf(stderr, "FAILED (%s:%d) rc=%d ", zFile, iLine, rc);
    15      test_failed();
    16    }
    17  }
    18  
    19  static int lsm_memcmp(u8 *a, u8 *b, int c){
    20    int i;
    21    for(i=0; i<c; i++){
    22      if( a[i]!=b[i] ) return a[i] - b[i];
    23    }
    24    return 0;
    25  }
    26  
    27  /*
    28  ** A test utility function.
    29  */
    30  void testFetch(
    31    TestDb *pDb,                    /* Database handle */
    32    void *pKey, int nKey,           /* Key to query database for */
    33    void *pVal, int nVal,           /* Expected value */
    34    int *pRc                        /* IN/OUT: Error code */
    35  ){
    36    if( *pRc==0 ){
    37      void *pDbVal;
    38      int nDbVal;
    39      int rc;
    40  
    41      static int nCall = 0; nCall++;
    42  
    43      rc = tdb_fetch(pDb, pKey, nKey, &pDbVal, &nDbVal);
    44      testSetError(rc);
    45      if( rc==0 && (nVal!=nDbVal || (nVal>0 && lsm_memcmp(pVal, pDbVal, nVal))) ){
    46        testSetError(1);
    47      }
    48    }
    49  }
    50  
    51  void testWrite(
    52    TestDb *pDb,                    /* Database handle */
    53    void *pKey, int nKey,           /* Key to query database for */
    54    void *pVal, int nVal,           /* Value to write */
    55    int *pRc                        /* IN/OUT: Error code */
    56  ){
    57    if( *pRc==0 ){
    58      int rc;
    59  static int nCall = 0;
    60  nCall++;
    61      rc = tdb_write(pDb, pKey, nKey, pVal, nVal);
    62      testSetError(rc);
    63    }
    64  }
    65  void testDelete(
    66    TestDb *pDb,                    /* Database handle */
    67    void *pKey, int nKey,           /* Key to query database for */
    68    int *pRc                        /* IN/OUT: Error code */
    69  ){
    70    if( *pRc==0 ){
    71      int rc;
    72      *pRc = rc = tdb_delete(pDb, pKey, nKey);
    73      testSetError(rc);
    74    }
    75  }
    76  void testDeleteRange(
    77    TestDb *pDb,                    /* Database handle */
    78    void *pKey1, int nKey1,
    79    void *pKey2, int nKey2,
    80    int *pRc                        /* IN/OUT: Error code */
    81  ){
    82    if( *pRc==0 ){
    83      int rc;
    84      *pRc = rc = tdb_delete_range(pDb, pKey1, nKey1, pKey2, nKey2);
    85      testSetError(rc);
    86    }
    87  }
    88  
    89  void testBegin(TestDb *pDb, int iTrans, int *pRc){
    90    if( *pRc==0 ){
    91      int rc;
    92      rc = tdb_begin(pDb, iTrans);
    93      testSetError(rc);
    94    }
    95  }
    96  void testCommit(TestDb *pDb, int iTrans, int *pRc){
    97    if( *pRc==0 ){
    98      int rc;
    99      rc = tdb_commit(pDb, iTrans);
   100      testSetError(rc);
   101    }
   102  }
   103  #if 0 /* unused */
   104  static void testRollback(TestDb *pDb, int iTrans, int *pRc){
   105    if( *pRc==0 ){
   106      int rc;
   107      rc = tdb_rollback(pDb, iTrans);
   108      testSetError(rc);
   109    }
   110  }
   111  #endif
   112  
   113  void testWriteStr(
   114    TestDb *pDb,                    /* Database handle */
   115    const char *zKey,               /* Key to query database for */
   116    const char *zVal,               /* Value to write */
   117    int *pRc                        /* IN/OUT: Error code */
   118  ){
   119    int nVal = (zVal ? strlen(zVal) : 0);
   120    testWrite(pDb, (void *)zKey, strlen(zKey), (void *)zVal, nVal, pRc);
   121  }
   122  
   123  #if 0 /* unused */
   124  static void testDeleteStr(TestDb *pDb, const char *zKey, int *pRc){
   125    testDelete(pDb, (void *)zKey, strlen(zKey), pRc);
   126  }
   127  #endif
   128  void testFetchStr(
   129    TestDb *pDb,                    /* Database handle */
   130    const char *zKey,               /* Key to query database for */
   131    const char *zVal,               /* Value to write */
   132    int *pRc                        /* IN/OUT: Error code */
   133  ){
   134    int nVal = (zVal ? strlen(zVal) : 0);
   135    testFetch(pDb, (void *)zKey, strlen(zKey), (void *)zVal, nVal, pRc);
   136  }
   137  
   138  void testFetchCompare(
   139    TestDb *pControl, 
   140    TestDb *pDb, 
   141    void *pKey, int nKey, 
   142    int *pRc
   143  ){
   144    int rc;
   145    void *pDbVal1;
   146    void *pDbVal2;
   147    int nDbVal1;
   148    int nDbVal2;
   149  
   150    static int nCall = 0;
   151    nCall++;
   152  
   153    rc = tdb_fetch(pControl, pKey, nKey, &pDbVal1, &nDbVal1);
   154    testSetError(rc);
   155  
   156    rc = tdb_fetch(pDb, pKey, nKey, &pDbVal2, &nDbVal2);
   157    testSetError(rc);
   158  
   159    if( *pRc==0 
   160     && (nDbVal1!=nDbVal2 || (nDbVal1>0 && memcmp(pDbVal1, pDbVal2, nDbVal1)))
   161    ){
   162      testSetError(1);
   163    }
   164  }
   165  
   166  typedef struct ScanResult ScanResult;
   167  struct ScanResult {
   168    TestDb *pDb;
   169  
   170    int nRow;
   171    u32 cksum1;
   172    u32 cksum2;
   173    void *pKey1; int nKey1;
   174    void *pKey2; int nKey2;
   175  
   176    int bReverse;
   177    int nPrevKey;
   178    u8 aPrevKey[256];
   179  };
   180  
   181  static int keyCompare(void *pKey1, int nKey1, void *pKey2, int nKey2){
   182    int res;
   183    res = memcmp(pKey1, pKey2, MIN(nKey1, nKey2));
   184    if( res==0 ){
   185      res = nKey1 - nKey2;
   186    }
   187    return res;
   188  }
   189  
   190  int test_scan_debug = 0;
   191  
   192  static void scanCompareCb(
   193    void *pCtx, 
   194    void *pKey, int nKey,
   195    void *pVal, int nVal
   196  ){
   197    ScanResult *p = (ScanResult *)pCtx;
   198    u8 *aKey = (u8 *)pKey;
   199    u8 *aVal = (u8 *)pVal;
   200    int i;
   201  
   202    if( test_scan_debug ){
   203      printf("%d: %.*s\n", p->nRow, nKey, (char *)pKey);
   204      fflush(stdout);
   205    }
   206  #if 0
   207    if( test_scan_debug ) printf("%.20s\n", (char *)pVal);
   208  #endif
   209  
   210  #if 0
   211    /* Check tdb_fetch() matches */
   212    int rc = 0;
   213    testFetch(p->pDb, pKey, nKey, pVal, nVal, &rc);
   214    assert( rc==0 );
   215  #endif
   216  
   217    /* Update the checksum data */
   218    p->nRow++;
   219    for(i=0; i<nKey; i++){
   220      p->cksum1 += ((int)aKey[i] << (i&0x0F));
   221      p->cksum2 += p->cksum1;
   222    }
   223    for(i=0; i<nVal; i++){
   224      p->cksum1 += ((int)aVal[i] << (i&0x0F));
   225      p->cksum2 += p->cksum1;
   226    }
   227  
   228    /* Check that the delivered row is not out of order. */
   229    if( nKey<(int)sizeof(p->aPrevKey) ){
   230      if( p->nPrevKey ){
   231        int res = keyCompare(p->aPrevKey, p->nPrevKey, pKey, nKey);
   232        if( (res<0 && p->bReverse) || (res>0 && p->bReverse==0) ){
   233          testPrintError("Returned key out of order at %s:%d\n", 
   234              __FILE__, __LINE__
   235          );
   236        }
   237      }
   238  
   239      p->nPrevKey = nKey;
   240      memcpy(p->aPrevKey, pKey, MIN(p->nPrevKey, nKey));
   241    }
   242  
   243    /* Check that the delivered row is within range. */
   244    if( p->pKey1 && (
   245        (memcmp(p->pKey1, pKey, MIN(p->nKey1, nKey))>0)
   246     || (memcmp(p->pKey1, pKey, MIN(p->nKey1, nKey))==0 && p->nKey1>nKey)
   247    )){
   248      testPrintError("Returned key too small at %s:%d\n", __FILE__, __LINE__);
   249    }
   250    if( p->pKey2 && (
   251        (memcmp(p->pKey2, pKey, MIN(p->nKey2, nKey))<0)
   252     || (memcmp(p->pKey2, pKey, MIN(p->nKey2, nKey))==0 && p->nKey2<nKey)
   253    )){
   254      testPrintError("Returned key too large at %s:%d\n", __FILE__, __LINE__);
   255    }
   256  
   257  }
   258  
   259  /*
   260  ** Scan the contents of the two databases. Check that they match.
   261  */
   262  void testScanCompare(
   263    TestDb *pDb1,                   /* Control (trusted) database */
   264    TestDb *pDb2,                   /* Database being tested */
   265    int bReverse,
   266    void *pKey1, int nKey1, 
   267    void *pKey2, int nKey2, 
   268    int *pRc
   269  ){
   270    static int nCall = 0; nCall++;
   271    if( *pRc==0 ){
   272      ScanResult res1;
   273      ScanResult res2;
   274      void *pRes1 = (void *)&res1;
   275      void *pRes2 = (void *)&res2;
   276  
   277      memset(&res1, 0, sizeof(ScanResult));
   278      memset(&res2, 0, sizeof(ScanResult));
   279  
   280      res1.pDb = pDb1;
   281      res1.nKey1 = nKey1; res1.pKey1 = pKey1;
   282      res1.nKey2 = nKey2; res1.pKey2 = pKey2;
   283      res1.bReverse = bReverse;
   284      res2.pDb = pDb2;
   285      res2.nKey1 = nKey1; res2.pKey1 = pKey1;
   286      res2.nKey2 = nKey2; res2.pKey2 = pKey2;
   287      res2.bReverse = bReverse;
   288  
   289      tdb_scan(pDb1, pRes1, bReverse, pKey1, nKey1, pKey2, nKey2, scanCompareCb);
   290  if( test_scan_debug ) printf("\n\n\n");
   291      tdb_scan(pDb2, pRes2, bReverse, pKey1, nKey1, pKey2, nKey2, scanCompareCb);
   292  if( test_scan_debug ) printf("\n\n\n");
   293  
   294      if( res1.nRow!=res2.nRow 
   295       || res1.cksum1!=res2.cksum1 
   296       || res1.cksum2!=res2.cksum2
   297      ){
   298        printf("expected: %d %X %X\n", res1.nRow, res1.cksum1, res1.cksum2);
   299        printf("got:      %d %X %X\n", res2.nRow, res2.cksum1, res2.cksum2);
   300        testSetError(1);
   301        *pRc = 1;
   302      }
   303    }
   304  }
   305  
   306  void testClose(TestDb **ppDb){
   307    tdb_close(*ppDb);
   308    *ppDb = 0;
   309  }
   310  
   311  TestDb *testOpen(const char *zSystem, int bClear, int *pRc){
   312    TestDb *pDb = 0;
   313    if( *pRc==0 ){
   314      int rc;
   315      rc = tdb_open(zSystem, 0, bClear, &pDb);
   316      if( rc!=0 ){
   317        testSetError(rc);
   318        *pRc = rc;
   319      }
   320    }
   321    return pDb;
   322  }
   323  
   324  void testReopen(TestDb **ppDb, int *pRc){
   325    if( *pRc==0 ){
   326      const char *zLib;
   327      zLib = tdb_library_name(*ppDb);
   328      testClose(ppDb);
   329      *pRc = tdb_open(zLib, 0, 0, ppDb);
   330    }
   331  }
   332  
   333  
   334  #if 0 /* unused */
   335  static void testSystemSelect(const char *zSys, int *piSel, int *pRc){
   336    if( *pRc==0 ){
   337      struct SysName { const char *zName; } *aName;
   338      int nSys;
   339      int i;
   340  
   341      for(nSys=0; tdb_system_name(nSys); nSys++);
   342      aName = malloc(sizeof(struct SysName) * (nSys+1));
   343      for(i=0; i<=nSys; i++){
   344        aName[i].zName = tdb_system_name(i);
   345      }
   346  
   347      *pRc = testArgSelect(aName, "db", zSys, piSel);
   348      free(aName);
   349    }
   350  }
   351  #endif
   352  
   353  char *testMallocVPrintf(const char *zFormat, va_list ap){
   354    int nByte;
   355    va_list copy;
   356    char *zRet;
   357  
   358    __va_copy(copy, ap);
   359    nByte = vsnprintf(0, 0, zFormat, copy);
   360    va_end(copy);
   361  
   362    assert( nByte>=0 );
   363    zRet = (char *)testMalloc(nByte+1);
   364    vsnprintf(zRet, nByte+1, zFormat, ap);
   365    return zRet;
   366  }
   367  
   368  char *testMallocPrintf(const char *zFormat, ...){
   369    va_list ap;
   370    char *zRet;
   371  
   372    va_start(ap, zFormat);
   373    zRet = testMallocVPrintf(zFormat, ap);
   374    va_end(ap);
   375  
   376    return zRet;
   377  }
   378  
   379  
   380  /*
   381  ** A wrapper around malloc(3).
   382  **
   383  ** This function should be used for all allocations made by test procedures.
   384  ** It has the following properties:
   385  **
   386  **   * Test code may assume that allocations may not fail.
   387  **   * Returned memory is always zeroed.
   388  **
   389  ** Allocations made using testMalloc() should be freed using testFree().
   390  */
   391  void *testMalloc(int n){
   392    u8 *p = (u8*)malloc(n + 8);
   393    memset(p, 0, n+8);
   394    *(int*)p = n;
   395    return (void*)&p[8];
   396  }
   397  
   398  void *testMallocCopy(void *pCopy, int nByte){
   399    void *pRet = testMalloc(nByte);
   400    memcpy(pRet, pCopy, nByte);
   401    return pRet;
   402  }
   403  
   404  void *testRealloc(void *ptr, int n){
   405    if( ptr ){
   406      u8 *p = (u8*)ptr - 8;
   407      int nOrig =  *(int*)p;
   408      p = (u8*)realloc(p, n+8);
   409      if( nOrig<n ){
   410        memset(&p[8+nOrig], 0, n-nOrig);
   411      }
   412      *(int*)p = n;
   413      return (void*)&p[8];
   414    }
   415    return testMalloc(n);
   416  }
   417  
   418  /*
   419  ** Free an allocation made by an earlier call to testMalloc().
   420  */
   421  void testFree(void *ptr){
   422    if( ptr ){
   423      u8 *p = (u8*)ptr - 8;
   424      memset(p, 0x55, *(int*)p + 8);
   425      free(p);
   426    }
   427  }
   428  
   429  /*
   430  ** String zPattern contains a glob pattern. Return true if zStr matches 
   431  ** the pattern, or false if it does not.
   432  */
   433  int testGlobMatch(const char *zPattern, const char *zStr){
   434    int i = 0;
   435    int j = 0;
   436  
   437    while( zPattern[i] ){
   438      char p = zPattern[i];
   439  
   440      if( p=='*' || p=='%' ){
   441        do {
   442          if( testGlobMatch(&zPattern[i+1], &zStr[j]) ) return 1;
   443        }while( zStr[j++] );
   444        return 0;
   445      }
   446  
   447      if( zStr[j]==0 || (p!='?' && p!=zStr[j]) ){
   448        /* Match failed. */
   449        return 0;
   450      }
   451  
   452      j++;
   453      i++;
   454    }
   455  
   456    return (zPattern[i]==0 && zStr[j]==0);
   457  }
   458  
   459  /* 
   460  ** End of test utilities 
   461  **************************************************************************/
   462  
   463  int do_test(int nArg, char **azArg){
   464    int j;
   465    int rc;
   466    int nFail = 0;
   467    const char *zPattern = 0;
   468  
   469    if( nArg>1 ){
   470      testPrintError("Usage: test ?PATTERN?\n");
   471      return 1;
   472    }
   473    if( nArg==1 ){
   474      zPattern = azArg[0];
   475    }
   476  
   477    for(j=0; tdb_system_name(j); j++){
   478      rc = 0;
   479  
   480      test_data_1(tdb_system_name(j), zPattern, &rc);
   481      test_data_2(tdb_system_name(j), zPattern, &rc);
   482      test_data_3(tdb_system_name(j), zPattern, &rc);
   483      test_data_4(tdb_system_name(j), zPattern, &rc);
   484      test_rollback(tdb_system_name(j), zPattern, &rc);
   485      test_mc(tdb_system_name(j), zPattern, &rc);
   486      test_mt(tdb_system_name(j), zPattern, &rc);
   487  
   488      if( rc ) nFail++;
   489    }
   490  
   491    rc = 0;
   492    test_oom(zPattern, &rc);
   493    if( rc ) nFail++;
   494  
   495    rc = 0;
   496    test_api(zPattern, &rc);
   497    if( rc ) nFail++;
   498  
   499    rc = 0;
   500    do_crash_test(zPattern, &rc);
   501    if( rc ) nFail++;
   502  
   503    rc = 0;
   504    do_writer_crash_test(zPattern, &rc);
   505    if( rc ) nFail++;
   506  
   507    return (nFail!=0);
   508  }
   509  
   510  static lsm_db *configure_lsm_db(TestDb *pDb){
   511    lsm_db *pLsm;
   512    pLsm = tdb_lsm(pDb);
   513    if( pLsm ){
   514      tdb_lsm_config_str(pDb, "mmap=1 autowork=1 automerge=4 worker_automerge=4");
   515    }
   516    return pLsm;
   517  }
   518  
   519  typedef struct WriteHookEvent WriteHookEvent;
   520  struct WriteHookEvent {
   521    i64 iOff;
   522    int nData;
   523    int nUs;
   524  };
   525  WriteHookEvent prev = {0, 0, 0};
   526  
   527  static void flushPrev(FILE *pOut){
   528    if( prev.nData ){
   529      fprintf(pOut, "w %s %lld %d %d\n", "d", prev.iOff, prev.nData, prev.nUs);
   530      prev.nData = 0;
   531    }
   532  }
   533  
   534  #if 0 /* unused */
   535  static void do_speed_write_hook2(
   536    void *pCtx,
   537    int bLog,
   538    i64 iOff,
   539    int nData,
   540    int nUs
   541  ){
   542    FILE *pOut = (FILE *)pCtx;
   543    if( bLog ) return;
   544  
   545    if( prev.nData && nData && iOff==prev.iOff+prev.nData ){
   546      prev.nData += nData;
   547      prev.nUs += nUs;
   548    }else{
   549      flushPrev(pOut);
   550      if( nData==0 ){
   551        fprintf(pOut, "s %s 0 0 %d\n", (bLog ? "l" : "d"), nUs);
   552      }else{
   553        prev.iOff = iOff;
   554        prev.nData = nData;
   555        prev.nUs = nUs;
   556      }
   557    }
   558  }
   559  #endif
   560  
   561  #define ST_REPEAT  0
   562  #define ST_WRITE   1
   563  #define ST_PAUSE   2
   564  #define ST_FETCH   3
   565  #define ST_SCAN    4
   566  #define ST_NSCAN   5
   567  #define ST_KEYSIZE 6
   568  #define ST_VALSIZE 7
   569  #define ST_TRANS   8
   570  
   571  
   572  static void print_speed_test_help(){
   573    printf(
   574  "\n"
   575  "Repeat the following $repeat times:\n"
   576  "  1. Insert $write key-value pairs. One transaction for each write op.\n"
   577  "  2. Pause for $pause ms.\n"
   578  "  3. Perform $fetch queries on the database.\n"
   579  "\n"
   580  "  Keys are $keysize bytes in size. Values are $valsize bytes in size\n"
   581  "  Both keys and values are pseudo-randomly generated\n"
   582  "\n"
   583  "Options are:\n"
   584  "  -repeat  $repeat                 (default value 10)\n"
   585  "  -write   $write                  (default value 10000)\n"
   586  "  -pause   $pause                  (default value 0)\n"
   587  "  -fetch   $fetch                  (default value 0)\n"
   588  "  -keysize $keysize                (default value 12)\n"
   589  "  -valsize $valsize                (default value 100)\n"
   590  "  -system  $system                 (default value \"lsm\")\n"
   591  "  -trans   $trans                  (default value 0)\n"
   592  "\n"
   593  );
   594  }
   595  
   596  int do_speed_test2(int nArg, char **azArg){
   597    struct Option {
   598      const char *zOpt;
   599      int eVal;
   600      int iDefault;
   601    } aOpt[] = {
   602      { "-repeat",  ST_REPEAT,    10},
   603      { "-write",   ST_WRITE,  10000},
   604      { "-pause",   ST_PAUSE,      0},
   605      { "-fetch",   ST_FETCH,      0},
   606      { "-scan",    ST_SCAN,       0},
   607      { "-nscan",   ST_NSCAN,      0},
   608      { "-keysize", ST_KEYSIZE,   12},
   609      { "-valsize", ST_VALSIZE,  100},
   610      { "-trans",   ST_TRANS,      0},
   611      { "-system",  -1,            0},
   612      { "help",     -2,            0},
   613      {0, 0, 0}
   614    };
   615    int i;
   616    int aParam[9];
   617    int rc = 0;
   618    int bReadonly = 0;
   619    int nContent = 0;
   620  
   621    TestDb *pDb;
   622    Datasource *pData;
   623    DatasourceDefn defn = { TEST_DATASOURCE_RANDOM, 0, 0, 0, 0 };
   624    char *zSystem = "";
   625    int bLsm = 1;
   626    FILE *pLog = 0;
   627  
   628  #ifdef NDEBUG
   629    /* If NDEBUG is defined, disable the dynamic memory related checks in
   630    ** lsmtest_mem.c. They slow things down.  */
   631    testMallocUninstall(tdb_lsm_env());
   632  #endif
   633  
   634    /* Initialize aParam[] with default values. */
   635    for(i=0; i<ArraySize(aOpt); i++){
   636      if( aOpt[i].zOpt ) aParam[aOpt[i].eVal] = aOpt[i].iDefault;
   637    }
   638  
   639    /* Process the command line switches. */
   640    for(i=0; i<nArg; i+=2){
   641      int iSel;
   642      rc = testArgSelect(aOpt, "switch", azArg[i], &iSel);
   643      if( rc ){
   644        return rc;
   645      }
   646      if( aOpt[iSel].eVal==-2 ){
   647        print_speed_test_help();
   648        return 0;
   649      }
   650      if( i+1==nArg ){
   651        testPrintError("option %s requires an argument\n", aOpt[iSel].zOpt);
   652        return 1;
   653      }
   654      if( aOpt[iSel].eVal>=0 ){
   655        aParam[aOpt[iSel].eVal] = atoi(azArg[i+1]);
   656      }else{
   657        zSystem = azArg[i+1];
   658        bLsm = 0;
   659  #if 0
   660        for(j=0; zSystem[j]; j++){
   661          if( zSystem[j]=='=' ) bLsm = 1;
   662        }
   663  #endif
   664      }
   665    }
   666    
   667    printf("#");
   668    for(i=0; i<ArraySize(aOpt); i++){
   669      if( aOpt[i].zOpt ){
   670        if( aOpt[i].eVal>=0 ){
   671          printf(" %s=%d", &aOpt[i].zOpt[1], aParam[aOpt[i].eVal]);
   672        }else if( aOpt[i].eVal==-1 ){
   673          printf(" %s=\"%s\"", &aOpt[i].zOpt[1], zSystem);
   674        }
   675      }
   676    }
   677    printf("\n");
   678  
   679    defn.nMinKey = defn.nMaxKey = aParam[ST_KEYSIZE];
   680    defn.nMinVal = defn.nMaxVal = aParam[ST_VALSIZE];
   681    pData = testDatasourceNew(&defn);
   682  
   683    if( aParam[ST_WRITE]==0 ){
   684      bReadonly = 1;
   685    }
   686  
   687    if( bLsm ){
   688      rc = tdb_lsm_open(zSystem, "testdb.lsm", !bReadonly, &pDb);
   689    }else{
   690      pDb = testOpen(zSystem, !bReadonly, &rc);
   691    }
   692    if( rc!=0 ) return rc;
   693    if( bReadonly ){
   694      nContent = testCountDatabase(pDb);
   695    }
   696  
   697  #if 0
   698    pLog = fopen("/tmp/speed.log", "w");
   699    tdb_lsm_write_hook(pDb, do_speed_write_hook2, (void *)pLog);
   700  #endif
   701  
   702    for(i=0; i<aParam[ST_REPEAT] && rc==0; i++){
   703      int msWrite, msFetch;
   704      int iFetch;
   705      int nWrite = aParam[ST_WRITE];
   706  
   707      if( bReadonly ){
   708        msWrite = 0;
   709      }else{
   710        testTimeInit();
   711  
   712        if( aParam[ST_TRANS] ) testBegin(pDb, 2, &rc);
   713        testWriteDatasourceRange(pDb, pData, i*nWrite, nWrite, &rc);
   714        if( aParam[ST_TRANS] ) testCommit(pDb, 0, &rc);
   715  
   716        msWrite = testTimeGet();
   717        nContent += nWrite;
   718      }
   719  
   720      if( aParam[ST_PAUSE] ){
   721        if( aParam[ST_PAUSE]/1000 ) sleep(aParam[ST_PAUSE]/1000);
   722        if( aParam[ST_PAUSE]%1000 ) usleep(1000 * (aParam[ST_PAUSE]%1000));
   723      }
   724  
   725      if( aParam[ST_FETCH] ){
   726        testTimeInit();
   727        if( aParam[ST_TRANS] ) testBegin(pDb, 1, &rc);
   728        for(iFetch=0; iFetch<aParam[ST_FETCH]; iFetch++){
   729          int iKey = testPrngValue(i*nWrite+iFetch) % nContent;
   730  #ifndef NDEBUG
   731          testDatasourceFetch(pDb, pData, iKey, &rc);
   732  #else
   733          void *pKey; int nKey;           /* Database key to query for */
   734          void *pVal; int nVal;           /* Result of query */
   735  
   736          testDatasourceEntry(pData, iKey, &pKey, &nKey, 0, 0);
   737          rc = tdb_fetch(pDb, pKey, nKey, &pVal, &nVal);
   738          if( rc==0 && nVal<0 ) rc = 1;
   739          if( rc ) break;
   740  #endif
   741        }
   742        if( aParam[ST_TRANS] ) testCommit(pDb, 0, &rc);
   743        msFetch = testTimeGet();
   744      }else{
   745        msFetch = 0;
   746      }
   747  
   748      if( i==(aParam[ST_REPEAT]-1) ){
   749        testTimeInit();
   750        testClose(&pDb);
   751        msWrite += testTimeGet();
   752      }
   753  
   754      printf("%d %d %d\n", i, msWrite, msFetch);
   755      fflush(stdout);
   756    }
   757  
   758    testClose(&pDb);
   759    testDatasourceFree(pData);
   760  
   761    if( pLog ){
   762      flushPrev(pLog);
   763      fclose(pLog);
   764    }
   765    return rc;
   766  }
   767  
   768  int do_speed_tests(int nArg, char **azArg){
   769  
   770    struct DbSystem {
   771      const char *zLibrary;
   772      const char *zColor;
   773    } aSys[] = {
   774      { "sqlite3",      "black" },
   775      { "leveldb",      "blue" },
   776      { "lsm",          "red" },
   777      { "lsm_mt2",      "orange" },
   778      { "lsm_mt3",      "purple" },
   779      { "kyotocabinet", "green" },
   780      {0, 0}
   781    };
   782  
   783    int i;
   784    int j;
   785    int rc;
   786    int nSleep = 0;                 /* ms of rest allowed between INSERT tests */
   787    int nRow = 0;                   /* Number of rows to insert into database */
   788    int nStep;                      /* Measure INSERT time after this many rows */
   789    int nSelStep;                   /* Measure SELECT time after this many rows */
   790    int nSelTest;                   /* Number of SELECTs to run for timing */
   791    int doReadTest = 1;
   792    int doWriteTest = 1;
   793  
   794    int *aTime;                     /* INSERT timing data */
   795    int *aWrite;                    /* Writes per nStep inserts */
   796    int *aSelTime;                  /* SELECT timing data */
   797    int isFirst = 1;
   798    int bSleep = 0;
   799  
   800    /* File to write gnuplot script to. */
   801    const char *zOut = "lsmtest_speed.gnuplot";
   802  
   803    u32 sys_mask = 0;
   804  
   805    testMallocUninstall(tdb_lsm_env());
   806  
   807    for(i=0; i<nArg; i++){
   808      struct Opt { 
   809        const char *zOpt; 
   810        int isSwitch;
   811      } aOpt[] = {
   812        { "sqlite3" , 0},
   813        { "leveldb" , 0},
   814        { "lsm" , 0},
   815        { "lsm_mt2" , 0},
   816        { "lsm_mt3" , 0},
   817        { "kyotocabinet" , 0},
   818        { "-rows"     , 1},
   819        { "-sleep"    , 2},
   820        { "-testmode" , 3},
   821        { "-out"      , 4},
   822        { 0, 0}
   823      };
   824      int iSel;
   825  
   826      rc = testArgSelect(aOpt, "argument", azArg[i], &iSel);
   827      if( rc ) return rc;
   828  
   829      if( aOpt[iSel].isSwitch ){
   830        i++;
   831  
   832        if( i>=nArg ){
   833          testPrintError("option %s requires an argument\n", aOpt[iSel].zOpt);
   834          return 1;
   835        }
   836        if( aOpt[iSel].isSwitch==1 ){
   837          nRow = atoi(azArg[i]);
   838        }
   839        if( aOpt[iSel].isSwitch==2 ){
   840          nSleep = atoi(azArg[i]);
   841        }
   842        if( aOpt[iSel].isSwitch==3 ){
   843          struct Mode {
   844            const char *zMode;
   845            int doReadTest;
   846            int doWriteTest;
   847          } aMode[] = {{"ro", 1, 0} , {"rw", 1, 1}, {"wo", 0, 1}, {0, 0, 0}};
   848          int iMode;
   849          rc = testArgSelect(aMode, "option", azArg[i], &iMode);
   850          if( rc ) return rc;
   851          doReadTest = aMode[iMode].doReadTest;
   852          doWriteTest = aMode[iMode].doWriteTest;
   853        }
   854        if( aOpt[iSel].isSwitch==4 ){
   855          /* The "-out FILE" switch. This option is used to specify a file to
   856          ** write the gnuplot script to. */
   857          zOut = azArg[i];
   858        }
   859      }else{
   860        /* A db name */
   861        rc = testArgSelect(aOpt, "system", azArg[i], &iSel);
   862        if( rc ) return rc;
   863        sys_mask |= (1<<iSel);
   864      }
   865    }
   866  
   867    if( sys_mask==0 ) sys_mask = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3);
   868    nRow = MAX(nRow, 100000);
   869    nStep = nRow/100;
   870    nSelStep = nRow/10;
   871    nSelTest = (nSelStep > 100000) ? 100000 : nSelStep;
   872  
   873    aTime = malloc(sizeof(int) * ArraySize(aSys) * nRow/nStep);
   874    aWrite = malloc(sizeof(int) * nRow/nStep);
   875    aSelTime = malloc(sizeof(int) * ArraySize(aSys) * nRow/nSelStep);
   876  
   877    /* This loop collects the INSERT speed data. */
   878    if( doWriteTest ){
   879      printf("Writing output to file \"%s\".\n",  zOut);
   880  
   881      for(j=0; aSys[j].zLibrary; j++){
   882        FILE *pLog = 0;
   883        TestDb *pDb;                  /* Database being tested */
   884        lsm_db *pLsm;
   885        int iDot = 0;
   886    
   887        if( ((1<<j)&sys_mask)==0 ) continue;
   888        if( bSleep && nSleep ) sqlite3_sleep(nSleep);
   889        bSleep = 1;
   890  
   891        testCaseBegin(&rc, 0, "speed.insert.%s", aSys[j].zLibrary);
   892  
   893        rc = tdb_open(aSys[j].zLibrary, 0, 1, &pDb);
   894        if( rc ) return rc;
   895  
   896        pLsm = configure_lsm_db(pDb);
   897  #if 0
   898        pLog = fopen("/tmp/speed.log", "w");
   899        tdb_lsm_write_hook(pDb, do_speed_write_hook2, (void *)pLog);
   900  #endif
   901    
   902        testTimeInit();
   903        for(i=0; i<nRow; i+=nStep){
   904          int iStep;
   905          int nWrite1 = 0, nWrite2 = 0;
   906          testCaseProgress(i, nRow, testCaseNDot(), &iDot);
   907          if( pLsm ) lsm_info(pLsm, LSM_INFO_NWRITE, &nWrite1);
   908          for(iStep=0; iStep<nStep; iStep++){
   909            u32 aKey[4];                  /* 16-byte key */
   910            u32 aVal[25];                 /* 100 byte value */
   911            testPrngArray(i+iStep, aKey, ArraySize(aKey));
   912            testPrngArray(i+iStep, aVal, ArraySize(aVal));
   913            rc = tdb_write(pDb, aKey, sizeof(aKey), aVal, sizeof(aVal));
   914          }
   915          aTime[(j*nRow+i)/nStep] = testTimeGet();
   916          if( pLsm ) lsm_info(pLsm, LSM_INFO_NWRITE, &nWrite2);
   917          aWrite[i/nStep] = nWrite2 - nWrite1;
   918        }
   919  
   920        tdb_close(pDb);
   921        if( pLog ) fclose(pLog);
   922        testCaseFinish(rc);
   923      }
   924    }
   925  
   926    /* This loop collects the SELECT speed data. */
   927    if( doReadTest ){
   928      for(j=0; aSys[j].zLibrary; j++){
   929        int iDot = 0;
   930        TestDb *pDb;                  /* Database being tested */
   931  
   932        if( ((1<<j)&sys_mask)==0 ) continue;
   933        if( bSleep && nSleep ) sqlite3_sleep(nSleep);
   934        bSleep = 1;
   935  
   936        testCaseBegin(&rc, 0, "speed.select.%s", aSys[j].zLibrary);
   937  
   938        if( doWriteTest ){
   939          rc = tdb_open(aSys[j].zLibrary, 0, 1, &pDb);
   940          if( rc ) return rc;
   941          configure_lsm_db(pDb);
   942  
   943          for(i=0; i<nRow; i+=nSelStep){
   944            int iStep;
   945            int iSel;
   946            testCaseProgress(i, nRow, testCaseNDot(), &iDot);
   947            for(iStep=0; iStep<nSelStep; iStep++){
   948              u32 aKey[4];                  /* 16-byte key */
   949              u32 aVal[25];                 /* 100 byte value */
   950              testPrngArray(i+iStep, aKey, ArraySize(aKey));
   951              testPrngArray(i+iStep, aVal, ArraySize(aVal));
   952              rc = tdb_write(pDb, aKey, sizeof(aKey), aVal, sizeof(aVal));
   953            }
   954      
   955            testTimeInit();
   956            for(iSel=0; iSel<nSelTest; iSel++){
   957              void *pDummy;
   958              int nDummy;
   959              u32 iKey;
   960              u32 aKey[4];                  /* 16-byte key */
   961      
   962              iKey = testPrngValue(iSel) % (i+nSelStep);
   963              testPrngArray(iKey, aKey, ArraySize(aKey));
   964              rc = tdb_fetch(pDb, aKey, sizeof(aKey), &pDummy, &nDummy);
   965            }
   966            aSelTime[(j*nRow+i)/nSelStep] = testTimeGet();
   967            tdb_fetch(pDb, 0, 0, 0, 0);
   968          }
   969        }else{
   970          int t;
   971          int iSel;
   972  
   973          rc = tdb_open(aSys[j].zLibrary, 0, 0, &pDb);
   974          configure_lsm_db(pDb);
   975  
   976          testTimeInit();
   977          for(iSel=0; rc==LSM_OK && iSel<nSelTest; iSel++){
   978            void *pDummy;
   979            int nDummy;
   980            u32 iKey;
   981            u32 aKey[4];                  /* 16-byte key */
   982  #ifndef NDEBUG
   983            u32 aVal[25];                 /* 100 byte value */
   984  #endif
   985  
   986            testCaseProgress(iSel, nSelTest, testCaseNDot(), &iDot);
   987      
   988            iKey = testPrngValue(iSel) % nRow;
   989            testPrngArray(iKey, aKey, ArraySize(aKey));
   990            rc = tdb_fetch(pDb, aKey, sizeof(aKey), &pDummy, &nDummy);
   991  
   992  #ifndef NDEBUG
   993            testPrngArray(iKey, aVal, ArraySize(aVal));
   994            assert( nDummy==100 && memcmp(aVal, pDummy, 100)==0 );
   995  #endif
   996          }
   997          if( rc!=LSM_OK ) return rc;
   998  
   999          t = testTimeGet();
  1000          tdb_fetch(pDb, 0, 0, 0, 0);
  1001  
  1002          printf("%s: %d selects/second\n", 
  1003              aSys[j].zLibrary, (int)((double)nSelTest*1000.0/t)
  1004          );
  1005        }
  1006  
  1007        tdb_close(pDb);
  1008        testCaseFinish(rc);
  1009      }
  1010    }
  1011  
  1012  
  1013    if( doWriteTest ){
  1014      FILE *pOut = fopen(zOut, "w");
  1015      if( !pOut ){
  1016        printf("fopen(\"%s\", \"w\"): %s\n", zOut, strerror(errno));
  1017        return 1;
  1018      }
  1019  
  1020      fprintf(pOut, "set xlabel \"Rows Inserted\"\n");
  1021      fprintf(pOut, "set ylabel \"Inserts per second\"\n");
  1022      if( doReadTest ){
  1023        fprintf(pOut, "set y2label \"Selects per second\"\n");
  1024      }else if( sys_mask==(1<<2) ){
  1025        fprintf(pOut, "set y2label \"Page writes per insert\"\n");
  1026      }
  1027      fprintf(pOut, "set yrange [0:*]\n");
  1028      fprintf(pOut, "set y2range [0:*]\n");
  1029      fprintf(pOut, "set xrange [%d:*]\n", MAX(nStep, nRow/20) );
  1030      fprintf(pOut, "set ytics nomirror\n");
  1031      fprintf(pOut, "set y2tics nomirror\n");
  1032      fprintf(pOut, "set key box lw 0.01\n");
  1033      fprintf(pOut, "plot ");
  1034    
  1035      for(j=0; aSys[j].zLibrary; j++){
  1036        if( (1<<j)&sys_mask ){
  1037          const char *zLib = aSys[j].zLibrary;
  1038          fprintf(pOut, "%s\"-\" ti \"%s INSERT\" with lines lc rgb \"%s\" ", 
  1039              (isFirst?"":", "), zLib, aSys[j].zColor
  1040          );
  1041          if( doReadTest ){
  1042            fprintf(pOut, ", \"-\" ti \"%s SELECT\" "
  1043                   "axis x1y2 with points lw 3 lc rgb \"%s\""
  1044                , zLib, aSys[j].zColor
  1045            );
  1046          }
  1047          isFirst = 0;
  1048        }
  1049      }
  1050  
  1051      assert( strcmp(aSys[2].zLibrary, "lsm")==0 );
  1052      if( sys_mask==(1<<2) && !doReadTest ){
  1053        fprintf(pOut, ", \"-\" ti \"lsm pages written\" "
  1054          "axis x1y2 with boxes lw 1 lc rgb \"grey\""
  1055        );
  1056      }
  1057    
  1058      fprintf(pOut, "\n");
  1059    
  1060      for(j=0; aSys[j].zLibrary; j++){
  1061        if( ((1<<j)&sys_mask)==0 ) continue;
  1062        fprintf(pOut, "# Rows    Inserts per second\n");
  1063        for(i=0; i<nRow; i+=nStep){
  1064          int iTime = aTime[(j*nRow+i)/nStep];
  1065          int ips = (int)((i+nStep)*1000.0 / (double)iTime);
  1066          fprintf(pOut, "%d %d\n", i+nStep, ips);
  1067        }
  1068        fprintf(pOut, "end\n");
  1069    
  1070        if( doReadTest ){
  1071          fprintf(pOut, "# Rows    Selects per second\n");
  1072          for(i=0; i<nRow; i+=nSelStep){
  1073            int sps = (int)(nSelTest*1000.0/(double)aSelTime[(j*nRow+i)/nSelStep]);
  1074            fprintf(pOut, "%d %d\n", i+nSelStep, sps);
  1075          }
  1076          fprintf(pOut, "end\n");
  1077        }else if( sys_mask==(1<<2) ){
  1078          for(i=0; i<(nRow/nStep); i++){
  1079            fprintf(pOut, "%d %f\n", i*nStep, (double)aWrite[i] / (double)nStep);
  1080          }
  1081          fprintf(pOut, "end\n");
  1082        }
  1083      }
  1084    
  1085      fprintf(pOut, "pause -1\n");
  1086      fclose(pOut);
  1087    }
  1088  
  1089    free(aTime);
  1090    free(aSelTime);
  1091    free(aWrite);
  1092    testMallocInstall(tdb_lsm_env());
  1093    return 0;
  1094  }
  1095  
  1096  /*
  1097  ** Usage: lsmtest random ?N?
  1098  **
  1099  ** This command prints a sequence of zero or more numbers from the PRNG
  1100  ** system to stdout. If the "N" argument is missing, values the first 10
  1101  ** values (i=0, i=1, ... i=9) are printed. Otherwise, the first N.
  1102  **
  1103  ** This was added to verify that the PRNG values do not change between
  1104  ** runs of the lsmtest program.
  1105  */
  1106  int do_random_tests(int nArg, char **azArg){
  1107    int i;
  1108    int nRand;
  1109    if( nArg==0 ){
  1110      nRand = 10;
  1111    }else if( nArg==1 ){
  1112      nRand = atoi(azArg[0]);
  1113    }else{
  1114      testPrintError("Usage: random ?N?\n");
  1115      return -1;
  1116    }
  1117    for(i=0; i<nRand; i++){
  1118      printf("0x%x\n", testPrngValue(i));
  1119    }
  1120    return 0;
  1121  }
  1122  
  1123  static int testFormatSize(char *aBuf, int nBuf, i64 nByte){
  1124    int res;
  1125    if( nByte<(1<<10) ){
  1126      res = snprintf(aBuf, nBuf, "%d byte", (int)nByte);
  1127    }else if( nByte<(1<<20) ){
  1128      res = snprintf(aBuf, nBuf, "%dK", (int)(nByte/(1<<10)));
  1129    }else{
  1130      res = snprintf(aBuf, nBuf, "%dM", (int)(nByte/(1<<20)));
  1131    }
  1132    return res;
  1133  }
  1134  
  1135  static i64 testReadSize(char *z){
  1136    int n = strlen(z);
  1137    char c = z[n-1];
  1138    i64 nMul = 1;
  1139  
  1140    switch( c ){
  1141      case 'g': case 'G':
  1142        nMul = (1<<30);
  1143        break;
  1144  
  1145      case 'm': case 'M':
  1146        nMul = (1<<20);
  1147        break;
  1148  
  1149      case 'k': case 'K':
  1150        nMul = (1<<10);
  1151        break;
  1152  
  1153      default:
  1154        nMul = 1;
  1155    }
  1156  
  1157    return nMul * (i64)atoi(z);
  1158  } 
  1159  
  1160  /*
  1161  ** Usage: lsmtest writespeed FILESIZE BLOCKSIZE SYNCSIZE
  1162  */
  1163  static int do_writer_test(int nArg, char **azArg){
  1164    int nBlock;
  1165    int nSize;
  1166    int i;
  1167    int fd;
  1168    int ms;
  1169    char aFilesize[32];
  1170    char aBlockSize[32];
  1171  
  1172    char *aPage;
  1173    int *aOrder;
  1174    int nSync;
  1175  
  1176    i64 filesize;
  1177    i64 blocksize;
  1178    i64 syncsize;
  1179    int nPage = 4096;
  1180  
  1181    /* How long to sleep before running a trial (in ms). */
  1182  #if 0
  1183    const int nSleep = 10000;
  1184  #endif
  1185    const int nSleep = 0;
  1186  
  1187    if( nArg!=3 ){
  1188      testPrintUsage("FILESIZE BLOCKSIZE SYNCSIZE");
  1189      return -1;
  1190    }
  1191  
  1192    filesize = testReadSize(azArg[0]);
  1193    blocksize = testReadSize(azArg[1]);
  1194    syncsize = testReadSize(azArg[2]);
  1195  
  1196    nBlock = (int)(filesize / blocksize);
  1197    nSize = (int)blocksize;
  1198    nSync = (int)(syncsize / blocksize);
  1199  
  1200    aPage = (char *)malloc(4096);
  1201    aOrder = (int *)malloc(nBlock * sizeof(int));
  1202    for(i=0; i<nBlock; i++) aOrder[i] = i;
  1203    for(i=0; i<(nBlock*25); i++){
  1204      int tmp;
  1205      u32 a = testPrngValue(i);
  1206      u32 b = testPrngValue(a);
  1207      a = a % nBlock;
  1208      b = b % nBlock;
  1209      tmp = aOrder[a];
  1210      aOrder[a] = aOrder[b];
  1211      aOrder[b] = tmp;
  1212    }
  1213  
  1214    testFormatSize(aFilesize, sizeof(aFilesize), (i64)nBlock * (i64)nSize);
  1215    testFormatSize(aBlockSize, sizeof(aFilesize), nSize);
  1216  
  1217    printf("Testing writing a %s file using %s blocks. ", aFilesize, aBlockSize);
  1218    if( nSync==1 ){
  1219      printf("Sync after each block.\n");
  1220    }else{
  1221      printf("Sync after each %d blocks.\n", nSync);
  1222    }
  1223  
  1224    printf("Preparing file... ");
  1225    fflush(stdout);
  1226    unlink("writer.out");
  1227    fd = open("writer.out", O_RDWR|O_CREAT|_O_BINARY, 0664);
  1228    if( fd<0 ){
  1229      testPrintError("open(): %d - %s\n", errno, strerror(errno));
  1230      return -1;
  1231    }
  1232    testTimeInit();
  1233    for(i=0; i<nBlock; i++){
  1234      int iPg;
  1235      memset(aPage, i&0xFF, nPage);
  1236      for(iPg=0; iPg<(nSize/nPage); iPg++){
  1237        write(fd, aPage, nPage);
  1238      }
  1239    }
  1240    fsync(fd);
  1241    printf("ok (%d ms)\n", testTimeGet());
  1242  
  1243    for(i=0; i<5; i++){
  1244      int j;
  1245  
  1246      sqlite3_sleep(nSleep);
  1247      printf("Now writing sequentially...  ");
  1248      fflush(stdout);
  1249  
  1250      lseek(fd, 0, SEEK_SET);
  1251      testTimeInit();
  1252      for(j=0; j<nBlock; j++){
  1253        int iPg;
  1254        if( ((j+1)%nSync)==0 ) fdatasync(fd);
  1255        memset(aPage, j&0xFF, nPage);
  1256        for(iPg=0; iPg<(nSize/nPage); iPg++){
  1257          write(fd, aPage, nPage);
  1258        }
  1259      }
  1260      fdatasync(fd);
  1261      ms = testTimeGet();
  1262      printf("%d ms\n", ms);
  1263      sqlite3_sleep(nSleep);
  1264      printf("Now in an arbitrary order... ");
  1265  
  1266      fflush(stdout);
  1267      testTimeInit();
  1268      for(j=0; j<nBlock; j++){
  1269        int iPg;
  1270        if( ((j+1)%nSync)==0 ) fdatasync(fd);
  1271        lseek(fd, aOrder[j]*nSize, SEEK_SET);
  1272        memset(aPage, j&0xFF, nPage);
  1273        for(iPg=0; iPg<(nSize/nPage); iPg++){
  1274          write(fd, aPage, nPage);
  1275        }
  1276      }
  1277      fdatasync(fd);
  1278      ms = testTimeGet();
  1279      printf("%d ms\n", ms);
  1280    }
  1281  
  1282    close(fd);
  1283    free(aPage);
  1284    free(aOrder);
  1285  
  1286    return 0;
  1287  }
  1288  
  1289  static void do_insert_work_hook(lsm_db *db, void *p){
  1290    char *z = 0;
  1291    lsm_info(db, LSM_INFO_DB_STRUCTURE, &z);
  1292    if( z ){
  1293      printf("%s\n", z);
  1294      fflush(stdout);
  1295      lsm_free(lsm_get_env(db), z);
  1296    }
  1297  
  1298    unused_parameter(p);
  1299  }
  1300  
  1301  typedef struct InsertWriteHook InsertWriteHook;
  1302  struct InsertWriteHook {
  1303    FILE *pOut;
  1304    int bLog;
  1305    i64 iOff;
  1306    int nData;
  1307  };
  1308  
  1309  static void flushHook(InsertWriteHook *pHook){
  1310    if( pHook->nData ){
  1311      fprintf(pHook->pOut, "write %s %d %d\n", 
  1312          (pHook->bLog ? "log" : "db"), (int)pHook->iOff, pHook->nData
  1313      );
  1314      pHook->nData = 0;
  1315      fflush(pHook->pOut);
  1316    }
  1317  }
  1318  
  1319  static void do_insert_write_hook(
  1320    void *pCtx,
  1321    int bLog,
  1322    i64 iOff,
  1323    int nData,
  1324    int nUs
  1325  ){
  1326    InsertWriteHook *pHook = (InsertWriteHook *)pCtx;
  1327    if( bLog ) return;
  1328  
  1329    if( nData==0 ){
  1330      flushHook(pHook);
  1331      fprintf(pHook->pOut, "sync %s\n", (bLog ? "log" : "db"));
  1332    }else if( pHook->nData 
  1333           && bLog==pHook->bLog 
  1334           && iOff==(pHook->iOff+pHook->nData) 
  1335    ){
  1336      pHook->nData += nData;
  1337    }else{
  1338      flushHook(pHook);
  1339      pHook->bLog = bLog;
  1340      pHook->iOff = iOff;
  1341      pHook->nData = nData;
  1342    }
  1343  }
  1344  
  1345  static int do_replay(int nArg, char **azArg){
  1346    char aBuf[4096];
  1347    FILE *pInput;
  1348    FILE *pClose = 0;
  1349    const char *zDb;
  1350  
  1351    lsm_env *pEnv;
  1352    lsm_file *pOut;
  1353    int rc;
  1354  
  1355    if( nArg!=2 ){
  1356      testPrintError("Usage: replay WRITELOG FILE\n");
  1357      return 1;
  1358    }
  1359  
  1360    if( strcmp(azArg[0], "-")==0 ){
  1361      pInput = stdin;
  1362    }else{
  1363      pClose = pInput = fopen(azArg[0], "r");
  1364    }
  1365    zDb = azArg[1];
  1366    pEnv = tdb_lsm_env();
  1367    rc = pEnv->xOpen(pEnv, zDb, 0, &pOut);
  1368    if( rc!=LSM_OK ) return rc;
  1369  
  1370    while( feof(pInput)==0 ){
  1371      char zLine[80];
  1372      fgets(zLine, sizeof(zLine)-1, pInput);
  1373      zLine[sizeof(zLine)-1] = '\0';
  1374  
  1375      if( 0==memcmp("sync db", zLine, 7) ){
  1376        rc = pEnv->xSync(pOut);
  1377        if( rc!=0 ) break;
  1378      }else{
  1379        int iOff;
  1380        int nData;
  1381        int nMatch;
  1382        nMatch = sscanf(zLine, "write db %d %d", &iOff, &nData);
  1383        if( nMatch==2 ){
  1384          int i;
  1385          for(i=0; i<nData; i+=sizeof(aBuf)){
  1386            memset(aBuf, i&0xFF, sizeof(aBuf));
  1387            rc = pEnv->xWrite(pOut, iOff+i, aBuf, sizeof(aBuf));
  1388            if( rc!=0 ) break;
  1389          }
  1390        }
  1391      }
  1392    }
  1393    if( pClose ) fclose(pClose);
  1394    pEnv->xClose(pOut);
  1395  
  1396    return rc;
  1397  }
  1398  
  1399  static int do_insert(int nArg, char **azArg){
  1400    const char *zDb = "lsm";
  1401    TestDb *pDb = 0;
  1402    int i;
  1403    int rc;
  1404    const int nRow = 1 * 1000 * 1000;
  1405  
  1406    DatasourceDefn defn = { TEST_DATASOURCE_RANDOM, 8, 15, 80, 150 };
  1407    Datasource *pData = 0;
  1408  
  1409    if( nArg>1 ){
  1410      testPrintError("Usage: insert ?DATABASE?\n");
  1411      return 1;
  1412    }
  1413    if( nArg==1 ){ zDb = azArg[0]; }
  1414  
  1415    testMallocUninstall(tdb_lsm_env());
  1416    for(i=0; zDb[i] && zDb[i]!='='; i++);
  1417    if( zDb[i] ){
  1418      rc = tdb_lsm_open(zDb, "testdb.lsm", 1, &pDb);
  1419    }else{
  1420      rc = tdb_open(zDb, 0, 1, &pDb);
  1421    }
  1422  
  1423    if( rc!=0 ){
  1424      testPrintError("Error opening db \"%s\": %d\n", zDb, rc);
  1425    }else{
  1426      InsertWriteHook hook;
  1427      memset(&hook, 0, sizeof(hook));
  1428      hook.pOut = fopen("writelog.txt", "w");
  1429  
  1430      pData = testDatasourceNew(&defn);
  1431      tdb_lsm_config_work_hook(pDb, do_insert_work_hook, 0);
  1432      tdb_lsm_write_hook(pDb, do_insert_write_hook, (void *)&hook);
  1433  
  1434      if( rc==0 ){
  1435        for(i=0; i<nRow; i++){
  1436          void *pKey; int nKey;     /* Database key to insert */
  1437          void *pVal; int nVal;     /* Database value to insert */
  1438          testDatasourceEntry(pData, i, &pKey, &nKey, &pVal, &nVal);
  1439          tdb_write(pDb, pKey, nKey, pVal, nVal);
  1440        }
  1441      }
  1442  
  1443      testDatasourceFree(pData);
  1444      tdb_close(pDb);
  1445      flushHook(&hook);
  1446      fclose(hook.pOut);
  1447    }
  1448    testMallocInstall(tdb_lsm_env());
  1449  
  1450    return rc;
  1451  }
  1452  
  1453  static int st_do_show(int a, char **b)      { return do_show(a, b); }
  1454  static int st_do_work(int a, char **b)      { return do_work(a, b); }
  1455  static int st_do_io(int a, char **b)        { return do_io(a, b); }
  1456  
  1457  #ifdef __linux__
  1458  #include <sys/time.h>
  1459  #include <sys/resource.h>
  1460  
  1461  static void lsmtest_rusage_report(void){
  1462    struct rusage r;
  1463    memset(&r, 0, sizeof(r));
  1464  
  1465    getrusage(RUSAGE_SELF, &r);
  1466    printf("# getrusage: { ru_maxrss %d ru_oublock %d ru_inblock %d }\n", 
  1467        (int)r.ru_maxrss, (int)r.ru_oublock, (int)r.ru_inblock
  1468    );
  1469  }
  1470  #else
  1471  static void lsmtest_rusage_report(void){
  1472    /* no-op */
  1473  }
  1474  #endif
  1475  
  1476  int main(int argc, char **argv){
  1477    struct TestFunc {
  1478      const char *zName;
  1479      int bRusageReport;
  1480      int (*xFunc)(int, char **);
  1481    } aTest[] = {
  1482      {"random",      1, do_random_tests},
  1483      {"writespeed",  1, do_writer_test},
  1484      {"io",          1, st_do_io},
  1485  
  1486      {"insert",      1, do_insert},
  1487      {"replay",      1, do_replay},
  1488  
  1489      {"speed",       1, do_speed_tests},
  1490      {"speed2",      1, do_speed_test2},
  1491      {"show",        0, st_do_show},
  1492      {"work",        1, st_do_work},
  1493      {"test",        1, do_test},
  1494  
  1495      {0, 0}
  1496    };
  1497    int rc;                         /* Return Code */
  1498    int iFunc;                      /* Index into aTest[] */
  1499  
  1500    int nLeakAlloc = 0;             /* Allocations leaked by lsm */
  1501    int nLeakByte = 0;              /* Bytes leaked by lsm */
  1502  
  1503  #ifdef LSM_DEBUG_MEM
  1504    FILE *pReport = 0;              /* lsm malloc() report file */
  1505    const char *zReport = "malloc.txt generated";
  1506  #else
  1507    const char *zReport = "malloc.txt NOT generated";
  1508  #endif
  1509  
  1510    testMallocInstall(tdb_lsm_env());
  1511  
  1512    if( argc<2 ){
  1513      testPrintError("Usage: %s sub-command ?args...?\n", argv[0]);
  1514      return -1;
  1515    }
  1516  
  1517    /* Initialize error reporting */
  1518    testErrorInit(argc, argv);
  1519  
  1520    /* Initialize PRNG system */
  1521    testPrngInit();
  1522  
  1523    rc = testArgSelect(aTest, "sub-command", argv[1], &iFunc);
  1524    if( rc==0 ){
  1525      rc = aTest[iFunc].xFunc(argc-2, &argv[2]);
  1526    }
  1527  
  1528  #ifdef LSM_DEBUG_MEM
  1529    pReport = fopen("malloc.txt", "w");
  1530    testMallocCheck(tdb_lsm_env(), &nLeakAlloc, &nLeakByte, pReport);
  1531    fclose(pReport);
  1532  #else
  1533    testMallocCheck(tdb_lsm_env(), &nLeakAlloc, &nLeakByte, 0);
  1534  #endif
  1535  
  1536    if( nLeakAlloc ){
  1537      testPrintError("Leaked %d bytes in %d allocations (%s)\n", 
  1538          nLeakByte, nLeakAlloc, zReport
  1539      );
  1540      if( rc==0 ) rc = -1;
  1541    }
  1542    testMallocUninstall(tdb_lsm_env());
  1543  
  1544    if( aTest[iFunc].bRusageReport ){
  1545      lsmtest_rusage_report();
  1546    }
  1547    return rc;
  1548  }