xref: /sqlite-3.40.0/src/test_osinst.c (revision dfe4e6bb)
1 /*
2 ** 2008 April 10
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 contains the implementation of an SQLite vfs wrapper that
14 ** adds instrumentation to all vfs and file methods. C and Tcl interfaces
15 ** are provided to control the instrumentation.
16 */
17 
18 /*
19 ** This module contains code for a wrapper VFS that causes a log of
20 ** most VFS calls to be written into a nominated file on disk. The log
21 ** is stored in a compressed binary format to reduce the amount of IO
22 ** overhead introduced into the application by logging.
23 **
24 ** All calls on sqlite3_file objects except xFileControl() are logged.
25 ** Additionally, calls to the xAccess(), xOpen(), and xDelete()
26 ** methods are logged. The other sqlite3_vfs object methods (xDlXXX,
27 ** xRandomness, xSleep, xCurrentTime, xGetLastError and xCurrentTimeInt64)
28 ** are not logged.
29 **
30 ** The binary log files are read using a virtual table implementation
31 ** also contained in this file.
32 **
33 ** CREATING LOG FILES:
34 **
35 **       int sqlite3_vfslog_new(
36 **         const char *zVfs,          // Name of new VFS
37 **         const char *zParentVfs,    // Name of parent VFS (or NULL)
38 **         const char *zLog           // Name of log file to write to
39 **       );
40 **
41 **       int sqlite3_vfslog_finalize(const char *zVfs);
42 **
43 ** ANNOTATING LOG FILES:
44 **
45 **   To write an arbitrary message into a log file:
46 **
47 **       int sqlite3_vfslog_annotate(const char *zVfs, const char *zMsg);
48 **
49 ** READING LOG FILES:
50 **
51 **   Log files are read using the "vfslog" virtual table implementation
52 **   in this file. To register the virtual table with SQLite, use:
53 **
54 **       int sqlite3_vfslog_register(sqlite3 *db);
55 **
56 **   Then, if the log file is named "vfs.log", the following SQL command:
57 **
58 **       CREATE VIRTUAL TABLE v USING vfslog('vfs.log');
59 **
60 **   creates a virtual table with 6 columns, as follows:
61 **
62 **       CREATE TABLE v(
63 **         event    TEXT,             // "xOpen", "xRead" etc.
64 **         file     TEXT,             // Name of file this call applies to
65 **         clicks   INTEGER,          // Time spent in call
66 **         rc       INTEGER,          // Return value
67 **         size     INTEGER,          // Bytes read or written
68 **         offset   INTEGER           // File offset read or written
69 **       );
70 */
71 
72 #include "sqlite3.h"
73 
74 #include "os_setup.h"
75 #if SQLITE_OS_WIN
76 #  include "os_win.h"
77 #endif
78 
79 #include <string.h>
80 #include <assert.h>
81 
82 
83 /*
84 ** Maximum pathname length supported by the vfslog backend.
85 */
86 #define INST_MAX_PATHNAME 512
87 
88 #define OS_ACCESS            1
89 #define OS_CHECKRESERVEDLOCK 2
90 #define OS_CLOSE             3
91 #define OS_CURRENTTIME       4
92 #define OS_DELETE            5
93 #define OS_DEVCHAR           6
94 #define OS_FILECONTROL       7
95 #define OS_FILESIZE          8
96 #define OS_FULLPATHNAME      9
97 #define OS_LOCK              11
98 #define OS_OPEN              12
99 #define OS_RANDOMNESS        13
100 #define OS_READ              14
101 #define OS_SECTORSIZE        15
102 #define OS_SLEEP             16
103 #define OS_SYNC              17
104 #define OS_TRUNCATE          18
105 #define OS_UNLOCK            19
106 #define OS_WRITE             20
107 #define OS_SHMUNMAP          22
108 #define OS_SHMMAP            23
109 #define OS_SHMLOCK           25
110 #define OS_SHMBARRIER        26
111 #define OS_ANNOTATE          28
112 
113 #define OS_NUMEVENTS         29
114 
115 #define VFSLOG_BUFFERSIZE 8192
116 
117 typedef struct VfslogVfs VfslogVfs;
118 typedef struct VfslogFile VfslogFile;
119 
120 struct VfslogVfs {
121   sqlite3_vfs base;               /* VFS methods */
122   sqlite3_vfs *pVfs;              /* Parent VFS */
123   int iNextFileId;                /* Next file id */
124   sqlite3_file *pLog;             /* Log file handle */
125   sqlite3_int64 iOffset;          /* Log file offset of start of write buffer */
126   int nBuf;                       /* Number of valid bytes in aBuf[] */
127   char aBuf[VFSLOG_BUFFERSIZE];   /* Write buffer */
128 };
129 
130 struct VfslogFile {
131   sqlite3_file base;              /* IO methods */
132   sqlite3_file *pReal;            /* Underlying file handle */
133   sqlite3_vfs *pVfslog;           /* Associated VsflogVfs object */
134   int iFileId;                    /* File id number */
135 };
136 
137 #define REALVFS(p) (((VfslogVfs *)(p))->pVfs)
138 
139 
140 
141 /*
142 ** Method declarations for vfslog_file.
143 */
144 static int vfslogClose(sqlite3_file*);
145 static int vfslogRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
146 static int vfslogWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
147 static int vfslogTruncate(sqlite3_file*, sqlite3_int64 size);
148 static int vfslogSync(sqlite3_file*, int flags);
149 static int vfslogFileSize(sqlite3_file*, sqlite3_int64 *pSize);
150 static int vfslogLock(sqlite3_file*, int);
151 static int vfslogUnlock(sqlite3_file*, int);
152 static int vfslogCheckReservedLock(sqlite3_file*, int *pResOut);
153 static int vfslogFileControl(sqlite3_file*, int op, void *pArg);
154 static int vfslogSectorSize(sqlite3_file*);
155 static int vfslogDeviceCharacteristics(sqlite3_file*);
156 
157 static int vfslogShmLock(sqlite3_file *pFile, int ofst, int n, int flags);
158 static int vfslogShmMap(sqlite3_file *pFile,int,int,int,volatile void **);
159 static void vfslogShmBarrier(sqlite3_file*);
160 static int vfslogShmUnmap(sqlite3_file *pFile, int deleteFlag);
161 
162 /*
163 ** Method declarations for vfslog_vfs.
164 */
165 static int vfslogOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
166 static int vfslogDelete(sqlite3_vfs*, const char *zName, int syncDir);
167 static int vfslogAccess(sqlite3_vfs*, const char *zName, int flags, int *);
168 static int vfslogFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
169 static void *vfslogDlOpen(sqlite3_vfs*, const char *zFilename);
170 static void vfslogDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
171 static void (*vfslogDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
172 static void vfslogDlClose(sqlite3_vfs*, void*);
173 static int vfslogRandomness(sqlite3_vfs*, int nByte, char *zOut);
174 static int vfslogSleep(sqlite3_vfs*, int microseconds);
175 static int vfslogCurrentTime(sqlite3_vfs*, double*);
176 
177 static int vfslogGetLastError(sqlite3_vfs*, int, char *);
178 static int vfslogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
179 
180 static sqlite3_vfs vfslog_vfs = {
181   1,                              /* iVersion */
182   sizeof(VfslogFile),             /* szOsFile */
183   INST_MAX_PATHNAME,              /* mxPathname */
184   0,                              /* pNext */
185   0,                              /* zName */
186   0,                              /* pAppData */
187   vfslogOpen,                     /* xOpen */
188   vfslogDelete,                   /* xDelete */
189   vfslogAccess,                   /* xAccess */
190   vfslogFullPathname,             /* xFullPathname */
191   vfslogDlOpen,                   /* xDlOpen */
192   vfslogDlError,                  /* xDlError */
193   vfslogDlSym,                    /* xDlSym */
194   vfslogDlClose,                  /* xDlClose */
195   vfslogRandomness,               /* xRandomness */
196   vfslogSleep,                    /* xSleep */
197   vfslogCurrentTime,              /* xCurrentTime */
198   vfslogGetLastError,             /* xGetLastError */
199   vfslogCurrentTimeInt64          /* xCurrentTime */
200 };
201 
202 static sqlite3_io_methods vfslog_io_methods = {
203   2,                              /* iVersion */
204   vfslogClose,                    /* xClose */
205   vfslogRead,                     /* xRead */
206   vfslogWrite,                    /* xWrite */
207   vfslogTruncate,                 /* xTruncate */
208   vfslogSync,                     /* xSync */
209   vfslogFileSize,                 /* xFileSize */
210   vfslogLock,                     /* xLock */
211   vfslogUnlock,                   /* xUnlock */
212   vfslogCheckReservedLock,        /* xCheckReservedLock */
213   vfslogFileControl,              /* xFileControl */
214   vfslogSectorSize,               /* xSectorSize */
215   vfslogDeviceCharacteristics,    /* xDeviceCharacteristics */
216   vfslogShmMap,                   /* xShmMap */
217   vfslogShmLock,                  /* xShmLock */
218   vfslogShmBarrier,               /* xShmBarrier */
219   vfslogShmUnmap                  /* xShmUnmap */
220 };
221 
222 #if SQLITE_OS_UNIX && !defined(NO_GETTOD)
223 #include <sys/time.h>
224 static sqlite3_uint64 vfslog_time(){
225   struct timeval sTime;
226   gettimeofday(&sTime, 0);
227   return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000;
228 }
229 #elif SQLITE_OS_WIN
230 #include <time.h>
231 static sqlite3_uint64 vfslog_time(){
232   FILETIME ft;
233   sqlite3_uint64 u64time = 0;
234 
235   GetSystemTimeAsFileTime(&ft);
236 
237   u64time |= ft.dwHighDateTime;
238   u64time <<= 32;
239   u64time |= ft.dwLowDateTime;
240 
241   /* ft is 100-nanosecond intervals, we want microseconds */
242   return u64time /(sqlite3_uint64)10;
243 }
244 #else
245 static sqlite3_uint64 vfslog_time(){
246   return 0;
247 }
248 #endif
249 
250 static void vfslog_call(sqlite3_vfs *, int, int, sqlite3_int64, int, int, int);
251 static void vfslog_string(sqlite3_vfs *, const char *);
252 
253 /*
254 ** Close an vfslog-file.
255 */
256 static int vfslogClose(sqlite3_file *pFile){
257   sqlite3_uint64 t;
258   int rc = SQLITE_OK;
259   VfslogFile *p = (VfslogFile *)pFile;
260 
261   t = vfslog_time();
262   if( p->pReal->pMethods ){
263     rc = p->pReal->pMethods->xClose(p->pReal);
264   }
265   t = vfslog_time() - t;
266   vfslog_call(p->pVfslog, OS_CLOSE, p->iFileId, t, rc, 0, 0);
267   return rc;
268 }
269 
270 /*
271 ** Read data from an vfslog-file.
272 */
273 static int vfslogRead(
274   sqlite3_file *pFile,
275   void *zBuf,
276   int iAmt,
277   sqlite_int64 iOfst
278 ){
279   int rc;
280   sqlite3_uint64 t;
281   VfslogFile *p = (VfslogFile *)pFile;
282   t = vfslog_time();
283   rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
284   t = vfslog_time() - t;
285   vfslog_call(p->pVfslog, OS_READ, p->iFileId, t, rc, iAmt, (int)iOfst);
286   return rc;
287 }
288 
289 /*
290 ** Write data to an vfslog-file.
291 */
292 static int vfslogWrite(
293   sqlite3_file *pFile,
294   const void *z,
295   int iAmt,
296   sqlite_int64 iOfst
297 ){
298   int rc;
299   sqlite3_uint64 t;
300   VfslogFile *p = (VfslogFile *)pFile;
301   t = vfslog_time();
302   rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst);
303   t = vfslog_time() - t;
304   vfslog_call(p->pVfslog, OS_WRITE, p->iFileId, t, rc, iAmt, (int)iOfst);
305   return rc;
306 }
307 
308 /*
309 ** Truncate an vfslog-file.
310 */
311 static int vfslogTruncate(sqlite3_file *pFile, sqlite_int64 size){
312   int rc;
313   sqlite3_uint64 t;
314   VfslogFile *p = (VfslogFile *)pFile;
315   t = vfslog_time();
316   rc = p->pReal->pMethods->xTruncate(p->pReal, size);
317   t = vfslog_time() - t;
318   vfslog_call(p->pVfslog, OS_TRUNCATE, p->iFileId, t, rc, 0, (int)size);
319   return rc;
320 }
321 
322 /*
323 ** Sync an vfslog-file.
324 */
325 static int vfslogSync(sqlite3_file *pFile, int flags){
326   int rc;
327   sqlite3_uint64 t;
328   VfslogFile *p = (VfslogFile *)pFile;
329   t = vfslog_time();
330   rc = p->pReal->pMethods->xSync(p->pReal, flags);
331   t = vfslog_time() - t;
332   vfslog_call(p->pVfslog, OS_SYNC, p->iFileId, t, rc, flags, 0);
333   return rc;
334 }
335 
336 /*
337 ** Return the current file-size of an vfslog-file.
338 */
339 static int vfslogFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
340   int rc;
341   sqlite3_uint64 t;
342   VfslogFile *p = (VfslogFile *)pFile;
343   t = vfslog_time();
344   rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
345   t = vfslog_time() - t;
346   vfslog_call(p->pVfslog, OS_FILESIZE, p->iFileId, t, rc, 0, (int)*pSize);
347   return rc;
348 }
349 
350 /*
351 ** Lock an vfslog-file.
352 */
353 static int vfslogLock(sqlite3_file *pFile, int eLock){
354   int rc;
355   sqlite3_uint64 t;
356   VfslogFile *p = (VfslogFile *)pFile;
357   t = vfslog_time();
358   rc = p->pReal->pMethods->xLock(p->pReal, eLock);
359   t = vfslog_time() - t;
360   vfslog_call(p->pVfslog, OS_LOCK, p->iFileId, t, rc, eLock, 0);
361   return rc;
362 }
363 
364 /*
365 ** Unlock an vfslog-file.
366 */
367 static int vfslogUnlock(sqlite3_file *pFile, int eLock){
368   int rc;
369   sqlite3_uint64 t;
370   VfslogFile *p = (VfslogFile *)pFile;
371   t = vfslog_time();
372   rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
373   t = vfslog_time() - t;
374   vfslog_call(p->pVfslog, OS_UNLOCK, p->iFileId, t, rc, eLock, 0);
375   return rc;
376 }
377 
378 /*
379 ** Check if another file-handle holds a RESERVED lock on an vfslog-file.
380 */
381 static int vfslogCheckReservedLock(sqlite3_file *pFile, int *pResOut){
382   int rc;
383   sqlite3_uint64 t;
384   VfslogFile *p = (VfslogFile *)pFile;
385   t = vfslog_time();
386   rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
387   t = vfslog_time() - t;
388   vfslog_call(p->pVfslog, OS_CHECKRESERVEDLOCK, p->iFileId, t, rc, *pResOut, 0);
389   return rc;
390 }
391 
392 /*
393 ** File control method. For custom operations on an vfslog-file.
394 */
395 static int vfslogFileControl(sqlite3_file *pFile, int op, void *pArg){
396   VfslogFile *p = (VfslogFile *)pFile;
397   int rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
398   if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
399     *(char**)pArg = sqlite3_mprintf("vfslog/%z", *(char**)pArg);
400   }
401   return rc;
402 }
403 
404 /*
405 ** Return the sector-size in bytes for an vfslog-file.
406 */
407 static int vfslogSectorSize(sqlite3_file *pFile){
408   int rc;
409   sqlite3_uint64 t;
410   VfslogFile *p = (VfslogFile *)pFile;
411   t = vfslog_time();
412   rc = p->pReal->pMethods->xSectorSize(p->pReal);
413   t = vfslog_time() - t;
414   vfslog_call(p->pVfslog, OS_SECTORSIZE, p->iFileId, t, rc, 0, 0);
415   return rc;
416 }
417 
418 /*
419 ** Return the device characteristic flags supported by an vfslog-file.
420 */
421 static int vfslogDeviceCharacteristics(sqlite3_file *pFile){
422   int rc;
423   sqlite3_uint64 t;
424   VfslogFile *p = (VfslogFile *)pFile;
425   t = vfslog_time();
426   rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
427   t = vfslog_time() - t;
428   vfslog_call(p->pVfslog, OS_DEVCHAR, p->iFileId, t, rc, 0, 0);
429   return rc;
430 }
431 
432 static int vfslogShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
433   int rc;
434   sqlite3_uint64 t;
435   VfslogFile *p = (VfslogFile *)pFile;
436   t = vfslog_time();
437   rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
438   t = vfslog_time() - t;
439   vfslog_call(p->pVfslog, OS_SHMLOCK, p->iFileId, t, rc, 0, 0);
440   return rc;
441 }
442 static int vfslogShmMap(
443   sqlite3_file *pFile,
444   int iRegion,
445   int szRegion,
446   int isWrite,
447   volatile void **pp
448 ){
449   int rc;
450   sqlite3_uint64 t;
451   VfslogFile *p = (VfslogFile *)pFile;
452   t = vfslog_time();
453   rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
454   t = vfslog_time() - t;
455   vfslog_call(p->pVfslog, OS_SHMMAP, p->iFileId, t, rc, 0, 0);
456   return rc;
457 }
458 static void vfslogShmBarrier(sqlite3_file *pFile){
459   sqlite3_uint64 t;
460   VfslogFile *p = (VfslogFile *)pFile;
461   t = vfslog_time();
462   p->pReal->pMethods->xShmBarrier(p->pReal);
463   t = vfslog_time() - t;
464   vfslog_call(p->pVfslog, OS_SHMBARRIER, p->iFileId, t, SQLITE_OK, 0, 0);
465 }
466 static int vfslogShmUnmap(sqlite3_file *pFile, int deleteFlag){
467   int rc;
468   sqlite3_uint64 t;
469   VfslogFile *p = (VfslogFile *)pFile;
470   t = vfslog_time();
471   rc = p->pReal->pMethods->xShmUnmap(p->pReal, deleteFlag);
472   t = vfslog_time() - t;
473   vfslog_call(p->pVfslog, OS_SHMUNMAP, p->iFileId, t, rc, 0, 0);
474   return rc;
475 }
476 
477 
478 /*
479 ** Open an vfslog file handle.
480 */
481 static int vfslogOpen(
482   sqlite3_vfs *pVfs,
483   const char *zName,
484   sqlite3_file *pFile,
485   int flags,
486   int *pOutFlags
487 ){
488   int rc;
489   sqlite3_uint64 t;
490   VfslogFile *p = (VfslogFile *)pFile;
491   VfslogVfs *pLog = (VfslogVfs *)pVfs;
492 
493   pFile->pMethods = &vfslog_io_methods;
494   p->pReal = (sqlite3_file *)&p[1];
495   p->pVfslog = pVfs;
496   p->iFileId = ++pLog->iNextFileId;
497 
498   t = vfslog_time();
499   rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags);
500   t = vfslog_time() - t;
501 
502   vfslog_call(pVfs, OS_OPEN, p->iFileId, t, rc, 0, 0);
503   vfslog_string(pVfs, zName);
504   return rc;
505 }
506 
507 /*
508 ** Delete the file located at zPath. If the dirSync argument is true,
509 ** ensure the file-system modifications are synced to disk before
510 ** returning.
511 */
512 static int vfslogDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
513   int rc;
514   sqlite3_uint64 t;
515   t = vfslog_time();
516   rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync);
517   t = vfslog_time() - t;
518   vfslog_call(pVfs, OS_DELETE, 0, t, rc, dirSync, 0);
519   vfslog_string(pVfs, zPath);
520   return rc;
521 }
522 
523 /*
524 ** Test for access permissions. Return true if the requested permission
525 ** is available, or false otherwise.
526 */
527 static int vfslogAccess(
528   sqlite3_vfs *pVfs,
529   const char *zPath,
530   int flags,
531   int *pResOut
532 ){
533   int rc;
534   sqlite3_uint64 t;
535   t = vfslog_time();
536   rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut);
537   t = vfslog_time() - t;
538   vfslog_call(pVfs, OS_ACCESS, 0, t, rc, flags, *pResOut);
539   vfslog_string(pVfs, zPath);
540   return rc;
541 }
542 
543 /*
544 ** Populate buffer zOut with the full canonical pathname corresponding
545 ** to the pathname in zPath. zOut is guaranteed to point to a buffer
546 ** of at least (INST_MAX_PATHNAME+1) bytes.
547 */
548 static int vfslogFullPathname(
549   sqlite3_vfs *pVfs,
550   const char *zPath,
551   int nOut,
552   char *zOut
553 ){
554   return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
555 }
556 
557 /*
558 ** Open the dynamic library located at zPath and return a handle.
559 */
560 static void *vfslogDlOpen(sqlite3_vfs *pVfs, const char *zPath){
561   return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath);
562 }
563 
564 /*
565 ** Populate the buffer zErrMsg (size nByte bytes) with a human readable
566 ** utf-8 string describing the most recent error encountered associated
567 ** with dynamic libraries.
568 */
569 static void vfslogDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
570   REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg);
571 }
572 
573 /*
574 ** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
575 */
576 static void (*vfslogDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
577   return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym);
578 }
579 
580 /*
581 ** Close the dynamic library handle pHandle.
582 */
583 static void vfslogDlClose(sqlite3_vfs *pVfs, void *pHandle){
584   REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle);
585 }
586 
587 /*
588 ** Populate the buffer pointed to by zBufOut with nByte bytes of
589 ** random data.
590 */
591 static int vfslogRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
592   return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
593 }
594 
595 /*
596 ** Sleep for nMicro microseconds. Return the number of microseconds
597 ** actually slept.
598 */
599 static int vfslogSleep(sqlite3_vfs *pVfs, int nMicro){
600   return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro);
601 }
602 
603 /*
604 ** Return the current time as a Julian Day number in *pTimeOut.
605 */
606 static int vfslogCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
607   return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut);
608 }
609 
610 static int vfslogGetLastError(sqlite3_vfs *pVfs, int a, char *b){
611   return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b);
612 }
613 static int vfslogCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
614   return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p);
615 }
616 
617 static void vfslog_flush(VfslogVfs *p){
618 #ifdef SQLITE_TEST
619   extern int sqlite3_io_error_pending;
620   extern int sqlite3_io_error_persist;
621   extern int sqlite3_diskfull_pending;
622 
623   int pending = sqlite3_io_error_pending;
624   int persist = sqlite3_io_error_persist;
625   int diskfull = sqlite3_diskfull_pending;
626 
627   sqlite3_io_error_pending = 0;
628   sqlite3_io_error_persist = 0;
629   sqlite3_diskfull_pending = 0;
630 #endif
631 
632   if( p->nBuf ){
633     p->pLog->pMethods->xWrite(p->pLog, p->aBuf, p->nBuf, p->iOffset);
634     p->iOffset += p->nBuf;
635     p->nBuf = 0;
636   }
637 
638 #ifdef SQLITE_TEST
639   sqlite3_io_error_pending = pending;
640   sqlite3_io_error_persist = persist;
641   sqlite3_diskfull_pending = diskfull;
642 #endif
643 }
644 
645 static void put32bits(unsigned char *p, unsigned int v){
646   p[0] = v>>24;
647   p[1] = (unsigned char)(v>>16);
648   p[2] = (unsigned char)(v>>8);
649   p[3] = (unsigned char)v;
650 }
651 
652 static void vfslog_call(
653   sqlite3_vfs *pVfs,
654   int eEvent,
655   int iFileid,
656   sqlite3_int64 nClick,
657   int return_code,
658   int size,
659   int offset
660 ){
661   VfslogVfs *p = (VfslogVfs *)pVfs;
662   unsigned char *zRec;
663   if( (24+p->nBuf)>sizeof(p->aBuf) ){
664     vfslog_flush(p);
665   }
666   zRec = (unsigned char *)&p->aBuf[p->nBuf];
667   put32bits(&zRec[0], eEvent);
668   put32bits(&zRec[4], iFileid);
669   put32bits(&zRec[8], (unsigned int)(nClick&0xffff));
670   put32bits(&zRec[12], return_code);
671   put32bits(&zRec[16], size);
672   put32bits(&zRec[20], offset);
673   p->nBuf += 24;
674 }
675 
676 static void vfslog_string(sqlite3_vfs *pVfs, const char *zStr){
677   VfslogVfs *p = (VfslogVfs *)pVfs;
678   unsigned char *zRec;
679   int nStr = zStr ? (int)strlen(zStr) : 0;
680   if( (4+nStr+p->nBuf)>sizeof(p->aBuf) ){
681     vfslog_flush(p);
682   }
683   zRec = (unsigned char *)&p->aBuf[p->nBuf];
684   put32bits(&zRec[0], nStr);
685   if( zStr ){
686     memcpy(&zRec[4], zStr, nStr);
687   }
688   p->nBuf += (4 + nStr);
689 }
690 
691 static void vfslog_finalize(VfslogVfs *p){
692   if( p->pLog->pMethods ){
693     vfslog_flush(p);
694     p->pLog->pMethods->xClose(p->pLog);
695   }
696   sqlite3_free(p);
697 }
698 
699 int sqlite3_vfslog_finalize(const char *zVfs){
700   sqlite3_vfs *pVfs;
701   pVfs = sqlite3_vfs_find(zVfs);
702   if( !pVfs || pVfs->xOpen!=vfslogOpen ){
703     return SQLITE_ERROR;
704   }
705   sqlite3_vfs_unregister(pVfs);
706   vfslog_finalize((VfslogVfs *)pVfs);
707   return SQLITE_OK;
708 }
709 
710 int sqlite3_vfslog_new(
711   const char *zVfs,               /* New VFS name */
712   const char *zParentVfs,         /* Parent VFS name (or NULL) */
713   const char *zLog                /* Log file name */
714 ){
715   VfslogVfs *p;
716   sqlite3_vfs *pParent;
717   int nByte;
718   int flags;
719   int rc;
720   char *zFile;
721   int nVfs;
722 
723   pParent = sqlite3_vfs_find(zParentVfs);
724   if( !pParent ){
725     return SQLITE_ERROR;
726   }
727 
728   nVfs = (int)strlen(zVfs);
729   nByte = sizeof(VfslogVfs) + pParent->szOsFile + nVfs+1+pParent->mxPathname+1;
730   p = (VfslogVfs *)sqlite3_malloc(nByte);
731   memset(p, 0, nByte);
732 
733   p->pVfs = pParent;
734   p->pLog = (sqlite3_file *)&p[1];
735   memcpy(&p->base, &vfslog_vfs, sizeof(sqlite3_vfs));
736   p->base.zName = &((char *)p->pLog)[pParent->szOsFile];
737   p->base.szOsFile += pParent->szOsFile;
738   memcpy((char *)p->base.zName, zVfs, nVfs);
739 
740   zFile = (char *)&p->base.zName[nVfs+1];
741   pParent->xFullPathname(pParent, zLog, pParent->mxPathname, zFile);
742 
743   flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MASTER_JOURNAL;
744   pParent->xDelete(pParent, zFile, 0);
745   rc = pParent->xOpen(pParent, zFile, p->pLog, flags, &flags);
746   if( rc==SQLITE_OK ){
747     memcpy(p->aBuf, "sqlite_ostrace1.....", 20);
748     p->iOffset = 0;
749     p->nBuf = 20;
750     rc = sqlite3_vfs_register((sqlite3_vfs *)p, 1);
751   }
752   if( rc ){
753     vfslog_finalize(p);
754   }
755   return rc;
756 }
757 
758 int sqlite3_vfslog_annotate(const char *zVfs, const char *zMsg){
759   sqlite3_vfs *pVfs;
760   pVfs = sqlite3_vfs_find(zVfs);
761   if( !pVfs || pVfs->xOpen!=vfslogOpen ){
762     return SQLITE_ERROR;
763   }
764   vfslog_call(pVfs, OS_ANNOTATE, 0, 0, 0, 0, 0);
765   vfslog_string(pVfs, zMsg);
766   return SQLITE_OK;
767 }
768 
769 static const char *vfslog_eventname(int eEvent){
770   const char *zEvent = 0;
771 
772   switch( eEvent ){
773     case OS_CLOSE:             zEvent = "xClose"; break;
774     case OS_READ:              zEvent = "xRead"; break;
775     case OS_WRITE:             zEvent = "xWrite"; break;
776     case OS_TRUNCATE:          zEvent = "xTruncate"; break;
777     case OS_SYNC:              zEvent = "xSync"; break;
778     case OS_FILESIZE:          zEvent = "xFilesize"; break;
779     case OS_LOCK:              zEvent = "xLock"; break;
780     case OS_UNLOCK:            zEvent = "xUnlock"; break;
781     case OS_CHECKRESERVEDLOCK: zEvent = "xCheckResLock"; break;
782     case OS_FILECONTROL:       zEvent = "xFileControl"; break;
783     case OS_SECTORSIZE:        zEvent = "xSectorSize"; break;
784     case OS_DEVCHAR:           zEvent = "xDeviceChar"; break;
785     case OS_OPEN:              zEvent = "xOpen"; break;
786     case OS_DELETE:            zEvent = "xDelete"; break;
787     case OS_ACCESS:            zEvent = "xAccess"; break;
788     case OS_FULLPATHNAME:      zEvent = "xFullPathname"; break;
789     case OS_RANDOMNESS:        zEvent = "xRandomness"; break;
790     case OS_SLEEP:             zEvent = "xSleep"; break;
791     case OS_CURRENTTIME:       zEvent = "xCurrentTime"; break;
792 
793     case OS_SHMUNMAP:          zEvent = "xShmUnmap"; break;
794     case OS_SHMLOCK:           zEvent = "xShmLock"; break;
795     case OS_SHMBARRIER:        zEvent = "xShmBarrier"; break;
796     case OS_SHMMAP:            zEvent = "xShmMap"; break;
797 
798     case OS_ANNOTATE:          zEvent = "annotation"; break;
799   }
800 
801   return zEvent;
802 }
803 
804 typedef struct VfslogVtab VfslogVtab;
805 typedef struct VfslogCsr VfslogCsr;
806 
807 /*
808 ** Virtual table type for the vfslog reader module.
809 */
810 struct VfslogVtab {
811   sqlite3_vtab base;              /* Base class */
812   sqlite3_file *pFd;              /* File descriptor open on vfslog file */
813   sqlite3_int64 nByte;            /* Size of file in bytes */
814   char *zFile;                    /* File name for pFd */
815 };
816 
817 /*
818 ** Virtual table cursor type for the vfslog reader module.
819 */
820 struct VfslogCsr {
821   sqlite3_vtab_cursor base;       /* Base class */
822   sqlite3_int64 iRowid;           /* Current rowid. */
823   sqlite3_int64 iOffset;          /* Offset of next record in file */
824   char *zTransient;               /* Transient 'file' string */
825   int nFile;                      /* Size of array azFile[] */
826   char **azFile;                  /* File strings */
827   unsigned char aBuf[1024];       /* Current vfs log entry (read from file) */
828 };
829 
830 static unsigned int get32bits(unsigned char *p){
831   return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3];
832 }
833 
834 /*
835 ** The argument must point to a buffer containing a nul-terminated string.
836 ** If the string begins with an SQL quote character it is overwritten by
837 ** the dequoted version. Otherwise the buffer is left unmodified.
838 */
839 static void dequote(char *z){
840   char quote;                     /* Quote character (if any ) */
841   quote = z[0];
842   if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){
843     int iIn = 1;                  /* Index of next byte to read from input */
844     int iOut = 0;                 /* Index of next byte to write to output */
845     if( quote=='[' ) quote = ']';
846     while( z[iIn] ){
847       if( z[iIn]==quote ){
848         if( z[iIn+1]!=quote ) break;
849         z[iOut++] = quote;
850         iIn += 2;
851       }else{
852         z[iOut++] = z[iIn++];
853       }
854     }
855     z[iOut] = '\0';
856   }
857 }
858 
859 #ifndef SQLITE_OMIT_VIRTUALTABLE
860 /*
861 ** Connect to or create a vfslog virtual table.
862 */
863 static int vlogConnect(
864   sqlite3 *db,
865   void *pAux,
866   int argc, const char *const*argv,
867   sqlite3_vtab **ppVtab,
868   char **pzErr
869 ){
870   sqlite3_vfs *pVfs;              /* VFS used to read log file */
871   int flags;                      /* flags passed to pVfs->xOpen() */
872   VfslogVtab *p;
873   int rc;
874   int nByte;
875   char *zFile;
876 
877   *ppVtab = 0;
878   pVfs = sqlite3_vfs_find(0);
879   nByte = sizeof(VfslogVtab) + pVfs->szOsFile + pVfs->mxPathname;
880   p = sqlite3_malloc(nByte);
881   if( p==0 ) return SQLITE_NOMEM;
882   memset(p, 0, nByte);
883 
884   p->pFd = (sqlite3_file *)&p[1];
885   p->zFile = &((char *)p->pFd)[pVfs->szOsFile];
886 
887   zFile = sqlite3_mprintf("%s", argv[3]);
888   if( !zFile ){
889     sqlite3_free(p);
890     return SQLITE_NOMEM;
891   }
892   dequote(zFile);
893   pVfs->xFullPathname(pVfs, zFile, pVfs->mxPathname, p->zFile);
894   sqlite3_free(zFile);
895 
896   flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MASTER_JOURNAL;
897   rc = pVfs->xOpen(pVfs, p->zFile, p->pFd, flags, &flags);
898 
899   if( rc==SQLITE_OK ){
900     p->pFd->pMethods->xFileSize(p->pFd, &p->nByte);
901     sqlite3_declare_vtab(db,
902         "CREATE TABLE xxx(event, file, click, rc, size, offset)"
903     );
904     *ppVtab = &p->base;
905   }else{
906     sqlite3_free(p);
907   }
908 
909   return rc;
910 }
911 
912 /*
913 ** There is no "best-index". This virtual table always does a linear
914 ** scan of the binary VFS log file.
915 */
916 static int vlogBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
917   pIdxInfo->estimatedCost = 10.0;
918   return SQLITE_OK;
919 }
920 
921 /*
922 ** Disconnect from or destroy a vfslog virtual table.
923 */
924 static int vlogDisconnect(sqlite3_vtab *pVtab){
925   VfslogVtab *p = (VfslogVtab *)pVtab;
926   if( p->pFd->pMethods ){
927     p->pFd->pMethods->xClose(p->pFd);
928     p->pFd->pMethods = 0;
929   }
930   sqlite3_free(p);
931   return SQLITE_OK;
932 }
933 
934 /*
935 ** Open a new vfslog cursor.
936 */
937 static int vlogOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
938   VfslogCsr *pCsr;                /* Newly allocated cursor object */
939 
940   pCsr = sqlite3_malloc(sizeof(VfslogCsr));
941   if( !pCsr ) return SQLITE_NOMEM;
942   memset(pCsr, 0, sizeof(VfslogCsr));
943   *ppCursor = &pCsr->base;
944   return SQLITE_OK;
945 }
946 
947 /*
948 ** Close a vfslog cursor.
949 */
950 static int vlogClose(sqlite3_vtab_cursor *pCursor){
951   VfslogCsr *p = (VfslogCsr *)pCursor;
952   int i;
953   for(i=0; i<p->nFile; i++){
954     sqlite3_free(p->azFile[i]);
955   }
956   sqlite3_free(p->azFile);
957   sqlite3_free(p->zTransient);
958   sqlite3_free(p);
959   return SQLITE_OK;
960 }
961 
962 /*
963 ** Move a vfslog cursor to the next entry in the file.
964 */
965 static int vlogNext(sqlite3_vtab_cursor *pCursor){
966   VfslogCsr *pCsr = (VfslogCsr *)pCursor;
967   VfslogVtab *p = (VfslogVtab *)pCursor->pVtab;
968   int rc = SQLITE_OK;
969   int nRead;
970 
971   sqlite3_free(pCsr->zTransient);
972   pCsr->zTransient = 0;
973 
974   nRead = 24;
975   if( pCsr->iOffset+nRead<=p->nByte ){
976     int eEvent;
977     rc = p->pFd->pMethods->xRead(p->pFd, pCsr->aBuf, nRead, pCsr->iOffset);
978 
979     eEvent = get32bits(pCsr->aBuf);
980     if( (rc==SQLITE_OK)
981      && (eEvent==OS_OPEN || eEvent==OS_DELETE || eEvent==OS_ACCESS)
982     ){
983       char buf[4];
984       rc = p->pFd->pMethods->xRead(p->pFd, buf, 4, pCsr->iOffset+nRead);
985       nRead += 4;
986       if( rc==SQLITE_OK ){
987         int nStr = get32bits((unsigned char *)buf);
988         char *zStr = sqlite3_malloc(nStr+1);
989         rc = p->pFd->pMethods->xRead(p->pFd, zStr, nStr, pCsr->iOffset+nRead);
990         zStr[nStr] = '\0';
991         nRead += nStr;
992 
993         if( eEvent==OS_OPEN ){
994           int iFileid = get32bits(&pCsr->aBuf[4]);
995           if( iFileid>=pCsr->nFile ){
996             int nNew = sizeof(pCsr->azFile[0])*(iFileid+1);
997             pCsr->azFile = (char **)sqlite3_realloc(pCsr->azFile, nNew);
998             nNew -= sizeof(pCsr->azFile[0])*pCsr->nFile;
999             memset(&pCsr->azFile[pCsr->nFile], 0, nNew);
1000             pCsr->nFile = iFileid+1;
1001           }
1002           sqlite3_free(pCsr->azFile[iFileid]);
1003           pCsr->azFile[iFileid] = zStr;
1004         }else{
1005           pCsr->zTransient = zStr;
1006         }
1007       }
1008     }
1009   }
1010 
1011   pCsr->iRowid += 1;
1012   pCsr->iOffset += nRead;
1013   return rc;
1014 }
1015 
1016 static int vlogEof(sqlite3_vtab_cursor *pCursor){
1017   VfslogCsr *pCsr = (VfslogCsr *)pCursor;
1018   VfslogVtab *p = (VfslogVtab *)pCursor->pVtab;
1019   return (pCsr->iOffset>=p->nByte);
1020 }
1021 
1022 static int vlogFilter(
1023   sqlite3_vtab_cursor *pCursor,
1024   int idxNum, const char *idxStr,
1025   int argc, sqlite3_value **argv
1026 ){
1027   VfslogCsr *pCsr = (VfslogCsr *)pCursor;
1028   pCsr->iRowid = 0;
1029   pCsr->iOffset = 20;
1030   return vlogNext(pCursor);
1031 }
1032 
1033 static int vlogColumn(
1034   sqlite3_vtab_cursor *pCursor,
1035   sqlite3_context *ctx,
1036   int i
1037 ){
1038   unsigned int val;
1039   VfslogCsr *pCsr = (VfslogCsr *)pCursor;
1040 
1041   assert( i<7 );
1042   val = get32bits(&pCsr->aBuf[4*i]);
1043 
1044   switch( i ){
1045     case 0: {
1046       sqlite3_result_text(ctx, vfslog_eventname(val), -1, SQLITE_STATIC);
1047       break;
1048     }
1049     case 1: {
1050       char *zStr = pCsr->zTransient;
1051       if( val!=0 && val<(unsigned)pCsr->nFile ){
1052         zStr = pCsr->azFile[val];
1053       }
1054       sqlite3_result_text(ctx, zStr, -1, SQLITE_TRANSIENT);
1055       break;
1056     }
1057     default:
1058       sqlite3_result_int(ctx, val);
1059       break;
1060   }
1061 
1062   return SQLITE_OK;
1063 }
1064 
1065 static int vlogRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
1066   VfslogCsr *pCsr = (VfslogCsr *)pCursor;
1067   *pRowid = pCsr->iRowid;
1068   return SQLITE_OK;
1069 }
1070 
1071 int sqlite3_vfslog_register(sqlite3 *db){
1072   static sqlite3_module vfslog_module = {
1073     0,                            /* iVersion */
1074     vlogConnect,                /* xCreate */
1075     vlogConnect,                /* xConnect */
1076     vlogBestIndex,              /* xBestIndex */
1077     vlogDisconnect,             /* xDisconnect */
1078     vlogDisconnect,             /* xDestroy */
1079     vlogOpen,                   /* xOpen - open a cursor */
1080     vlogClose,                  /* xClose - close a cursor */
1081     vlogFilter,                 /* xFilter - configure scan constraints */
1082     vlogNext,                   /* xNext - advance a cursor */
1083     vlogEof,                    /* xEof - check for end of scan */
1084     vlogColumn,                 /* xColumn - read data */
1085     vlogRowid,                  /* xRowid - read data */
1086     0,                            /* xUpdate */
1087     0,                            /* xBegin */
1088     0,                            /* xSync */
1089     0,                            /* xCommit */
1090     0,                            /* xRollback */
1091     0,                            /* xFindMethod */
1092     0,                            /* xRename */
1093   };
1094 
1095   sqlite3_create_module(db, "vfslog", &vfslog_module, 0);
1096   return SQLITE_OK;
1097 }
1098 #endif /* SQLITE_OMIT_VIRTUALTABLE */
1099 
1100 /**************************************************************************
1101 ***************************************************************************
1102 ** Tcl interface starts here.
1103 */
1104 
1105 #if defined(SQLITE_TEST) || defined(TCLSH)
1106 
1107 #if defined(INCLUDE_SQLITE_TCL_H)
1108 #  include "sqlite_tcl.h"
1109 #else
1110 #  include "tcl.h"
1111 #  ifndef SQLITE_TCLAPI
1112 #    define SQLITE_TCLAPI
1113 #  endif
1114 #endif
1115 
1116 static int SQLITE_TCLAPI test_vfslog(
1117   void *clientData,
1118   Tcl_Interp *interp,
1119   int objc,
1120   Tcl_Obj *CONST objv[]
1121 ){
1122   struct SqliteDb { sqlite3 *db; };
1123   sqlite3 *db;
1124   Tcl_CmdInfo cmdInfo;
1125   int rc = SQLITE_ERROR;
1126 
1127   static const char *strs[] = { "annotate", "finalize", "new",  "register", 0 };
1128   enum VL_enum { VL_ANNOTATE, VL_FINALIZE, VL_NEW, VL_REGISTER };
1129   int iSub;
1130 
1131   if( objc<2 ){
1132     Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
1133     return TCL_ERROR;
1134   }
1135   if( Tcl_GetIndexFromObj(interp, objv[1], strs, "sub-command", 0, &iSub) ){
1136     return TCL_ERROR;
1137   }
1138 
1139   switch( (enum VL_enum)iSub ){
1140     case VL_ANNOTATE: {
1141       char *zVfs;
1142       char *zMsg;
1143       if( objc!=4 ){
1144         Tcl_WrongNumArgs(interp, 3, objv, "VFS");
1145         return TCL_ERROR;
1146       }
1147       zVfs = Tcl_GetString(objv[2]);
1148       zMsg = Tcl_GetString(objv[3]);
1149       rc = sqlite3_vfslog_annotate(zVfs, zMsg);
1150       if( rc!=SQLITE_OK ){
1151         Tcl_AppendResult(interp, "failed", 0);
1152         return TCL_ERROR;
1153       }
1154       break;
1155     }
1156     case VL_FINALIZE: {
1157       char *zVfs;
1158       if( objc!=3 ){
1159         Tcl_WrongNumArgs(interp, 2, objv, "VFS");
1160         return TCL_ERROR;
1161       }
1162       zVfs = Tcl_GetString(objv[2]);
1163       rc = sqlite3_vfslog_finalize(zVfs);
1164       if( rc!=SQLITE_OK ){
1165         Tcl_AppendResult(interp, "failed", 0);
1166         return TCL_ERROR;
1167       }
1168       break;
1169     };
1170 
1171     case VL_NEW: {
1172       char *zVfs;
1173       char *zParent;
1174       char *zLog;
1175       if( objc!=5 ){
1176         Tcl_WrongNumArgs(interp, 2, objv, "VFS PARENT LOGFILE");
1177         return TCL_ERROR;
1178       }
1179       zVfs = Tcl_GetString(objv[2]);
1180       zParent = Tcl_GetString(objv[3]);
1181       zLog = Tcl_GetString(objv[4]);
1182       if( *zParent=='\0' ) zParent = 0;
1183       rc = sqlite3_vfslog_new(zVfs, zParent, zLog);
1184       if( rc!=SQLITE_OK ){
1185         Tcl_AppendResult(interp, "failed", 0);
1186         return TCL_ERROR;
1187       }
1188       break;
1189     };
1190 
1191     case VL_REGISTER: {
1192       char *zDb;
1193       if( objc!=3 ){
1194         Tcl_WrongNumArgs(interp, 2, objv, "DB");
1195         return TCL_ERROR;
1196       }
1197 #ifdef SQLITE_OMIT_VIRTUALTABLE
1198       Tcl_AppendResult(interp, "vfslog not available because of "
1199                                "SQLITE_OMIT_VIRTUALTABLE", (void*)0);
1200       return TCL_ERROR;
1201 #else
1202       zDb = Tcl_GetString(objv[2]);
1203       if( Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){
1204         db = ((struct SqliteDb*)cmdInfo.objClientData)->db;
1205         rc = sqlite3_vfslog_register(db);
1206       }
1207       if( rc!=SQLITE_OK ){
1208         Tcl_AppendResult(interp, "bad sqlite3 handle: ", zDb, (void*)0);
1209         return TCL_ERROR;
1210       }
1211       break;
1212 #endif
1213     }
1214   }
1215 
1216   return TCL_OK;
1217 }
1218 
1219 int SqlitetestOsinst_Init(Tcl_Interp *interp){
1220   Tcl_CreateObjCommand(interp, "vfslog", test_vfslog, 0, 0);
1221   return TCL_OK;
1222 }
1223 
1224 #endif /* SQLITE_TEST */
1225