1 /*
2 ** 2016-12-28
3 **
4 ** The author disclaims copyright to this source code.  In place of
5 ** a legal notice, here is a blessing:
6 **
7 **    May you do good and not evil.
8 **    May you find forgiveness for yourself and forgive others.
9 **    May you share freely, never taking more than you give.
10 **
11 *************************************************************************
12 **
13 ** This file implements "key-value" performance test for SQLite.  The
14 ** purpose is to compare the speed of SQLite for accessing large BLOBs
15 ** versus reading those same BLOB values out of individual files in the
16 ** filesystem.
17 **
18 ** Run "kvtest" with no arguments for on-line help, or see comments below.
19 **
20 ** HOW TO COMPILE:
21 **
22 ** (1) Gather this source file and a recent SQLite3 amalgamation with its
23 **     header into the working directory.  You should have:
24 **
25 **          kvtest.c       >--- this file
26 **          sqlite3.c      \___ SQLite
27 **          sqlite3.h      /    amlagamation & header
28 **
29 ** (2) Run you compiler against the two C source code files.
30 **
31 **    (a) On linux or mac:
32 **
33 **        OPTS="-DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION"
34 **        gcc -Os -I. $OPTS kvtest.c sqlite3.c -o kvtest
35 **
36 **             The $OPTS options can be omitted.  The $OPTS merely omit
37 **             the need to link against -ldl and -lpthread, or whatever
38 **             the equivalent libraries are called on your system.
39 **
40 **    (b) Windows with MSVC:
41 **
42 **        cl -I. kvtest.c sqlite3.c
43 **
44 ** USAGE:
45 **
46 ** (1) Create a test database by running "kvtest init" with appropriate
47 **     options.  See the help message for available options.
48 **
49 ** (2) Construct the corresponding pile-of-files database on disk using
50 **     the "kvtest export" command.
51 **
52 ** (3) Run tests using "kvtest run" against either the SQLite database or
53 **     the pile-of-files database and with appropriate options.
54 **
55 ** For example:
56 **
57 **       ./kvtest init x1.db --count 100000 --size 10000
58 **       mkdir x1
59 **       ./kvtest export x1.db x1
60 **       ./kvtest run x1.db --count 10000 --max-id 1000000
61 **       ./kvtest run x1 --count 10000 --max-id 1000000
62 */
63 static const char zHelp[] =
64 "Usage: kvtest COMMAND ARGS...\n"
65 "\n"
66 "   kvtest init DBFILE --count N --size M --pagesize X\n"
67 "\n"
68 "        Generate a new test database file named DBFILE containing N\n"
69 "        BLOBs each of size M bytes.  The page size of the new database\n"
70 "        file will be X.  Additional options:\n"
71 "\n"
72 "           --variance V           Randomly vary M by plus or minus V\n"
73 "\n"
74 "   kvtest export DBFILE DIRECTORY [--tree]\n"
75 "\n"
76 "        Export all the blobs in the kv table of DBFILE into separate\n"
77 "        files in DIRECTORY.  DIRECTORY is created if it does not previously\n"
78 "        exist.  If the --tree option is used, then the blobs are written\n"
79 "        into a hierarchy of directories, using names like 00/00/00,\n"
80 "        00/00/01, 00/00/02, and so forth.  Without the --tree option, all\n"
81 "        files are in the top-level directory with names like 000000, 000001,\n"
82 "        000002, and so forth.\n"
83 "\n"
84 "   kvtest stat DBFILE [options]\n"
85 "\n"
86 "        Display summary information about DBFILE.  Options:\n"
87 "\n"
88 "           --vacuum               Run VACUUM on the database file\n"
89 "\n"
90 "   kvtest run DBFILE [options]\n"
91 "\n"
92 "        Run a performance test.  DBFILE can be either the name of a\n"
93 "        database or a directory containing sample files.  Options:\n"
94 "\n"
95 "           --asc                  Read blobs in ascending order\n"
96 "           --blob-api             Use the BLOB API\n"
97 "           --cache-size N         Database cache size\n"
98 "           --count N              Read N blobs\n"
99 "           --desc                 Read blobs in descending order\n"
100 "           --fsync                Synchronous file writes\n"
101 "           --integrity-check      Run \"PRAGMA integrity_check\" after test\n"
102 "           --max-id N             Maximum blob key to use\n"
103 "           --mmap N               Mmap as much as N bytes of DBFILE\n"
104 "           --multitrans           Each read or write in its own transaction\n"
105 "           --nocheckpoint         Omit the checkpoint on WAL mode writes\n"
106 "           --nosync               Set \"PRAGMA synchronous=OFF\"\n"
107 "           --jmode MODE           Set MODE journal mode prior to starting\n"
108 "           --random               Read blobs in a random order\n"
109 "           --start N              Start reading with this blob key\n"
110 "           --stats                Output operating stats before exiting\n"
111 "           --update               Do an overwrite test\n"
112 ;
113 
114 /* Reference resources used */
115 #include <stdio.h>
116 #include <stdlib.h>
117 #include <sys/types.h>
118 #include <sys/stat.h>
119 #include <assert.h>
120 #include <string.h>
121 #include "sqlite3.h"
122 
123 #ifndef _WIN32
124 # include <unistd.h>
125 #else
126   /* Provide Windows equivalent for the needed parts of unistd.h */
127 # include <direct.h>
128 # include <io.h>
129 # define R_OK 2
130 # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
131 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
132 # define access _access
133 #endif
134 
135 #if !defined(_MSC_VER)
136 # include <stdint.h>
137 #endif
138 
139 /*
140 ** The following macros are used to cast pointers to integers and
141 ** integers to pointers.  The way you do this varies from one compiler
142 ** to the next, so we have developed the following set of #if statements
143 ** to generate appropriate macros for a wide range of compilers.
144 **
145 ** The correct "ANSI" way to do this is to use the intptr_t type.
146 ** Unfortunately, that typedef is not available on all compilers, or
147 ** if it is available, it requires an #include of specific headers
148 ** that vary from one machine to the next.
149 **
150 ** Ticket #3860:  The llvm-gcc-4.2 compiler from Apple chokes on
151 ** the ((void*)&((char*)0)[X]) construct.  But MSVC chokes on ((void*)(X)).
152 ** So we have to define the macros in different ways depending on the
153 ** compiler.
154 */
155 #if defined(__PTRDIFF_TYPE__)  /* This case should work for GCC */
156 # define SQLITE_INT_TO_PTR(X)  ((void*)(__PTRDIFF_TYPE__)(X))
157 # define SQLITE_PTR_TO_INT(X)  ((sqlite3_int64)(__PTRDIFF_TYPE__)(X))
158 #else
159 # define SQLITE_INT_TO_PTR(X)  ((void*)(intptr_t)(X))
160 # define SQLITE_PTR_TO_INT(X)  ((sqlite3_int64)(intptr_t)(X))
161 #endif
162 
163 /*
164 ** Show thqe help text and quit.
165 */
showHelp(void)166 static void showHelp(void){
167   fprintf(stdout, "%s", zHelp);
168   exit(1);
169 }
170 
171 /*
172 ** Show an error message an quit.
173 */
fatalError(const char * zFormat,...)174 static void fatalError(const char *zFormat, ...){
175   va_list ap;
176   fprintf(stdout, "ERROR: ");
177   va_start(ap, zFormat);
178   vfprintf(stdout, zFormat, ap);
179   va_end(ap);
180   fprintf(stdout, "\n");
181   exit(1);
182 }
183 
184 /*
185 ** Return the value of a hexadecimal digit.  Return -1 if the input
186 ** is not a hex digit.
187 */
hexDigitValue(char c)188 static int hexDigitValue(char c){
189   if( c>='0' && c<='9' ) return c - '0';
190   if( c>='a' && c<='f' ) return c - 'a' + 10;
191   if( c>='A' && c<='F' ) return c - 'A' + 10;
192   return -1;
193 }
194 
195 /*
196 ** Interpret zArg as an integer value, possibly with suffixes.
197 */
integerValue(const char * zArg)198 static int integerValue(const char *zArg){
199   int v = 0;
200   static const struct { char *zSuffix; int iMult; } aMult[] = {
201     { "KiB", 1024 },
202     { "MiB", 1024*1024 },
203     { "GiB", 1024*1024*1024 },
204     { "KB",  1000 },
205     { "MB",  1000000 },
206     { "GB",  1000000000 },
207     { "K",   1000 },
208     { "M",   1000000 },
209     { "G",   1000000000 },
210   };
211   int i;
212   int isNeg = 0;
213   if( zArg[0]=='-' ){
214     isNeg = 1;
215     zArg++;
216   }else if( zArg[0]=='+' ){
217     zArg++;
218   }
219   if( zArg[0]=='0' && zArg[1]=='x' ){
220     int x;
221     zArg += 2;
222     while( (x = hexDigitValue(zArg[0]))>=0 ){
223       v = (v<<4) + x;
224       zArg++;
225     }
226   }else{
227     while( zArg[0]>='0' && zArg[0]<='9' ){
228       v = v*10 + zArg[0] - '0';
229       zArg++;
230     }
231   }
232   for(i=0; i<sizeof(aMult)/sizeof(aMult[0]); i++){
233     if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
234       v *= aMult[i].iMult;
235       break;
236     }
237   }
238   return isNeg? -v : v;
239 }
240 
241 
242 /*
243 ** Check the filesystem object zPath.  Determine what it is:
244 **
245 **    PATH_DIR     A single directory holding many files
246 **    PATH_TREE    A directory hierarchy with files at the leaves
247 **    PATH_DB      An SQLite database
248 **    PATH_NEXIST  Does not exist
249 **    PATH_OTHER   Something else
250 **
251 ** PATH_DIR means all of the separate files are grouped together
252 ** into a single directory with names like 000000, 000001, 000002, and
253 ** so forth.  PATH_TREE means there is a hierarchy of directories so
254 ** that no single directory has too many entries.  The files have names
255 ** like 00/00/00, 00/00/01, 00/00/02 and so forth.  The decision between
256 ** PATH_DIR and PATH_TREE is determined by the presence of a subdirectory
257 ** named "00" at the top-level.
258 */
259 #define PATH_DIR     1
260 #define PATH_TREE    2
261 #define PATH_DB      3
262 #define PATH_NEXIST  0
263 #define PATH_OTHER   99
pathType(const char * zPath)264 static int pathType(const char *zPath){
265   struct stat x;
266   int rc;
267   if( access(zPath,R_OK) ) return PATH_NEXIST;
268   memset(&x, 0, sizeof(x));
269   rc = stat(zPath, &x);
270   if( rc<0 ) return PATH_OTHER;
271   if( S_ISDIR(x.st_mode) ){
272     char *zLayer1 = sqlite3_mprintf("%s/00", zPath);
273     memset(&x, 0, sizeof(x));
274     rc = stat(zLayer1, &x);
275     sqlite3_free(zLayer1);
276     if( rc<0 ) return PATH_DIR;
277     if( S_ISDIR(x.st_mode) ) return PATH_TREE;
278     return PATH_DIR;
279   }
280   if( (x.st_size%512)==0 ) return PATH_DB;
281   return PATH_OTHER;
282 }
283 
284 /*
285 ** Return the size of a file in bytes.  Or return -1 if the
286 ** named object is not a regular file or does not exist.
287 */
fileSize(const char * zPath)288 static sqlite3_int64 fileSize(const char *zPath){
289   struct stat x;
290   int rc;
291   memset(&x, 0, sizeof(x));
292   rc = stat(zPath, &x);
293   if( rc<0 ) return -1;
294   if( !S_ISREG(x.st_mode) ) return -1;
295   return x.st_size;
296 }
297 
298 /*
299 ** A Pseudo-random number generator with a fixed seed.  Use this so
300 ** that the same sequence of "random" numbers are generated on each
301 ** run, for repeatability.
302 */
randInt(void)303 static unsigned int randInt(void){
304   static unsigned int x = 0x333a13cd;
305   static unsigned int y = 0xecb2adea;
306   x = (x>>1) ^ ((1+~(x&1)) & 0xd0000001);
307   y = y*1103515245 + 12345;
308   return x^y;
309 }
310 
311 /*
312 ** Do database initialization.
313 */
initMain(int argc,char ** argv)314 static int initMain(int argc, char **argv){
315   char *zDb;
316   int i, rc;
317   int nCount = 1000;
318   int sz = 10000;
319   int iVariance = 0;
320   int pgsz = 4096;
321   sqlite3 *db;
322   char *zSql;
323   char *zErrMsg = 0;
324 
325   assert( strcmp(argv[1],"init")==0 );
326   assert( argc>=3 );
327   zDb = argv[2];
328   for(i=3; i<argc; i++){
329     char *z = argv[i];
330     if( z[0]!='-' ) fatalError("unknown argument: \"%s\"", z);
331     if( z[1]=='-' ) z++;
332     if( strcmp(z, "-count")==0 ){
333       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
334       nCount = integerValue(argv[++i]);
335       if( nCount<1 ) fatalError("the --count must be positive");
336       continue;
337     }
338     if( strcmp(z, "-size")==0 ){
339       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
340       sz = integerValue(argv[++i]);
341       if( sz<1 ) fatalError("the --size must be positive");
342       continue;
343     }
344     if( strcmp(z, "-variance")==0 ){
345       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
346       iVariance = integerValue(argv[++i]);
347       continue;
348     }
349     if( strcmp(z, "-pagesize")==0 ){
350       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
351       pgsz = integerValue(argv[++i]);
352       if( pgsz<512 || pgsz>65536 || ((pgsz-1)&pgsz)!=0 ){
353         fatalError("the --pagesize must be power of 2 between 512 and 65536");
354       }
355       continue;
356     }
357     fatalError("unknown option: \"%s\"", argv[i]);
358   }
359   rc = sqlite3_open(zDb, &db);
360   if( rc ){
361     fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db));
362   }
363   zSql = sqlite3_mprintf(
364     "DROP TABLE IF EXISTS kv;\n"
365     "PRAGMA page_size=%d;\n"
366     "VACUUM;\n"
367     "BEGIN;\n"
368     "CREATE TABLE kv(k INTEGER PRIMARY KEY, v BLOB);\n"
369     "WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<%d)"
370     " INSERT INTO kv(k,v) SELECT x, randomblob(%d+(random()%%(%d))) FROM c;\n"
371     "COMMIT;\n",
372     pgsz, nCount, sz, iVariance+1
373   );
374   rc = sqlite3_exec(db, zSql, 0, 0, &zErrMsg);
375   if( rc ) fatalError("database create failed: %s", zErrMsg);
376   sqlite3_free(zSql);
377   sqlite3_close(db);
378   return 0;
379 }
380 
381 /*
382 ** Analyze an existing database file.  Report its content.
383 */
statMain(int argc,char ** argv)384 static int statMain(int argc, char **argv){
385   char *zDb;
386   int i, rc;
387   sqlite3 *db;
388   char *zSql;
389   sqlite3_stmt *pStmt;
390   int doVacuum = 0;
391 
392   assert( strcmp(argv[1],"stat")==0 );
393   assert( argc>=3 );
394   zDb = argv[2];
395   for(i=3; i<argc; i++){
396     char *z = argv[i];
397     if( z[0]!='-' ) fatalError("unknown argument: \"%s\"", z);
398     if( z[1]=='-' ) z++;
399     if( strcmp(z, "-vacuum")==0 ){
400       doVacuum = 1;
401       continue;
402     }
403     fatalError("unknown option: \"%s\"", argv[i]);
404   }
405   rc = sqlite3_open(zDb, &db);
406   if( rc ){
407     fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db));
408   }
409   if( doVacuum ){
410     printf("Vacuuming...."); fflush(stdout);
411     sqlite3_exec(db, "VACUUM", 0, 0, 0);
412     printf("       done\n");
413   }
414   zSql = sqlite3_mprintf(
415     "SELECT count(*), min(length(v)), max(length(v)), avg(length(v))"
416     "  FROM kv"
417   );
418   rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
419   if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db));
420   sqlite3_free(zSql);
421   if( sqlite3_step(pStmt)==SQLITE_ROW ){
422     printf("Number of entries:  %8d\n", sqlite3_column_int(pStmt, 0));
423     printf("Average value size: %8d\n", sqlite3_column_int(pStmt, 3));
424     printf("Minimum value size: %8d\n", sqlite3_column_int(pStmt, 1));
425     printf("Maximum value size: %8d\n", sqlite3_column_int(pStmt, 2));
426   }else{
427     printf("No rows\n");
428   }
429   sqlite3_finalize(pStmt);
430   zSql = sqlite3_mprintf("PRAGMA page_size");
431   rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
432   if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db));
433   sqlite3_free(zSql);
434   if( sqlite3_step(pStmt)==SQLITE_ROW ){
435     printf("Page-size:          %8d\n", sqlite3_column_int(pStmt, 0));
436   }
437   sqlite3_finalize(pStmt);
438   zSql = sqlite3_mprintf("PRAGMA page_count");
439   rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
440   if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db));
441   sqlite3_free(zSql);
442   if( sqlite3_step(pStmt)==SQLITE_ROW ){
443     printf("Page-count:         %8d\n", sqlite3_column_int(pStmt, 0));
444   }
445   sqlite3_finalize(pStmt);
446   zSql = sqlite3_mprintf("PRAGMA freelist_count");
447   rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
448   if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db));
449   sqlite3_free(zSql);
450   if( sqlite3_step(pStmt)==SQLITE_ROW ){
451     printf("Freelist-count:     %8d\n", sqlite3_column_int(pStmt, 0));
452   }
453   sqlite3_finalize(pStmt);
454   rc = sqlite3_prepare_v2(db, "PRAGMA integrity_check(10)", -1, &pStmt, 0);
455   if( rc ) fatalError("cannot prepare integrity check: %s", sqlite3_errmsg(db));
456   while( sqlite3_step(pStmt)==SQLITE_ROW ){
457     printf("Integrity-check:    %s\n", sqlite3_column_text(pStmt, 0));
458   }
459   sqlite3_finalize(pStmt);
460   sqlite3_close(db);
461   return 0;
462 }
463 
464 /*
465 **      remember(V,PTR)
466 **
467 ** Return the integer value V.  Also save the value of V in a
468 ** C-language variable whose address is PTR.
469 */
rememberFunc(sqlite3_context * pCtx,int argc,sqlite3_value ** argv)470 static void rememberFunc(
471   sqlite3_context *pCtx,
472   int argc,
473   sqlite3_value **argv
474 ){
475   sqlite3_int64 v;
476   sqlite3_int64 ptr;
477   assert( argc==2 );
478   v = sqlite3_value_int64(argv[0]);
479   ptr = sqlite3_value_int64(argv[1]);
480   *(sqlite3_int64*)SQLITE_INT_TO_PTR(ptr) = v;
481   sqlite3_result_int64(pCtx, v);
482 }
483 
484 /*
485 ** Make sure a directory named zDir exists.
486 */
kvtest_mkdir(const char * zDir)487 static void kvtest_mkdir(const char *zDir){
488 #if defined(_WIN32)
489   (void)mkdir(zDir);
490 #else
491   (void)mkdir(zDir, 0755);
492 #endif
493 }
494 
495 /*
496 ** Export the kv table to individual files in the filesystem
497 */
exportMain(int argc,char ** argv)498 static int exportMain(int argc, char **argv){
499   char *zDb;
500   char *zDir;
501   sqlite3 *db;
502   sqlite3_stmt *pStmt;
503   int rc;
504   int ePathType;
505   int nFN;
506   char *zFN;
507   char *zTail;
508   size_t nWrote;
509   int i;
510 
511   assert( strcmp(argv[1],"export")==0 );
512   assert( argc>=3 );
513   if( argc<4 ) fatalError("Usage: kvtest export DATABASE DIRECTORY [OPTIONS]");
514   zDb = argv[2];
515   zDir = argv[3];
516   kvtest_mkdir(zDir);
517   for(i=4; i<argc; i++){
518     const char *z = argv[i];
519     if( z[0]=='-' && z[1]=='-' ) z++;
520     if( strcmp(z,"-tree")==0 ){
521       zFN = sqlite3_mprintf("%s/00", zDir);
522       kvtest_mkdir(zFN);
523       sqlite3_free(zFN);
524       continue;
525     }
526     fatalError("unknown argument: \"%s\"\n", argv[i]);
527   }
528   ePathType = pathType(zDir);
529   if( ePathType!=PATH_DIR && ePathType!=PATH_TREE ){
530     fatalError("object \"%s\" is not a directory", zDir);
531   }
532   rc = sqlite3_open(zDb, &db);
533   if( rc ){
534     fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db));
535   }
536   rc = sqlite3_prepare_v2(db, "SELECT k, v FROM kv ORDER BY k", -1, &pStmt, 0);
537   if( rc ){
538     fatalError("prepare_v2 failed: %s\n", sqlite3_errmsg(db));
539   }
540   nFN = (int)strlen(zDir);
541   zFN = sqlite3_mprintf("%s/00/00/00.extra---------------------", zDir);
542   if( zFN==0 ){
543     fatalError("malloc failed\n");
544   }
545   zTail = zFN + nFN + 1;
546   while( sqlite3_step(pStmt)==SQLITE_ROW ){
547     int iKey = sqlite3_column_int(pStmt, 0);
548     sqlite3_int64 nData = sqlite3_column_bytes(pStmt, 1);
549     const void *pData = sqlite3_column_blob(pStmt, 1);
550     FILE *out;
551     if( ePathType==PATH_DIR ){
552       sqlite3_snprintf(20, zTail, "%06d", iKey);
553     }else{
554       sqlite3_snprintf(20, zTail, "%02d", iKey/10000);
555       kvtest_mkdir(zFN);
556       sqlite3_snprintf(20, zTail, "%02d/%02d", iKey/10000, (iKey/100)%100);
557       kvtest_mkdir(zFN);
558       sqlite3_snprintf(20, zTail, "%02d/%02d/%02d",
559                        iKey/10000, (iKey/100)%100, iKey%100);
560     }
561     out = fopen(zFN, "wb");
562     nWrote = fwrite(pData, 1, (size_t)nData, out);
563     fclose(out);
564     printf("\r%s   ", zTail); fflush(stdout);
565     if( nWrote!=(size_t)nData ){
566       fatalError("Wrote only %d of %d bytes to %s\n",
567                   (int)nWrote, nData, zFN);
568     }
569   }
570   sqlite3_finalize(pStmt);
571   sqlite3_close(db);
572   sqlite3_free(zFN);
573   printf("\n");
574   return 0;
575 }
576 
577 /*
578 ** Read the content of file zName into memory obtained from sqlite3_malloc64()
579 ** and return a pointer to the buffer. The caller is responsible for freeing
580 ** the memory.
581 **
582 ** If parameter pnByte is not NULL, (*pnByte) is set to the number of bytes
583 ** read.
584 **
585 ** For convenience, a nul-terminator byte is always appended to the data read
586 ** from the file before the buffer is returned. This byte is not included in
587 ** the final value of (*pnByte), if applicable.
588 **
589 ** NULL is returned if any error is encountered. The final value of *pnByte
590 ** is undefined in this case.
591 */
readFile(const char * zName,sqlite3_int64 * pnByte)592 static unsigned char *readFile(const char *zName, sqlite3_int64 *pnByte){
593   FILE *in;               /* FILE from which to read content of zName */
594   sqlite3_int64 nIn;      /* Size of zName in bytes */
595   size_t nRead;           /* Number of bytes actually read */
596   unsigned char *pBuf;    /* Content read from disk */
597 
598   nIn = fileSize(zName);
599   if( nIn<0 ) return 0;
600   in = fopen(zName, "rb");
601   if( in==0 ) return 0;
602   pBuf = sqlite3_malloc64( nIn );
603   if( pBuf==0 ) return 0;
604   nRead = fread(pBuf, (size_t)nIn, 1, in);
605   fclose(in);
606   if( nRead!=1 ){
607     sqlite3_free(pBuf);
608     return 0;
609   }
610   if( pnByte ) *pnByte = nIn;
611   return pBuf;
612 }
613 
614 /*
615 ** Overwrite a file with randomness.  Do not change the size of the
616 ** file.
617 */
updateFile(const char * zName,sqlite3_int64 * pnByte,int doFsync)618 static void updateFile(const char *zName, sqlite3_int64 *pnByte, int doFsync){
619   FILE *out;              /* FILE from which to read content of zName */
620   sqlite3_int64 sz;       /* Size of zName in bytes */
621   size_t nWritten;        /* Number of bytes actually read */
622   unsigned char *pBuf;    /* Content to store on disk */
623   const char *zMode = "wb";   /* Mode for fopen() */
624 
625   sz = fileSize(zName);
626   if( sz<0 ){
627     fatalError("No such file: \"%s\"", zName);
628   }
629   *pnByte = sz;
630   if( sz==0 ) return;
631   pBuf = sqlite3_malloc64( sz );
632   if( pBuf==0 ){
633     fatalError("Cannot allocate %lld bytes\n", sz);
634   }
635   sqlite3_randomness((int)sz, pBuf);
636 #if defined(_WIN32)
637   if( doFsync ) zMode = "wbc";
638 #endif
639   out = fopen(zName, zMode);
640   if( out==0 ){
641     fatalError("Cannot open \"%s\" for writing\n", zName);
642   }
643   nWritten = fwrite(pBuf, 1, (size_t)sz, out);
644   if( doFsync ){
645 #if defined(_WIN32)
646     fflush(out);
647 #else
648     fsync(fileno(out));
649 #endif
650   }
651   fclose(out);
652   if( nWritten!=(size_t)sz ){
653     fatalError("Wrote only %d of %d bytes to \"%s\"\n",
654                (int)nWritten, (int)sz, zName);
655   }
656   sqlite3_free(pBuf);
657 }
658 
659 /*
660 ** Return the current time in milliseconds since the beginning of
661 ** the Julian epoch.
662 */
timeOfDay(void)663 static sqlite3_int64 timeOfDay(void){
664   static sqlite3_vfs *clockVfs = 0;
665   sqlite3_int64 t;
666   if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
667   if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
668     clockVfs->xCurrentTimeInt64(clockVfs, &t);
669   }else{
670     double r;
671     clockVfs->xCurrentTime(clockVfs, &r);
672     t = (sqlite3_int64)(r*86400000.0);
673   }
674   return t;
675 }
676 
677 #ifdef __linux__
678 /*
679 ** Attempt to display I/O stats on Linux using /proc/PID/io
680 */
displayLinuxIoStats(FILE * out)681 static void displayLinuxIoStats(FILE *out){
682   FILE *in;
683   char z[200];
684   sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid());
685   in = fopen(z, "rb");
686   if( in==0 ) return;
687   while( fgets(z, sizeof(z), in)!=0 ){
688     static const struct {
689       const char *zPattern;
690       const char *zDesc;
691     } aTrans[] = {
692       { "rchar: ",                  "Bytes received by read():" },
693       { "wchar: ",                  "Bytes sent to write():"    },
694       { "syscr: ",                  "Read() system calls:"      },
695       { "syscw: ",                  "Write() system calls:"     },
696       { "read_bytes: ",             "Bytes read from storage:"  },
697       { "write_bytes: ",            "Bytes written to storage:" },
698       { "cancelled_write_bytes: ",  "Cancelled write bytes:"    },
699     };
700     int i;
701     for(i=0; i<sizeof(aTrans)/sizeof(aTrans[0]); i++){
702       int n = (int)strlen(aTrans[i].zPattern);
703       if( strncmp(aTrans[i].zPattern, z, n)==0 ){
704         fprintf(out, "%-36s %s", aTrans[i].zDesc, &z[n]);
705         break;
706       }
707     }
708   }
709   fclose(in);
710 }
711 #endif
712 
713 /*
714 ** Display memory stats.
715 */
display_stats(sqlite3 * db,int bReset)716 static int display_stats(
717   sqlite3 *db,                    /* Database to query */
718   int bReset                      /* True to reset SQLite stats */
719 ){
720   int iCur;
721   int iHiwtr;
722   FILE *out = stdout;
723 
724   fprintf(out, "\n");
725 
726   iHiwtr = iCur = -1;
727   sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset);
728   fprintf(out,
729           "Memory Used:                         %d (max %d) bytes\n",
730           iCur, iHiwtr);
731   iHiwtr = iCur = -1;
732   sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset);
733   fprintf(out, "Number of Outstanding Allocations:   %d (max %d)\n",
734           iCur, iHiwtr);
735   iHiwtr = iCur = -1;
736   sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &iCur, &iHiwtr, bReset);
737   fprintf(out,
738       "Number of Pcache Pages Used:         %d (max %d) pages\n",
739       iCur, iHiwtr);
740   iHiwtr = iCur = -1;
741   sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, bReset);
742   fprintf(out,
743           "Number of Pcache Overflow Bytes:     %d (max %d) bytes\n",
744           iCur, iHiwtr);
745   iHiwtr = iCur = -1;
746   sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, bReset);
747   fprintf(out, "Largest Allocation:                  %d bytes\n",
748           iHiwtr);
749   iHiwtr = iCur = -1;
750   sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, bReset);
751   fprintf(out, "Largest Pcache Allocation:           %d bytes\n",
752           iHiwtr);
753 
754   iHiwtr = iCur = -1;
755   sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
756   fprintf(out, "Pager Heap Usage:                    %d bytes\n",
757       iCur);
758   iHiwtr = iCur = -1;
759   sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
760   fprintf(out, "Page cache hits:                     %d\n", iCur);
761   iHiwtr = iCur = -1;
762   sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
763   fprintf(out, "Page cache misses:                   %d\n", iCur);
764   iHiwtr = iCur = -1;
765   sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
766   fprintf(out, "Page cache writes:                   %d\n", iCur);
767   iHiwtr = iCur = -1;
768 
769 #ifdef __linux__
770   displayLinuxIoStats(out);
771 #endif
772 
773   return 0;
774 }
775 
776 /* Blob access order */
777 #define ORDER_ASC     1
778 #define ORDER_DESC    2
779 #define ORDER_RANDOM  3
780 
781 
782 /*
783 ** Run a performance test
784 */
runMain(int argc,char ** argv)785 static int runMain(int argc, char **argv){
786   int eType;                  /* Is zDb a database or a directory? */
787   char *zDb;                  /* Database or directory name */
788   int i;                      /* Loop counter */
789   int rc;                     /* Return code from SQLite calls */
790   int nCount = 1000;          /* Number of blob fetch operations */
791   int nExtra = 0;             /* Extra cycles */
792   int iKey = 1;               /* Next blob key */
793   int iMax = 0;               /* Largest allowed key */
794   int iPagesize = 0;          /* Database page size */
795   int iCache = 1000;          /* Database cache size in kibibytes */
796   int bBlobApi = 0;           /* Use the incremental blob I/O API */
797   int bStats = 0;             /* Print stats before exiting */
798   int eOrder = ORDER_ASC;     /* Access order */
799   int isUpdateTest = 0;       /* Do in-place updates rather than reads */
800   int doIntegrityCk = 0;      /* Run PRAGMA integrity_check after the test */
801   int noSync = 0;             /* Disable synchronous mode */
802   int doFsync = 0;            /* Update disk files synchronously */
803   int doMultiTrans = 0;       /* Each operation in its own transaction */
804   int noCheckpoint = 0;       /* Omit the checkpoint in WAL mode */
805   sqlite3 *db = 0;            /* Database connection */
806   sqlite3_stmt *pStmt = 0;    /* Prepared statement for SQL access */
807   sqlite3_blob *pBlob = 0;    /* Handle for incremental Blob I/O */
808   sqlite3_int64 tmStart;      /* Start time */
809   sqlite3_int64 tmElapsed;    /* Elapsed time */
810   int mmapSize = 0;           /* --mmap N argument */
811   sqlite3_int64 nData = 0;    /* Bytes of data */
812   sqlite3_int64 nTotal = 0;   /* Total data read */
813   unsigned char *pData = 0;   /* Content of the blob */
814   sqlite3_int64 nAlloc = 0;   /* Space allocated for pData[] */
815   const char *zJMode = 0;     /* Journal mode */
816 
817 
818   assert( strcmp(argv[1],"run")==0 );
819   assert( argc>=3 );
820   zDb = argv[2];
821   eType = pathType(zDb);
822   if( eType==PATH_OTHER ) fatalError("unknown object type: \"%s\"", zDb);
823   if( eType==PATH_NEXIST ) fatalError("object does not exist: \"%s\"", zDb);
824   for(i=3; i<argc; i++){
825     char *z = argv[i];
826     if( z[0]!='-' ) fatalError("unknown argument: \"%s\"", z);
827     if( z[1]=='-' ) z++;
828     if( strcmp(z, "-asc")==0 ){
829       eOrder = ORDER_ASC;
830       continue;
831     }
832     if( strcmp(z, "-blob-api")==0 ){
833       bBlobApi = 1;
834       continue;
835     }
836     if( strcmp(z, "-cache-size")==0 ){
837       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
838       iCache = integerValue(argv[++i]);
839       continue;
840     }
841     if( strcmp(z, "-count")==0 ){
842       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
843       nCount = integerValue(argv[++i]);
844       if( nCount<1 ) fatalError("the --count must be positive");
845       continue;
846     }
847     if( strcmp(z, "-desc")==0 ){
848       eOrder = ORDER_DESC;
849       continue;
850     }
851     if( strcmp(z, "-fsync")==0 ){
852       doFsync = 1;
853       continue;
854     }
855     if( strcmp(z, "-integrity-check")==0 ){
856       doIntegrityCk = 1;
857       continue;
858     }
859     if( strcmp(z, "-jmode")==0 ){
860       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
861       zJMode = argv[++i];
862       continue;
863     }
864     if( strcmp(z, "-mmap")==0 ){
865       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
866       mmapSize = integerValue(argv[++i]);
867       if( nCount<0 ) fatalError("the --mmap must be non-negative");
868       continue;
869     }
870     if( strcmp(z, "-max-id")==0 ){
871       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
872       iMax = integerValue(argv[++i]);
873       continue;
874     }
875     if( strcmp(z, "-multitrans")==0 ){
876       doMultiTrans = 1;
877       continue;
878     }
879     if( strcmp(z, "-nocheckpoint")==0 ){
880       noCheckpoint = 1;
881       continue;
882     }
883     if( strcmp(z, "-nosync")==0 ){
884       noSync = 1;
885       continue;
886     }
887     if( strcmp(z, "-random")==0 ){
888       eOrder = ORDER_RANDOM;
889       continue;
890     }
891     if( strcmp(z, "-start")==0 ){
892       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
893       iKey = integerValue(argv[++i]);
894       if( iKey<1 ) fatalError("the --start must be positive");
895       continue;
896     }
897     if( strcmp(z, "-stats")==0 ){
898       bStats = 1;
899       continue;
900     }
901     if( strcmp(z, "-update")==0 ){
902       isUpdateTest = 1;
903       continue;
904     }
905     fatalError("unknown option: \"%s\"", argv[i]);
906   }
907   if( eType==PATH_DB ){
908     /* Recover any prior crashes prior to starting the timer */
909     sqlite3_open(zDb, &db);
910     sqlite3_exec(db, "SELECT rowid FROM sqlite_schema LIMIT 1", 0, 0, 0);
911     sqlite3_close(db);
912     db = 0;
913   }
914   tmStart = timeOfDay();
915   if( eType==PATH_DB ){
916     char *zSql;
917     rc = sqlite3_open(zDb, &db);
918     if( rc ){
919       fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db));
920     }
921     zSql = sqlite3_mprintf("PRAGMA mmap_size=%d", mmapSize);
922     sqlite3_exec(db, zSql, 0, 0, 0);
923     sqlite3_free(zSql);
924     zSql = sqlite3_mprintf("PRAGMA cache_size=%d", iCache);
925     sqlite3_exec(db, zSql, 0, 0, 0);
926     sqlite3_free(zSql);
927     if( noSync ){
928       sqlite3_exec(db, "PRAGMA synchronous=OFF", 0, 0, 0);
929     }
930     pStmt = 0;
931     sqlite3_prepare_v2(db, "PRAGMA page_size", -1, &pStmt, 0);
932     if( sqlite3_step(pStmt)==SQLITE_ROW ){
933       iPagesize = sqlite3_column_int(pStmt, 0);
934     }
935     sqlite3_finalize(pStmt);
936     sqlite3_prepare_v2(db, "PRAGMA cache_size", -1, &pStmt, 0);
937     if( sqlite3_step(pStmt)==SQLITE_ROW ){
938       iCache = sqlite3_column_int(pStmt, 0);
939     }else{
940       iCache = 0;
941     }
942     sqlite3_finalize(pStmt);
943     pStmt = 0;
944     if( zJMode ){
945       zSql = sqlite3_mprintf("PRAGMA journal_mode=%Q", zJMode);
946       sqlite3_exec(db, zSql, 0, 0, 0);
947       sqlite3_free(zSql);
948       if( noCheckpoint ){
949         sqlite3_exec(db, "PRAGMA wal_autocheckpoint=0", 0, 0, 0);
950       }
951     }
952     sqlite3_prepare_v2(db, "PRAGMA journal_mode", -1, &pStmt, 0);
953     if( sqlite3_step(pStmt)==SQLITE_ROW ){
954       zJMode = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
955     }else{
956       zJMode = "???";
957     }
958     sqlite3_finalize(pStmt);
959     if( iMax<=0 ){
960       sqlite3_prepare_v2(db, "SELECT max(k) FROM kv", -1, &pStmt, 0);
961       if( sqlite3_step(pStmt)==SQLITE_ROW ){
962         iMax = sqlite3_column_int(pStmt, 0);
963       }
964       sqlite3_finalize(pStmt);
965     }
966     pStmt = 0;
967     if( !doMultiTrans ) sqlite3_exec(db, "BEGIN", 0, 0, 0);
968   }
969   if( iMax<=0 ) iMax = 1000;
970   for(i=0; i<nCount; i++){
971     if( eType==PATH_DIR || eType==PATH_TREE ){
972       /* CASE 1: Reading or writing blobs out of separate files */
973       char *zKey;
974       if( eType==PATH_DIR ){
975         zKey = sqlite3_mprintf("%s/%06d", zDb, iKey);
976       }else{
977         zKey = sqlite3_mprintf("%s/%02d/%02d/%02d", zDb, iKey/10000,
978                                (iKey/100)%100, iKey%100);
979       }
980       nData = 0;
981       if( isUpdateTest ){
982         updateFile(zKey, &nData, doFsync);
983       }else{
984         pData = readFile(zKey, &nData);
985         sqlite3_free(pData);
986       }
987       sqlite3_free(zKey);
988     }else if( bBlobApi ){
989       /* CASE 2: Reading from database using the incremental BLOB I/O API */
990       if( pBlob==0 ){
991         rc = sqlite3_blob_open(db, "main", "kv", "v", iKey,
992                                isUpdateTest, &pBlob);
993         if( rc ){
994           fatalError("could not open sqlite3_blob handle: %s",
995                      sqlite3_errmsg(db));
996         }
997       }else{
998         rc = sqlite3_blob_reopen(pBlob, iKey);
999       }
1000       if( rc==SQLITE_OK ){
1001         nData = sqlite3_blob_bytes(pBlob);
1002         if( nAlloc<nData+1 ){
1003           nAlloc = nData+100;
1004           pData = sqlite3_realloc64(pData, nAlloc);
1005         }
1006         if( pData==0 ) fatalError("cannot allocate %d bytes", nData+1);
1007         if( isUpdateTest ){
1008           sqlite3_randomness((int)nData, pData);
1009           rc = sqlite3_blob_write(pBlob, pData, (int)nData, 0);
1010           if( rc!=SQLITE_OK ){
1011             fatalError("could not write the blob at %d: %s", iKey,
1012                       sqlite3_errmsg(db));
1013           }
1014         }else{
1015           rc = sqlite3_blob_read(pBlob, pData, (int)nData, 0);
1016           if( rc!=SQLITE_OK ){
1017             fatalError("could not read the blob at %d: %s", iKey,
1018                       sqlite3_errmsg(db));
1019           }
1020         }
1021       }
1022     }else{
1023       /* CASE 3: Reading from database using SQL */
1024       if( pStmt==0 ){
1025         if( isUpdateTest ){
1026           sqlite3_create_function(db, "remember", 2, SQLITE_UTF8, 0,
1027                                   rememberFunc, 0, 0);
1028 
1029           rc = sqlite3_prepare_v2(db,
1030             "UPDATE kv SET v=randomblob(remember(length(v),?2))"
1031             " WHERE k=?1", -1, &pStmt, 0);
1032           sqlite3_bind_int64(pStmt, 2, SQLITE_PTR_TO_INT(&nData));
1033         }else{
1034           rc = sqlite3_prepare_v2(db,
1035                  "SELECT v FROM kv WHERE k=?1", -1, &pStmt, 0);
1036         }
1037         if( rc ){
1038           fatalError("cannot prepare query: %s", sqlite3_errmsg(db));
1039         }
1040       }else{
1041         sqlite3_reset(pStmt);
1042       }
1043       sqlite3_bind_int(pStmt, 1, iKey);
1044       nData = 0;
1045       rc = sqlite3_step(pStmt);
1046       if( rc==SQLITE_ROW ){
1047         nData = sqlite3_column_bytes(pStmt, 0);
1048         pData = (unsigned char*)sqlite3_column_blob(pStmt, 0);
1049       }
1050     }
1051     if( eOrder==ORDER_ASC ){
1052       iKey++;
1053       if( iKey>iMax ) iKey = 1;
1054     }else if( eOrder==ORDER_DESC ){
1055       iKey--;
1056       if( iKey<=0 ) iKey = iMax;
1057     }else{
1058       iKey = (randInt()%iMax)+1;
1059     }
1060     nTotal += nData;
1061     if( nData==0 ){ nCount++; nExtra++; }
1062   }
1063   if( nAlloc ) sqlite3_free(pData);
1064   if( pStmt ) sqlite3_finalize(pStmt);
1065   if( pBlob ) sqlite3_blob_close(pBlob);
1066   if( bStats ){
1067     display_stats(db, 0);
1068   }
1069   if( db ){
1070     if( !doMultiTrans ) sqlite3_exec(db, "COMMIT", 0, 0, 0);
1071     if( !noCheckpoint ){
1072       sqlite3_close(db);
1073       db = 0;
1074     }
1075   }
1076   tmElapsed = timeOfDay() - tmStart;
1077   if( db && noCheckpoint ){
1078     sqlite3_close(db);
1079     db = 0;
1080   }
1081   if( nExtra ){
1082     printf("%d cycles due to %d misses\n", nCount, nExtra);
1083   }
1084   if( eType==PATH_DB ){
1085     printf("SQLite version: %s\n", sqlite3_libversion());
1086     if( doIntegrityCk ){
1087       sqlite3_open(zDb, &db);
1088       sqlite3_prepare_v2(db, "PRAGMA integrity_check", -1, &pStmt, 0);
1089       while( sqlite3_step(pStmt)==SQLITE_ROW ){
1090         printf("integrity-check: %s\n", sqlite3_column_text(pStmt, 0));
1091       }
1092       sqlite3_finalize(pStmt);
1093       sqlite3_close(db);
1094       db = 0;
1095     }
1096   }
1097   printf("--count %d --max-id %d", nCount-nExtra, iMax);
1098   switch( eOrder ){
1099     case ORDER_RANDOM:  printf(" --random\n");  break;
1100     case ORDER_DESC:    printf(" --desc\n");    break;
1101     default:            printf(" --asc\n");     break;
1102   }
1103   if( eType==PATH_DB ){
1104     printf("--cache-size %d --jmode %s\n", iCache, zJMode);
1105     printf("--mmap %d%s\n", mmapSize, bBlobApi ? " --blob-api" : "");
1106     if( noSync ) printf("--nosync\n");
1107   }
1108   if( iPagesize ) printf("Database page size: %d\n", iPagesize);
1109   printf("Total elapsed time: %.3f\n", tmElapsed/1000.0);
1110   if( isUpdateTest ){
1111     printf("Microseconds per BLOB write: %.3f\n", tmElapsed*1000.0/nCount);
1112     printf("Content write rate: %.1f MB/s\n", nTotal/(1000.0*tmElapsed));
1113   }else{
1114     printf("Microseconds per BLOB read: %.3f\n", tmElapsed*1000.0/nCount);
1115     printf("Content read rate: %.1f MB/s\n", nTotal/(1000.0*tmElapsed));
1116   }
1117   return 0;
1118 }
1119 
1120 
main(int argc,char ** argv)1121 int main(int argc, char **argv){
1122   if( argc<3 ) showHelp();
1123   if( strcmp(argv[1],"init")==0 ){
1124     return initMain(argc, argv);
1125   }
1126   if( strcmp(argv[1],"export")==0 ){
1127     return exportMain(argc, argv);
1128   }
1129   if( strcmp(argv[1],"run")==0 ){
1130     return runMain(argc, argv);
1131   }
1132   if( strcmp(argv[1],"stat")==0 ){
1133     return statMain(argc, argv);
1134   }
1135   showHelp();
1136   return 0;
1137 }
1138