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