10 #define testSetError(rc) testSetErrorFunc(rc, pRc, __FILE__, __LINE__)
11 static void testSetErrorFunc(int rc
, int *pRc
, const char *zFile
, int iLine
){
14 fprintf(stderr
, "FAILED (%s:%d) rc=%d ", zFile
, iLine
, rc
);
19 static int lsm_memcmp(u8
*a
, u8
*b
, int c
){
22 if( a
[i
]!=b
[i
] ) return a
[i
] - b
[i
];
28 ** A test utility function.
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 */
41 static int nCall
= 0; nCall
++;
43 rc
= tdb_fetch(pDb
, pKey
, nKey
, &pDbVal
, &nDbVal
);
45 if( rc
==0 && (nVal
!=nDbVal
|| (nVal
>0 && lsm_memcmp(pVal
, pDbVal
, nVal
))) ){
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 */
61 rc
= tdb_write(pDb
, pKey
, nKey
, pVal
, nVal
);
66 TestDb
*pDb
, /* Database handle */
67 void *pKey
, int nKey
, /* Key to query database for */
68 int *pRc
/* IN/OUT: Error code */
72 *pRc
= rc
= tdb_delete(pDb
, pKey
, nKey
);
77 TestDb
*pDb
, /* Database handle */
78 void *pKey1
, int nKey1
,
79 void *pKey2
, int nKey2
,
80 int *pRc
/* IN/OUT: Error code */
84 *pRc
= rc
= tdb_delete_range(pDb
, pKey1
, nKey1
, pKey2
, nKey2
);
89 void testBegin(TestDb
*pDb
, int iTrans
, int *pRc
){
92 rc
= tdb_begin(pDb
, iTrans
);
96 void testCommit(TestDb
*pDb
, int iTrans
, int *pRc
){
99 rc
= tdb_commit(pDb
, iTrans
);
104 static void testRollback(TestDb
*pDb
, int iTrans
, int *pRc
){
107 rc
= tdb_rollback(pDb
, iTrans
);
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 */
119 int nVal
= (zVal
? strlen(zVal
) : 0);
120 testWrite(pDb
, (void *)zKey
, strlen(zKey
), (void *)zVal
, nVal
, pRc
);
124 static void testDeleteStr(TestDb
*pDb
, const char *zKey
, int *pRc
){
125 testDelete(pDb
, (void *)zKey
, strlen(zKey
), pRc
);
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 */
134 int nVal
= (zVal
? strlen(zVal
) : 0);
135 testFetch(pDb
, (void *)zKey
, strlen(zKey
), (void *)zVal
, nVal
, pRc
);
138 void testFetchCompare(
141 void *pKey
, int nKey
,
150 static int nCall
= 0;
153 rc
= tdb_fetch(pControl
, pKey
, nKey
, &pDbVal1
, &nDbVal1
);
156 rc
= tdb_fetch(pDb
, pKey
, nKey
, &pDbVal2
, &nDbVal2
);
160 && (nDbVal1
!=nDbVal2
|| (nDbVal1
>0 && memcmp(pDbVal1
, pDbVal2
, nDbVal1
)))
166 typedef struct ScanResult ScanResult
;
173 void *pKey1
; int nKey1
;
174 void *pKey2
; int nKey2
;
181 static int keyCompare(void *pKey1
, int nKey1
, void *pKey2
, int nKey2
){
183 res
= memcmp(pKey1
, pKey2
, MIN(nKey1
, nKey2
));
190 int test_scan_debug
= 0;
192 static void scanCompareCb(
194 void *pKey
, int nKey
,
197 ScanResult
*p
= (ScanResult
*)pCtx
;
198 u8
*aKey
= (u8
*)pKey
;
199 u8
*aVal
= (u8
*)pVal
;
202 if( test_scan_debug
){
203 printf("%d: %.*s\n", p
->nRow
, nKey
, (char *)pKey
);
207 if( test_scan_debug
) printf("%.20s\n", (char *)pVal
);
211 /* Check tdb_fetch() matches */
213 testFetch(p
->pDb
, pKey
, nKey
, pVal
, nVal
, &rc
);
217 /* Update the checksum data */
219 for(i
=0; i
<nKey
; i
++){
220 p
->cksum1
+= ((int)aKey
[i
] << (i
&0x0F));
221 p
->cksum2
+= p
->cksum1
;
223 for(i
=0; i
<nVal
; i
++){
224 p
->cksum1
+= ((int)aVal
[i
] << (i
&0x0F));
225 p
->cksum2
+= p
->cksum1
;
228 /* Check that the delivered row is not out of order. */
229 if( nKey
<(int)sizeof(p
->aPrevKey
) ){
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",
240 memcpy(p
->aPrevKey
, pKey
, MIN(p
->nPrevKey
, nKey
));
243 /* Check that the delivered row is within range. */
245 (memcmp(p
->pKey1
, pKey
, MIN(p
->nKey1
, nKey
))>0)
246 || (memcmp(p
->pKey1
, pKey
, MIN(p
->nKey1
, nKey
))==0 && p
->nKey1
>nKey
)
248 testPrintError("Returned key too small at %s:%d\n", __FILE__
, __LINE__
);
251 (memcmp(p
->pKey2
, pKey
, MIN(p
->nKey2
, nKey
))<0)
252 || (memcmp(p
->pKey2
, pKey
, MIN(p
->nKey2
, nKey
))==0 && p
->nKey2
<nKey
)
254 testPrintError("Returned key too large at %s:%d\n", __FILE__
, __LINE__
);
260 ** Scan the contents of the two databases. Check that they match.
262 void testScanCompare(
263 TestDb
*pDb1
, /* Control (trusted) database */
264 TestDb
*pDb2
, /* Database being tested */
266 void *pKey1
, int nKey1
,
267 void *pKey2
, int nKey2
,
270 static int nCall
= 0; nCall
++;
274 void *pRes1
= (void *)&res1
;
275 void *pRes2
= (void *)&res2
;
277 memset(&res1
, 0, sizeof(ScanResult
));
278 memset(&res2
, 0, sizeof(ScanResult
));
281 res1
.nKey1
= nKey1
; res1
.pKey1
= pKey1
;
282 res1
.nKey2
= nKey2
; res1
.pKey2
= pKey2
;
283 res1
.bReverse
= bReverse
;
285 res2
.nKey1
= nKey1
; res2
.pKey1
= pKey1
;
286 res2
.nKey2
= nKey2
; res2
.pKey2
= pKey2
;
287 res2
.bReverse
= bReverse
;
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");
294 if( res1
.nRow
!=res2
.nRow
295 || res1
.cksum1
!=res2
.cksum1
296 || res1
.cksum2
!=res2
.cksum2
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
);
306 void testClose(TestDb
**ppDb
){
311 TestDb
*testOpen(const char *zSystem
, int bClear
, int *pRc
){
315 rc
= tdb_open(zSystem
, 0, bClear
, &pDb
);
324 void testReopen(TestDb
**ppDb
, int *pRc
){
327 zLib
= tdb_library_name(*ppDb
);
329 *pRc
= tdb_open(zLib
, 0, 0, ppDb
);
335 static void testSystemSelect(const char *zSys
, int *piSel
, int *pRc
){
337 struct SysName
{ const char *zName
; } *aName
;
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
);
347 *pRc
= testArgSelect(aName
, "db", zSys
, piSel
);
353 char *testMallocVPrintf(const char *zFormat
, va_list ap
){
359 nByte
= vsnprintf(0, 0, zFormat
, copy
);
363 zRet
= (char *)testMalloc(nByte
+1);
364 vsnprintf(zRet
, nByte
+1, zFormat
, ap
);
368 char *testMallocPrintf(const char *zFormat
, ...){
372 va_start(ap
, zFormat
);
373 zRet
= testMallocVPrintf(zFormat
, ap
);
381 ** A wrapper around malloc(3).
383 ** This function should be used for all allocations made by test procedures.
384 ** It has the following properties:
386 ** * Test code may assume that allocations may not fail.
387 ** * Returned memory is always zeroed.
389 ** Allocations made using testMalloc() should be freed using testFree().
391 void *testMalloc(int n
){
392 u8
*p
= (u8
*)malloc(n
+ 8);
398 void *testMallocCopy(void *pCopy
, int nByte
){
399 void *pRet
= testMalloc(nByte
);
400 memcpy(pRet
, pCopy
, nByte
);
404 void *testRealloc(void *ptr
, int n
){
406 u8
*p
= (u8
*)ptr
- 8;
407 int nOrig
= *(int*)p
;
408 p
= (u8
*)realloc(p
, n
+8);
410 memset(&p
[8+nOrig
], 0, n
-nOrig
);
415 return testMalloc(n
);
419 ** Free an allocation made by an earlier call to testMalloc().
421 void testFree(void *ptr
){
423 u8
*p
= (u8
*)ptr
- 8;
424 memset(p
, 0x55, *(int*)p
+ 8);
430 ** String zPattern contains a glob pattern. Return true if zStr matches
431 ** the pattern, or false if it does not.
433 int testGlobMatch(const char *zPattern
, const char *zStr
){
437 while( zPattern
[i
] ){
438 char p
= zPattern
[i
];
440 if( p
=='*' || p
=='%' ){
442 if( testGlobMatch(&zPattern
[i
+1], &zStr
[j
]) ) return 1;
447 if( zStr
[j
]==0 || (p
!='?' && p
!=zStr
[j
]) ){
456 return (zPattern
[i
]==0 && zStr
[j
]==0);
460 ** End of test utilities
461 **************************************************************************/
463 int do_test(int nArg
, char **azArg
){
467 const char *zPattern
= 0;
470 testPrintError("Usage: test ?PATTERN?\n");
477 for(j
=0; tdb_system_name(j
); j
++){
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
);
492 test_oom(zPattern
, &rc
);
496 test_api(zPattern
, &rc
);
500 do_crash_test(zPattern
, &rc
);
504 do_writer_crash_test(zPattern
, &rc
);
510 static lsm_db
*configure_lsm_db(TestDb
*pDb
){
514 tdb_lsm_config_str(pDb
, "mmap=1 autowork=1 automerge=4 worker_automerge=4");
519 typedef struct WriteHookEvent WriteHookEvent
;
520 struct WriteHookEvent
{
525 WriteHookEvent prev
= {0, 0, 0};
527 static void flushPrev(FILE *pOut
){
529 fprintf(pOut
, "w %s %lld %d %d\n", "d", prev
.iOff
, prev
.nData
, prev
.nUs
);
535 static void do_speed_write_hook2(
542 FILE *pOut
= (FILE *)pCtx
;
545 if( prev
.nData
&& nData
&& iOff
==prev
.iOff
+prev
.nData
){
551 fprintf(pOut
, "s %s 0 0 %d\n", (bLog
? "l" : "d"), nUs
);
572 static void print_speed_test_help(){
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"
580 " Keys are $keysize bytes in size. Values are $valsize bytes in size\n"
581 " Both keys and values are pseudo-randomly generated\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"
596 int do_speed_test2(int nArg
, char **azArg
){
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},
623 DatasourceDefn defn
= { TEST_DATASOURCE_RANDOM
, 0, 0, 0, 0 };
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());
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
;
639 /* Process the command line switches. */
640 for(i
=0; i
<nArg
; i
+=2){
642 rc
= testArgSelect(aOpt
, "switch", azArg
[i
], &iSel
);
646 if( aOpt
[iSel
].eVal
==-2 ){
647 print_speed_test_help();
651 testPrintError("option %s requires an argument\n", aOpt
[iSel
].zOpt
);
654 if( aOpt
[iSel
].eVal
>=0 ){
655 aParam
[aOpt
[iSel
].eVal
] = atoi(azArg
[i
+1]);
657 zSystem
= azArg
[i
+1];
660 for(j
=0; zSystem
[j
]; j
++){
661 if( zSystem
[j
]=='=' ) bLsm
= 1;
668 for(i
=0; i
<ArraySize(aOpt
); i
++){
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
);
679 defn
.nMinKey
= defn
.nMaxKey
= aParam
[ST_KEYSIZE
];
680 defn
.nMinVal
= defn
.nMaxVal
= aParam
[ST_VALSIZE
];
681 pData
= testDatasourceNew(&defn
);
683 if( aParam
[ST_WRITE
]==0 ){
688 rc
= tdb_lsm_open(zSystem
, "testdb.lsm", !bReadonly
, &pDb
);
690 pDb
= testOpen(zSystem
, !bReadonly
, &rc
);
692 if( rc
!=0 ) return rc
;
694 nContent
= testCountDatabase(pDb
);
698 pLog
= fopen("/tmp/speed.log", "w");
699 tdb_lsm_write_hook(pDb
, do_speed_write_hook2
, (void *)pLog
);
702 for(i
=0; i
<aParam
[ST_REPEAT
] && rc
==0; i
++){
703 int msWrite
, msFetch
;
705 int nWrite
= aParam
[ST_WRITE
];
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
);
716 msWrite
= testTimeGet();
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));
725 if( aParam
[ST_FETCH
] ){
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
;
731 testDatasourceFetch(pDb
, pData
, iKey
, &rc
);
733 void *pKey
; int nKey
; /* Database key to query for */
734 void *pVal
; int nVal
; /* Result of query */
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;
742 if( aParam
[ST_TRANS
] ) testCommit(pDb
, 0, &rc
);
743 msFetch
= testTimeGet();
748 if( i
==(aParam
[ST_REPEAT
]-1) ){
751 msWrite
+= testTimeGet();
754 printf("%d %d %d\n", i
, msWrite
, msFetch
);
759 testDatasourceFree(pData
);
768 int do_speed_tests(int nArg
, char **azArg
){
771 const char *zLibrary
;
774 { "sqlite3", "black" },
775 { "leveldb", "blue" },
777 { "lsm_mt2", "orange" },
778 { "lsm_mt3", "purple" },
779 { "kyotocabinet", "green" },
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 */
794 int *aTime
; /* INSERT timing data */
795 int *aWrite
; /* Writes per nStep inserts */
796 int *aSelTime
; /* SELECT timing data */
800 /* File to write gnuplot script to. */
801 const char *zOut
= "lsmtest_speed.gnuplot";
805 testMallocUninstall(tdb_lsm_env());
807 for(i
=0; i
<nArg
; i
++){
817 { "kyotocabinet" , 0},
826 rc
= testArgSelect(aOpt
, "argument", azArg
[i
], &iSel
);
829 if( aOpt
[iSel
].isSwitch
){
833 testPrintError("option %s requires an argument\n", aOpt
[iSel
].zOpt
);
836 if( aOpt
[iSel
].isSwitch
==1 ){
837 nRow
= atoi(azArg
[i
]);
839 if( aOpt
[iSel
].isSwitch
==2 ){
840 nSleep
= atoi(azArg
[i
]);
842 if( aOpt
[iSel
].isSwitch
==3 ){
847 } aMode
[] = {{"ro", 1, 0} , {"rw", 1, 1}, {"wo", 0, 1}, {0, 0, 0}};
849 rc
= testArgSelect(aMode
, "option", azArg
[i
], &iMode
);
851 doReadTest
= aMode
[iMode
].doReadTest
;
852 doWriteTest
= aMode
[iMode
].doWriteTest
;
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. */
861 rc
= testArgSelect(aOpt
, "system", azArg
[i
], &iSel
);
863 sys_mask
|= (1<<iSel
);
867 if( sys_mask
==0 ) sys_mask
= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3);
868 nRow
= MAX(nRow
, 100000);
871 nSelTest
= (nSelStep
> 100000) ? 100000 : nSelStep
;
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
);
877 /* This loop collects the INSERT speed data. */
879 printf("Writing output to file \"%s\".\n", zOut
);
881 for(j
=0; aSys
[j
].zLibrary
; j
++){
883 TestDb
*pDb
; /* Database being tested */
887 if( ((1<<j
)&sys_mask
)==0 ) continue;
888 if( bSleep
&& nSleep
) sqlite3_sleep(nSleep
);
891 testCaseBegin(&rc
, 0, "speed.insert.%s", aSys
[j
].zLibrary
);
893 rc
= tdb_open(aSys
[j
].zLibrary
, 0, 1, &pDb
);
896 pLsm
= configure_lsm_db(pDb
);
898 pLog
= fopen("/tmp/speed.log", "w");
899 tdb_lsm_write_hook(pDb
, do_speed_write_hook2
, (void *)pLog
);
903 for(i
=0; i
<nRow
; i
+=nStep
){
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
));
915 aTime
[(j
*nRow
+i
)/nStep
] = testTimeGet();
916 if( pLsm
) lsm_info(pLsm
, LSM_INFO_NWRITE
, &nWrite2
);
917 aWrite
[i
/nStep
] = nWrite2
- nWrite1
;
921 if( pLog
) fclose(pLog
);
926 /* This loop collects the SELECT speed data. */
928 for(j
=0; aSys
[j
].zLibrary
; j
++){
930 TestDb
*pDb
; /* Database being tested */
932 if( ((1<<j
)&sys_mask
)==0 ) continue;
933 if( bSleep
&& nSleep
) sqlite3_sleep(nSleep
);
936 testCaseBegin(&rc
, 0, "speed.select.%s", aSys
[j
].zLibrary
);
939 rc
= tdb_open(aSys
[j
].zLibrary
, 0, 1, &pDb
);
941 configure_lsm_db(pDb
);
943 for(i
=0; i
<nRow
; i
+=nSelStep
){
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
));
956 for(iSel
=0; iSel
<nSelTest
; iSel
++){
960 u32 aKey
[4]; /* 16-byte key */
962 iKey
= testPrngValue(iSel
) % (i
+nSelStep
);
963 testPrngArray(iKey
, aKey
, ArraySize(aKey
));
964 rc
= tdb_fetch(pDb
, aKey
, sizeof(aKey
), &pDummy
, &nDummy
);
966 aSelTime
[(j
*nRow
+i
)/nSelStep
] = testTimeGet();
967 tdb_fetch(pDb
, 0, 0, 0, 0);
973 rc
= tdb_open(aSys
[j
].zLibrary
, 0, 0, &pDb
);
974 configure_lsm_db(pDb
);
977 for(iSel
=0; rc
==LSM_OK
&& iSel
<nSelTest
; iSel
++){
981 u32 aKey
[4]; /* 16-byte key */
983 u32 aVal
[25]; /* 100 byte value */
986 testCaseProgress(iSel
, nSelTest
, testCaseNDot(), &iDot
);
988 iKey
= testPrngValue(iSel
) % nRow
;
989 testPrngArray(iKey
, aKey
, ArraySize(aKey
));
990 rc
= tdb_fetch(pDb
, aKey
, sizeof(aKey
), &pDummy
, &nDummy
);
993 testPrngArray(iKey
, aVal
, ArraySize(aVal
));
994 assert( nDummy
==100 && memcmp(aVal
, pDummy
, 100)==0 );
997 if( rc
!=LSM_OK
) return rc
;
1000 tdb_fetch(pDb
, 0, 0, 0, 0);
1002 printf("%s: %d selects/second\n",
1003 aSys
[j
].zLibrary
, (int)((double)nSelTest
*1000.0/t
)
1014 FILE *pOut
= fopen(zOut
, "w");
1016 printf("fopen(\"%s\", \"w\"): %s\n", zOut
, strerror(errno
));
1020 fprintf(pOut
, "set xlabel \"Rows Inserted\"\n");
1021 fprintf(pOut
, "set ylabel \"Inserts per second\"\n");
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");
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 ");
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
1042 fprintf(pOut
, ", \"-\" ti \"%s SELECT\" "
1043 "axis x1y2 with points lw 3 lc rgb \"%s\""
1044 , zLib
, aSys
[j
].zColor
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\""
1058 fprintf(pOut
, "\n");
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
);
1068 fprintf(pOut
, "end\n");
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
);
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
);
1081 fprintf(pOut
, "end\n");
1085 fprintf(pOut
, "pause -1\n");
1092 testMallocInstall(tdb_lsm_env());
1097 ** Usage: lsmtest random ?N?
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.
1103 ** This was added to verify that the PRNG values do not change between
1104 ** runs of the lsmtest program.
1106 int do_random_tests(int nArg
, char **azArg
){
1111 }else if( nArg
==1 ){
1112 nRand
= atoi(azArg
[0]);
1114 testPrintError("Usage: random ?N?\n");
1117 for(i
=0; i
<nRand
; i
++){
1118 printf("0x%x\n", testPrngValue(i
));
1123 static int testFormatSize(char *aBuf
, int nBuf
, i64 nByte
){
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)));
1130 res
= snprintf(aBuf
, nBuf
, "%dM", (int)(nByte
/(1<<20)));
1135 static i64
testReadSize(char *z
){
1157 return nMul
* (i64
)atoi(z
);
1161 ** Usage: lsmtest writespeed FILESIZE BLOCKSIZE SYNCSIZE
1163 static int do_writer_test(int nArg
, char **azArg
){
1170 char aBlockSize
[32];
1181 /* How long to sleep before running a trial (in ms). */
1183 const int nSleep
= 10000;
1185 const int nSleep
= 0;
1188 testPrintUsage("FILESIZE BLOCKSIZE SYNCSIZE");
1192 filesize
= testReadSize(azArg
[0]);
1193 blocksize
= testReadSize(azArg
[1]);
1194 syncsize
= testReadSize(azArg
[2]);
1196 nBlock
= (int)(filesize
/ blocksize
);
1197 nSize
= (int)blocksize
;
1198 nSync
= (int)(syncsize
/ blocksize
);
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
++){
1205 u32 a
= testPrngValue(i
);
1206 u32 b
= testPrngValue(a
);
1210 aOrder
[a
] = aOrder
[b
];
1214 testFormatSize(aFilesize
, sizeof(aFilesize
), (i64
)nBlock
* (i64
)nSize
);
1215 testFormatSize(aBlockSize
, sizeof(aFilesize
), nSize
);
1217 printf("Testing writing a %s file using %s blocks. ", aFilesize
, aBlockSize
);
1219 printf("Sync after each block.\n");
1221 printf("Sync after each %d blocks.\n", nSync
);
1224 printf("Preparing file... ");
1226 unlink("writer.out");
1227 fd
= open("writer.out", O_RDWR
|O_CREAT
|_O_BINARY
, 0664);
1229 testPrintError("open(): %d - %s\n", errno
, strerror(errno
));
1233 for(i
=0; i
<nBlock
; i
++){
1235 memset(aPage
, i
&0xFF, nPage
);
1236 for(iPg
=0; iPg
<(nSize
/nPage
); iPg
++){
1237 write(fd
, aPage
, nPage
);
1241 printf("ok (%d ms)\n", testTimeGet());
1246 sqlite3_sleep(nSleep
);
1247 printf("Now writing sequentially... ");
1250 lseek(fd
, 0, SEEK_SET
);
1252 for(j
=0; j
<nBlock
; j
++){
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
);
1262 printf("%d ms\n", ms
);
1263 sqlite3_sleep(nSleep
);
1264 printf("Now in an arbitrary order... ");
1268 for(j
=0; j
<nBlock
; j
++){
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
);
1279 printf("%d ms\n", ms
);
1289 static void do_insert_work_hook(lsm_db
*db
, void *p
){
1291 lsm_info(db
, LSM_INFO_DB_STRUCTURE
, &z
);
1295 lsm_free(lsm_get_env(db
), z
);
1298 unused_parameter(p
);
1301 typedef struct InsertWriteHook InsertWriteHook
;
1302 struct InsertWriteHook
{
1309 static void flushHook(InsertWriteHook
*pHook
){
1311 fprintf(pHook
->pOut
, "write %s %d %d\n",
1312 (pHook
->bLog
? "log" : "db"), (int)pHook
->iOff
, pHook
->nData
1315 fflush(pHook
->pOut
);
1319 static void do_insert_write_hook(
1326 InsertWriteHook
*pHook
= (InsertWriteHook
*)pCtx
;
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
)
1336 pHook
->nData
+= nData
;
1341 pHook
->nData
= nData
;
1345 static int do_replay(int nArg
, char **azArg
){
1356 testPrintError("Usage: replay WRITELOG FILE\n");
1360 if( strcmp(azArg
[0], "-")==0 ){
1363 pClose
= pInput
= fopen(azArg
[0], "r");
1366 pEnv
= tdb_lsm_env();
1367 rc
= pEnv
->xOpen(pEnv
, zDb
, 0, &pOut
);
1368 if( rc
!=LSM_OK
) return rc
;
1370 while( feof(pInput
)==0 ){
1372 fgets(zLine
, sizeof(zLine
)-1, pInput
);
1373 zLine
[sizeof(zLine
)-1] = '\0';
1375 if( 0==memcmp("sync db", zLine
, 7) ){
1376 rc
= pEnv
->xSync(pOut
);
1382 nMatch
= sscanf(zLine
, "write db %d %d", &iOff
, &nData
);
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
));
1393 if( pClose
) fclose(pClose
);
1399 static int do_insert(int nArg
, char **azArg
){
1400 const char *zDb
= "lsm";
1404 const int nRow
= 1 * 1000 * 1000;
1406 DatasourceDefn defn
= { TEST_DATASOURCE_RANDOM
, 8, 15, 80, 150 };
1407 Datasource
*pData
= 0;
1410 testPrintError("Usage: insert ?DATABASE?\n");
1413 if( nArg
==1 ){ zDb
= azArg
[0]; }
1415 testMallocUninstall(tdb_lsm_env());
1416 for(i
=0; zDb
[i
] && zDb
[i
]!='='; i
++);
1418 rc
= tdb_lsm_open(zDb
, "testdb.lsm", 1, &pDb
);
1420 rc
= tdb_open(zDb
, 0, 1, &pDb
);
1424 testPrintError("Error opening db \"%s\": %d\n", zDb
, rc
);
1426 InsertWriteHook hook
;
1427 memset(&hook
, 0, sizeof(hook
));
1428 hook
.pOut
= fopen("writelog.txt", "w");
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
);
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
);
1443 testDatasourceFree(pData
);
1448 testMallocInstall(tdb_lsm_env());
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
); }
1458 #include <sys/time.h>
1459 #include <sys/resource.h>
1461 static void lsmtest_rusage_report(void){
1463 memset(&r
, 0, sizeof(r
));
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
1471 static void lsmtest_rusage_report(void){
1476 int main(int argc
, char **argv
){
1480 int (*xFunc
)(int, char **);
1482 {"random", 1, do_random_tests
},
1483 {"writespeed", 1, do_writer_test
},
1484 {"io", 1, st_do_io
},
1486 {"insert", 1, do_insert
},
1487 {"replay", 1, do_replay
},
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
},
1497 int rc
; /* Return Code */
1498 int iFunc
; /* Index into aTest[] */
1500 int nLeakAlloc
= 0; /* Allocations leaked by lsm */
1501 int nLeakByte
= 0; /* Bytes leaked by lsm */
1503 #ifdef LSM_DEBUG_MEM
1504 FILE *pReport
= 0; /* lsm malloc() report file */
1505 const char *zReport
= "malloc.txt generated";
1507 const char *zReport
= "malloc.txt NOT generated";
1510 testMallocInstall(tdb_lsm_env());
1513 testPrintError("Usage: %s sub-command ?args...?\n", argv
[0]);
1517 /* Initialize error reporting */
1518 testErrorInit(argc
, argv
);
1520 /* Initialize PRNG system */
1523 rc
= testArgSelect(aTest
, "sub-command", argv
[1], &iFunc
);
1525 rc
= aTest
[iFunc
].xFunc(argc
-2, &argv
[2]);
1528 #ifdef LSM_DEBUG_MEM
1529 pReport
= fopen("malloc.txt", "w");
1530 testMallocCheck(tdb_lsm_env(), &nLeakAlloc
, &nLeakByte
, pReport
);
1533 testMallocCheck(tdb_lsm_env(), &nLeakAlloc
, &nLeakByte
, 0);
1537 testPrintError("Leaked %d bytes in %d allocations (%s)\n",
1538 nLeakByte
, nLeakAlloc
, zReport
1540 if( rc
==0 ) rc
= -1;
1542 testMallocUninstall(tdb_lsm_env());
1544 if( aTest
[iFunc
].bRusageReport
){
1545 lsmtest_rusage_report();