| /* | 
 | ** 2008 April 10 | 
 | ** | 
 | ** The author disclaims copyright to this source code.  In place of | 
 | ** a legal notice, here is a blessing: | 
 | ** | 
 | **    May you do good and not evil. | 
 | **    May you find forgiveness for yourself and forgive others. | 
 | **    May you share freely, never taking more than you give. | 
 | ** | 
 | ****************************************************************************** | 
 | ** | 
 | ** This file contains the implementation of an SQLite vfs wrapper that | 
 | ** adds instrumentation to all vfs and file methods. C and Tcl interfaces | 
 | ** are provided to control the instrumentation. | 
 | */ | 
 |  | 
 | /* | 
 | ** This module contains code for a wrapper VFS that causes a log of | 
 | ** most VFS calls to be written into a nominated file on disk. The log  | 
 | ** is stored in a compressed binary format to reduce the amount of IO  | 
 | ** overhead introduced into the application by logging. | 
 | ** | 
 | ** All calls on sqlite3_file objects except xFileControl() are logged. | 
 | ** Additionally, calls to the xAccess(), xOpen(), and xDelete() | 
 | ** methods are logged. The other sqlite3_vfs object methods (xDlXXX, | 
 | ** xRandomness, xSleep, xCurrentTime, xGetLastError and xCurrentTimeInt64)  | 
 | ** are not logged. | 
 | ** | 
 | ** The binary log files are read using a virtual table implementation | 
 | ** also contained in this file.  | 
 | ** | 
 | ** CREATING LOG FILES: | 
 | ** | 
 | **       int sqlite3_vfslog_new( | 
 | **         const char *zVfs,          // Name of new VFS | 
 | **         const char *zParentVfs,    // Name of parent VFS (or NULL) | 
 | **         const char *zLog           // Name of log file to write to | 
 | **       ); | 
 | ** | 
 | **       int sqlite3_vfslog_finalize(const char *zVfs); | 
 | ** | 
 | ** ANNOTATING LOG FILES: | 
 | ** | 
 | **   To write an arbitrary message into a log file: | 
 | ** | 
 | **       int sqlite3_vfslog_annotate(const char *zVfs, const char *zMsg); | 
 | ** | 
 | ** READING LOG FILES: | 
 | ** | 
 | **   Log files are read using the "vfslog" virtual table implementation | 
 | **   in this file. To register the virtual table with SQLite, use: | 
 | ** | 
 | **       int sqlite3_vfslog_register(sqlite3 *db); | 
 | ** | 
 | **   Then, if the log file is named "vfs.log", the following SQL command: | 
 | ** | 
 | **       CREATE VIRTUAL TABLE v USING vfslog('vfs.log'); | 
 | ** | 
 | **   creates a virtual table with 6 columns, as follows: | 
 | ** | 
 | **       CREATE TABLE v( | 
 | **         event    TEXT,             // "xOpen", "xRead" etc. | 
 | **         file     TEXT,             // Name of file this call applies to | 
 | **         time     INTEGER,          // Timestamp | 
 | **         clicks   INTEGER,          // Time spent in call | 
 | **         rc       INTEGER,          // Return value | 
 | **         size     INTEGER,          // Bytes read or written | 
 | **         offset   INTEGER           // File offset read or written | 
 | **       ); | 
 | */ | 
 |  | 
 | #include "sqlite3.h" | 
 |  | 
 | #include "os_setup.h" | 
 | #if SQLITE_OS_WIN | 
 | #  include "os_win.h" | 
 | #endif | 
 |  | 
 | #include <string.h> | 
 | #include <assert.h> | 
 |  | 
 |  | 
 | /* | 
 | ** Maximum pathname length supported by the vfslog backend. | 
 | */ | 
 | #define INST_MAX_PATHNAME 512 | 
 |  | 
 | #define OS_ACCESS            1 | 
 | #define OS_CHECKRESERVEDLOCK 2 | 
 | #define OS_CLOSE             3 | 
 | #define OS_CURRENTTIME       4 | 
 | #define OS_DELETE            5 | 
 | #define OS_DEVCHAR           6 | 
 | #define OS_FILECONTROL       7 | 
 | #define OS_FILESIZE          8 | 
 | #define OS_FULLPATHNAME      9 | 
 | #define OS_LOCK              11 | 
 | #define OS_OPEN              12 | 
 | #define OS_RANDOMNESS        13 | 
 | #define OS_READ              14  | 
 | #define OS_SECTORSIZE        15 | 
 | #define OS_SLEEP             16 | 
 | #define OS_SYNC              17 | 
 | #define OS_TRUNCATE          18 | 
 | #define OS_UNLOCK            19 | 
 | #define OS_WRITE             20 | 
 | #define OS_SHMUNMAP          22 | 
 | #define OS_SHMMAP            23 | 
 | #define OS_SHMLOCK           25 | 
 | #define OS_SHMBARRIER        26 | 
 | #define OS_ANNOTATE          28 | 
 |  | 
 | #define OS_NUMEVENTS         29 | 
 |  | 
 | #define VFSLOG_BUFFERSIZE 8192 | 
 |  | 
 | typedef struct VfslogVfs VfslogVfs; | 
 | typedef struct VfslogFile VfslogFile; | 
 |  | 
 | struct VfslogVfs { | 
 |   sqlite3_vfs base;               /* VFS methods */ | 
 |   sqlite3_vfs *pVfs;              /* Parent VFS */ | 
 |   int iNextFileId;                /* Next file id */ | 
 |   sqlite3_file *pLog;             /* Log file handle */ | 
 |   sqlite3_int64 iOffset;          /* Log file offset of start of write buffer */ | 
 |   int nBuf;                       /* Number of valid bytes in aBuf[] */ | 
 |   char aBuf[VFSLOG_BUFFERSIZE];   /* Write buffer */ | 
 | }; | 
 |  | 
 | struct VfslogFile { | 
 |   sqlite3_file base;              /* IO methods */ | 
 |   sqlite3_file *pReal;            /* Underlying file handle */ | 
 |   sqlite3_vfs *pVfslog;           /* Associated VsflogVfs object */ | 
 |   int iFileId;                    /* File id number */ | 
 | }; | 
 |  | 
 | #define REALVFS(p) (((VfslogVfs *)(p))->pVfs) | 
 |  | 
 |  | 
 |  | 
 | /* | 
 | ** Method declarations for vfslog_file. | 
 | */ | 
 | static int vfslogClose(sqlite3_file*); | 
 | static int vfslogRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); | 
 | static int vfslogWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); | 
 | static int vfslogTruncate(sqlite3_file*, sqlite3_int64 size); | 
 | static int vfslogSync(sqlite3_file*, int flags); | 
 | static int vfslogFileSize(sqlite3_file*, sqlite3_int64 *pSize); | 
 | static int vfslogLock(sqlite3_file*, int); | 
 | static int vfslogUnlock(sqlite3_file*, int); | 
 | static int vfslogCheckReservedLock(sqlite3_file*, int *pResOut); | 
 | static int vfslogFileControl(sqlite3_file*, int op, void *pArg); | 
 | static int vfslogSectorSize(sqlite3_file*); | 
 | static int vfslogDeviceCharacteristics(sqlite3_file*); | 
 |  | 
 | static int vfslogShmLock(sqlite3_file *pFile, int ofst, int n, int flags); | 
 | static int vfslogShmMap(sqlite3_file *pFile,int,int,int,volatile void **); | 
 | static void vfslogShmBarrier(sqlite3_file*); | 
 | static int vfslogShmUnmap(sqlite3_file *pFile, int deleteFlag); | 
 |  | 
 | /* | 
 | ** Method declarations for vfslog_vfs. | 
 | */ | 
 | static int vfslogOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); | 
 | static int vfslogDelete(sqlite3_vfs*, const char *zName, int syncDir); | 
 | static int vfslogAccess(sqlite3_vfs*, const char *zName, int flags, int *); | 
 | static int vfslogFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); | 
 | static void *vfslogDlOpen(sqlite3_vfs*, const char *zFilename); | 
 | static void vfslogDlError(sqlite3_vfs*, int nByte, char *zErrMsg); | 
 | static void (*vfslogDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void); | 
 | static void vfslogDlClose(sqlite3_vfs*, void*); | 
 | static int vfslogRandomness(sqlite3_vfs*, int nByte, char *zOut); | 
 | static int vfslogSleep(sqlite3_vfs*, int microseconds); | 
 | static int vfslogCurrentTime(sqlite3_vfs*, double*); | 
 |  | 
 | static int vfslogGetLastError(sqlite3_vfs*, int, char *); | 
 | static int vfslogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); | 
 |  | 
 | static sqlite3_vfs vfslog_vfs = { | 
 |   1,                              /* iVersion */ | 
 |   sizeof(VfslogFile),             /* szOsFile */ | 
 |   INST_MAX_PATHNAME,              /* mxPathname */ | 
 |   0,                              /* pNext */ | 
 |   0,                              /* zName */ | 
 |   0,                              /* pAppData */ | 
 |   vfslogOpen,                     /* xOpen */ | 
 |   vfslogDelete,                   /* xDelete */ | 
 |   vfslogAccess,                   /* xAccess */ | 
 |   vfslogFullPathname,             /* xFullPathname */ | 
 |   vfslogDlOpen,                   /* xDlOpen */ | 
 |   vfslogDlError,                  /* xDlError */ | 
 |   vfslogDlSym,                    /* xDlSym */ | 
 |   vfslogDlClose,                  /* xDlClose */ | 
 |   vfslogRandomness,               /* xRandomness */ | 
 |   vfslogSleep,                    /* xSleep */ | 
 |   vfslogCurrentTime,              /* xCurrentTime */ | 
 |   vfslogGetLastError,             /* xGetLastError */ | 
 |   vfslogCurrentTimeInt64          /* xCurrentTime */ | 
 | }; | 
 |  | 
 | static sqlite3_io_methods vfslog_io_methods = { | 
 |   2,                              /* iVersion */ | 
 |   vfslogClose,                    /* xClose */ | 
 |   vfslogRead,                     /* xRead */ | 
 |   vfslogWrite,                    /* xWrite */ | 
 |   vfslogTruncate,                 /* xTruncate */ | 
 |   vfslogSync,                     /* xSync */ | 
 |   vfslogFileSize,                 /* xFileSize */ | 
 |   vfslogLock,                     /* xLock */ | 
 |   vfslogUnlock,                   /* xUnlock */ | 
 |   vfslogCheckReservedLock,        /* xCheckReservedLock */ | 
 |   vfslogFileControl,              /* xFileControl */ | 
 |   vfslogSectorSize,               /* xSectorSize */ | 
 |   vfslogDeviceCharacteristics,    /* xDeviceCharacteristics */ | 
 |   vfslogShmMap,                   /* xShmMap */ | 
 |   vfslogShmLock,                  /* xShmLock */ | 
 |   vfslogShmBarrier,               /* xShmBarrier */ | 
 |   vfslogShmUnmap                  /* xShmUnmap */ | 
 | }; | 
 |  | 
 | #if SQLITE_OS_UNIX && !defined(NO_GETTOD) | 
 | #include <sys/time.h> | 
 | static sqlite3_uint64 vfslog_time(){ | 
 |   struct timeval sTime; | 
 |   gettimeofday(&sTime, 0); | 
 |   return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000; | 
 | } | 
 | #elif SQLITE_OS_WIN | 
 | #include <time.h> | 
 | static sqlite3_uint64 vfslog_time(){ | 
 |   FILETIME ft; | 
 |   sqlite3_uint64 u64time = 0; | 
 |   | 
 |   GetSystemTimeAsFileTime(&ft); | 
 |  | 
 |   u64time |= ft.dwHighDateTime; | 
 |   u64time <<= 32; | 
 |   u64time |= ft.dwLowDateTime; | 
 |  | 
 |   /* ft is 100-nanosecond intervals, we want microseconds */ | 
 |   return u64time /(sqlite3_uint64)10; | 
 | } | 
 | #else | 
 | static sqlite3_uint64 vfslog_time(){ | 
 |   return 0; | 
 | } | 
 | #endif | 
 |  | 
 | static void vfslog_call( | 
 | sqlite3_vfs *, int, int, sqlite3_uint64, sqlite3_int64, int, int, sqlite_int64); | 
 | static void vfslog_string(sqlite3_vfs *, const char *); | 
 |  | 
 | /* | 
 | ** Close an vfslog-file. | 
 | */ | 
 | static int vfslogClose(sqlite3_file *pFile){ | 
 |   sqlite3_uint64 t, t2; | 
 |   int rc = SQLITE_OK; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |  | 
 |   t = vfslog_time(); | 
 |   if( p->pReal->pMethods ){ | 
 |     rc = p->pReal->pMethods->xClose(p->pReal); | 
 |   } | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_CLOSE, p->iFileId, t, t2, rc, 0, 0); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Read data from an vfslog-file. | 
 | */ | 
 | static int vfslogRead( | 
 |   sqlite3_file *pFile,  | 
 |   void *zBuf,  | 
 |   int iAmt,  | 
 |   sqlite_int64 iOfst | 
 | ){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   t = vfslog_time(); | 
 |   rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_READ, p->iFileId, t, t2, rc, iAmt, iOfst); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Write data to an vfslog-file. | 
 | */ | 
 | static int vfslogWrite( | 
 |   sqlite3_file *pFile, | 
 |   const void *z, | 
 |   int iAmt, | 
 |   sqlite_int64 iOfst | 
 | ){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   t = vfslog_time(); | 
 |   rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_WRITE, p->iFileId, t, t2, rc, iAmt, iOfst); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Truncate an vfslog-file. | 
 | */ | 
 | static int vfslogTruncate(sqlite3_file *pFile, sqlite_int64 size){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   t = vfslog_time(); | 
 |   rc = p->pReal->pMethods->xTruncate(p->pReal, size); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_TRUNCATE, p->iFileId, t, t2, rc, 0, size); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Sync an vfslog-file. | 
 | */ | 
 | static int vfslogSync(sqlite3_file *pFile, int flags){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   t = vfslog_time(); | 
 |   rc = p->pReal->pMethods->xSync(p->pReal, flags); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_SYNC, p->iFileId, t, t2, rc, flags, 0); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Return the current file-size of an vfslog-file. | 
 | */ | 
 | static int vfslogFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   t = vfslog_time(); | 
 |   rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_FILESIZE, p->iFileId, t, t2, rc, 0, *pSize); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Lock an vfslog-file. | 
 | */ | 
 | static int vfslogLock(sqlite3_file *pFile, int eLock){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   t = vfslog_time(); | 
 |   rc = p->pReal->pMethods->xLock(p->pReal, eLock); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_LOCK, p->iFileId, t, t2, rc, eLock, 0); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Unlock an vfslog-file. | 
 | */ | 
 | static int vfslogUnlock(sqlite3_file *pFile, int eLock){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   t = vfslog_time(); | 
 |   rc = p->pReal->pMethods->xUnlock(p->pReal, eLock); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_UNLOCK, p->iFileId, t, t2, rc, eLock, 0); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Check if another file-handle holds a RESERVED lock on an vfslog-file. | 
 | */ | 
 | static int vfslogCheckReservedLock(sqlite3_file *pFile, int *pResOut){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   t = vfslog_time(); | 
 |   rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_CHECKRESERVEDLOCK, p->iFileId, t,t2,rc,*pResOut,0); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** File control method. For custom operations on an vfslog-file. | 
 | */ | 
 | static int vfslogFileControl(sqlite3_file *pFile, int op, void *pArg){ | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   int rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg); | 
 |   if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){ | 
 |     *(char**)pArg = sqlite3_mprintf("vfslog/%z", *(char**)pArg); | 
 |   } | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Return the sector-size in bytes for an vfslog-file. | 
 | */ | 
 | static int vfslogSectorSize(sqlite3_file *pFile){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   t = vfslog_time(); | 
 |   rc = p->pReal->pMethods->xSectorSize(p->pReal); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_SECTORSIZE, p->iFileId, t, t2, rc, 0, 0); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Return the device characteristic flags supported by an vfslog-file. | 
 | */ | 
 | static int vfslogDeviceCharacteristics(sqlite3_file *pFile){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   t = vfslog_time(); | 
 |   rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_DEVCHAR, p->iFileId, t, t2, rc, 0, 0); | 
 |   return rc; | 
 | } | 
 |  | 
 | static int vfslogShmLock(sqlite3_file *pFile, int ofst, int n, int flags){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   t = vfslog_time(); | 
 |   rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_SHMLOCK, p->iFileId, t, t2, rc, 0, 0); | 
 |   return rc; | 
 | } | 
 | static int vfslogShmMap( | 
 |   sqlite3_file *pFile,  | 
 |   int iRegion,  | 
 |   int szRegion,  | 
 |   int isWrite,  | 
 |   volatile void **pp | 
 | ){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   t = vfslog_time(); | 
 |   rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_SHMMAP, p->iFileId, t, t2, rc, 0, 0); | 
 |   return rc; | 
 | } | 
 | static void vfslogShmBarrier(sqlite3_file *pFile){ | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   t = vfslog_time(); | 
 |   p->pReal->pMethods->xShmBarrier(p->pReal); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_SHMBARRIER, p->iFileId, t, t2, SQLITE_OK, 0, 0); | 
 | } | 
 | static int vfslogShmUnmap(sqlite3_file *pFile, int deleteFlag){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   t = vfslog_time(); | 
 |   rc = p->pReal->pMethods->xShmUnmap(p->pReal, deleteFlag); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(p->pVfslog, OS_SHMUNMAP, p->iFileId, t, t2, rc, 0, 0); | 
 |   return rc; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 | ** Open an vfslog file handle. | 
 | */ | 
 | static int vfslogOpen( | 
 |   sqlite3_vfs *pVfs, | 
 |   const char *zName, | 
 |   sqlite3_file *pFile, | 
 |   int flags, | 
 |   int *pOutFlags | 
 | ){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   VfslogFile *p = (VfslogFile *)pFile; | 
 |   VfslogVfs *pLog = (VfslogVfs *)pVfs; | 
 |  | 
 |   pFile->pMethods = &vfslog_io_methods; | 
 |   p->pReal = (sqlite3_file *)&p[1]; | 
 |   p->pVfslog = pVfs; | 
 |   p->iFileId = ++pLog->iNextFileId; | 
 |  | 
 |   t = vfslog_time(); | 
 |   rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags); | 
 |   t2 = vfslog_time() - t; | 
 |  | 
 |   vfslog_call(pVfs, OS_OPEN, p->iFileId, t, t2, rc, 0, 0); | 
 |   vfslog_string(pVfs, zName); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Delete the file located at zPath. If the dirSync argument is true, | 
 | ** ensure the file-system modifications are synced to disk before | 
 | ** returning. | 
 | */ | 
 | static int vfslogDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   t = vfslog_time(); | 
 |   rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(pVfs, OS_DELETE, 0, t, t2, rc, dirSync, 0); | 
 |   vfslog_string(pVfs, zPath); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Test for access permissions. Return true if the requested permission | 
 | ** is available, or false otherwise. | 
 | */ | 
 | static int vfslogAccess( | 
 |   sqlite3_vfs *pVfs,  | 
 |   const char *zPath,  | 
 |   int flags,  | 
 |   int *pResOut | 
 | ){ | 
 |   int rc; | 
 |   sqlite3_uint64 t, t2; | 
 |   t = vfslog_time(); | 
 |   rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut); | 
 |   t2 = vfslog_time() - t; | 
 |   vfslog_call(pVfs, OS_ACCESS, 0, t, t2, rc, flags, (sqlite3_int64)*pResOut); | 
 |   vfslog_string(pVfs, zPath); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Populate buffer zOut with the full canonical pathname corresponding | 
 | ** to the pathname in zPath. zOut is guaranteed to point to a buffer | 
 | ** of at least (INST_MAX_PATHNAME+1) bytes. | 
 | */ | 
 | static int vfslogFullPathname( | 
 |   sqlite3_vfs *pVfs,  | 
 |   const char *zPath,  | 
 |   int nOut,  | 
 |   char *zOut | 
 | ){ | 
 |   return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut); | 
 | } | 
 |  | 
 | /* | 
 | ** Open the dynamic library located at zPath and return a handle. | 
 | */ | 
 | static void *vfslogDlOpen(sqlite3_vfs *pVfs, const char *zPath){ | 
 |   return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath); | 
 | } | 
 |  | 
 | /* | 
 | ** Populate the buffer zErrMsg (size nByte bytes) with a human readable | 
 | ** utf-8 string describing the most recent error encountered associated  | 
 | ** with dynamic libraries. | 
 | */ | 
 | static void vfslogDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ | 
 |   REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg); | 
 | } | 
 |  | 
 | /* | 
 | ** Return a pointer to the symbol zSymbol in the dynamic library pHandle. | 
 | */ | 
 | static void (*vfslogDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ | 
 |   return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym); | 
 | } | 
 |  | 
 | /* | 
 | ** Close the dynamic library handle pHandle. | 
 | */ | 
 | static void vfslogDlClose(sqlite3_vfs *pVfs, void *pHandle){ | 
 |   REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle); | 
 | } | 
 |  | 
 | /* | 
 | ** Populate the buffer pointed to by zBufOut with nByte bytes of  | 
 | ** random data. | 
 | */ | 
 | static int vfslogRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ | 
 |   return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut); | 
 | } | 
 |  | 
 | /* | 
 | ** Sleep for nMicro microseconds. Return the number of microseconds  | 
 | ** actually slept. | 
 | */ | 
 | static int vfslogSleep(sqlite3_vfs *pVfs, int nMicro){ | 
 |   return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro); | 
 | } | 
 |  | 
 | /* | 
 | ** Return the current time as a Julian Day number in *pTimeOut. | 
 | */ | 
 | static int vfslogCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ | 
 |   return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut); | 
 | } | 
 |  | 
 | static int vfslogGetLastError(sqlite3_vfs *pVfs, int a, char *b){ | 
 |   return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b); | 
 | } | 
 | static int vfslogCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){ | 
 |   return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p); | 
 | } | 
 |  | 
 | static void vfslog_flush(VfslogVfs *p){ | 
 | #ifdef SQLITE_TEST | 
 |   extern int sqlite3_io_error_pending; | 
 |   extern int sqlite3_io_error_persist; | 
 |   extern int sqlite3_diskfull_pending; | 
 |  | 
 |   int pending = sqlite3_io_error_pending; | 
 |   int persist = sqlite3_io_error_persist; | 
 |   int diskfull = sqlite3_diskfull_pending; | 
 |  | 
 |   sqlite3_io_error_pending = 0; | 
 |   sqlite3_io_error_persist = 0; | 
 |   sqlite3_diskfull_pending = 0; | 
 | #endif | 
 |  | 
 |   if( p->nBuf ){ | 
 |     p->pLog->pMethods->xWrite(p->pLog, p->aBuf, p->nBuf, p->iOffset); | 
 |     p->iOffset += p->nBuf; | 
 |     p->nBuf = 0; | 
 |   } | 
 |  | 
 | #ifdef SQLITE_TEST | 
 |   sqlite3_io_error_pending = pending; | 
 |   sqlite3_io_error_persist = persist; | 
 |   sqlite3_diskfull_pending = diskfull; | 
 | #endif | 
 | } | 
 |  | 
 | static void vfslogPut32bits(unsigned char *p, unsigned int v){ | 
 |   p[0] = v>>24; | 
 |   p[1] = (unsigned char)(v>>16); | 
 |   p[2] = (unsigned char)(v>>8); | 
 |   p[3] = (unsigned char)v; | 
 | } | 
 |  | 
 | static void vfslogPut64bits(unsigned char *p, sqlite3_int64 v){ | 
 |   vfslogPut32bits(p, (v >> 32) & 0xFFFFFFFF); | 
 |   vfslogPut32bits(&p[4], v & 0xFFFFFFFF); | 
 | } | 
 |  | 
 | #define VFSLOG_RECORD_SIZE 36 | 
 |  | 
 | static void vfslog_call( | 
 |   sqlite3_vfs *pVfs, | 
 |   int eEvent, | 
 |   int iFileid, | 
 |   sqlite3_uint64 tStamp, | 
 |   sqlite3_int64 nClick, | 
 |   int return_code, | 
 |   int size, | 
 |   sqlite3_int64 offset | 
 | ){ | 
 |   VfslogVfs *p = (VfslogVfs *)pVfs; | 
 |   unsigned char *zRec; | 
 |   if( (VFSLOG_RECORD_SIZE+p->nBuf)>sizeof(p->aBuf) ){ | 
 |     vfslog_flush(p); | 
 |   } | 
 |   zRec = (unsigned char *)&p->aBuf[p->nBuf]; | 
 |   vfslogPut32bits(&zRec[0], eEvent); | 
 |   vfslogPut32bits(&zRec[4], iFileid); | 
 |   vfslogPut64bits(&zRec[8], (sqlite3_int64)tStamp); | 
 |   vfslogPut32bits(&zRec[16], (unsigned int)(nClick&0xffffffff)); | 
 |   vfslogPut32bits(&zRec[20], return_code); | 
 |   vfslogPut32bits(&zRec[24], size); | 
 |   vfslogPut64bits(&zRec[28], offset); | 
 |  | 
 |   p->nBuf += VFSLOG_RECORD_SIZE; | 
 | } | 
 |  | 
 | static void vfslog_string(sqlite3_vfs *pVfs, const char *zStr){ | 
 |   VfslogVfs *p = (VfslogVfs *)pVfs; | 
 |   unsigned char *zRec; | 
 |   int nStr = zStr ? (int)strlen(zStr) : 0; | 
 |   if( (4+nStr+p->nBuf)>sizeof(p->aBuf) ){ | 
 |     vfslog_flush(p); | 
 |   } | 
 |   zRec = (unsigned char *)&p->aBuf[p->nBuf]; | 
 |   vfslogPut32bits(&zRec[0], nStr); | 
 |   if( zStr ){ | 
 |     memcpy(&zRec[4], zStr, nStr); | 
 |   } | 
 |   p->nBuf += (4 + nStr); | 
 | } | 
 |  | 
 | static void vfslog_finalize(VfslogVfs *p){ | 
 |   if( p->pLog->pMethods ){ | 
 |     vfslog_flush(p); | 
 |     p->pLog->pMethods->xClose(p->pLog); | 
 |   } | 
 |   sqlite3_free(p); | 
 | } | 
 |  | 
 | int sqlite3_vfslog_finalize(const char *zVfs){ | 
 |   sqlite3_vfs *pVfs; | 
 |   pVfs = sqlite3_vfs_find(zVfs); | 
 |   if( !pVfs || pVfs->xOpen!=vfslogOpen ){ | 
 |     return SQLITE_ERROR; | 
 |   }  | 
 |   sqlite3_vfs_unregister(pVfs); | 
 |   vfslog_finalize((VfslogVfs *)pVfs); | 
 |   return SQLITE_OK; | 
 | } | 
 |  | 
 | int sqlite3_vfslog_new( | 
 |   const char *zVfs,               /* New VFS name */ | 
 |   const char *zParentVfs,         /* Parent VFS name (or NULL) */ | 
 |   const char *zLog,               /* Log file name */ | 
 |   sqlite3_vfs **ppVfs             /* OUT: New VFS object */ | 
 | ){ | 
 |   VfslogVfs *p; | 
 |   sqlite3_vfs *pParent; | 
 |   int nByte; | 
 |   int flags; | 
 |   int rc; | 
 |   char *zFile; | 
 |   int nVfs; | 
 |  | 
 |   pParent = sqlite3_vfs_find(zParentVfs); | 
 |   if( !pParent ){ | 
 |     return SQLITE_ERROR; | 
 |   } | 
 |  | 
 |   nVfs = (int)strlen(zVfs); | 
 |   nByte = sizeof(VfslogVfs) + pParent->szOsFile + nVfs+1+pParent->mxPathname+1; | 
 |   p = (VfslogVfs *)sqlite3_malloc(nByte); | 
 |   memset(p, 0, nByte); | 
 |  | 
 |   p->pVfs = pParent; | 
 |   p->pLog = (sqlite3_file *)&p[1]; | 
 |   memcpy(&p->base, &vfslog_vfs, sizeof(sqlite3_vfs)); | 
 |   p->base.zName = &((char *)p->pLog)[pParent->szOsFile]; | 
 |   p->base.szOsFile += pParent->szOsFile; | 
 |   memcpy((char *)p->base.zName, zVfs, nVfs); | 
 |  | 
 |   zFile = (char *)&p->base.zName[nVfs+1]; | 
 |   pParent->xFullPathname(pParent, zLog, pParent->mxPathname, zFile); | 
 |  | 
 |   flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MASTER_JOURNAL; | 
 |   pParent->xDelete(pParent, zFile, 0); | 
 |   rc = pParent->xOpen(pParent, zFile, p->pLog, flags, &flags); | 
 |   if( rc==SQLITE_OK ){ | 
 |     memcpy(p->aBuf, "sqlite_ostrace1.....", 20); | 
 |     p->iOffset = 0; | 
 |     p->nBuf = 20; | 
 |     rc = sqlite3_vfs_register((sqlite3_vfs *)p, 0); | 
 |     if( ppVfs ) *ppVfs = (sqlite3_vfs*)p; | 
 |   } | 
 |   if( rc ){ | 
 |     vfslog_finalize(p); | 
 |   } | 
 |   return rc; | 
 | } | 
 |  | 
 | int sqlite3_vfslog_annotate(const char *zVfs, const char *zMsg){ | 
 |   sqlite3_vfs *pVfs; | 
 |   sqlite3_uint64 t = vfslog_time(); | 
 |   pVfs = sqlite3_vfs_find(zVfs); | 
 |   if( !pVfs || pVfs->xOpen!=vfslogOpen ){ | 
 |     return SQLITE_ERROR; | 
 |   }  | 
 |   vfslog_call(pVfs, OS_ANNOTATE, 0, t, 0, 0, 0, 0); | 
 |   vfslog_string(pVfs, zMsg); | 
 |   return SQLITE_OK; | 
 | } | 
 |  | 
 | static const char *vfslog_eventname(int eEvent){ | 
 |   const char *zEvent = 0; | 
 |  | 
 |   switch( eEvent ){ | 
 |     case OS_CLOSE:             zEvent = "xClose"; break; | 
 |     case OS_READ:              zEvent = "xRead"; break; | 
 |     case OS_WRITE:             zEvent = "xWrite"; break; | 
 |     case OS_TRUNCATE:          zEvent = "xTruncate"; break; | 
 |     case OS_SYNC:              zEvent = "xSync"; break; | 
 |     case OS_FILESIZE:          zEvent = "xFilesize"; break; | 
 |     case OS_LOCK:              zEvent = "xLock"; break; | 
 |     case OS_UNLOCK:            zEvent = "xUnlock"; break; | 
 |     case OS_CHECKRESERVEDLOCK: zEvent = "xCheckResLock"; break; | 
 |     case OS_FILECONTROL:       zEvent = "xFileControl"; break; | 
 |     case OS_SECTORSIZE:        zEvent = "xSectorSize"; break; | 
 |     case OS_DEVCHAR:           zEvent = "xDeviceChar"; break; | 
 |     case OS_OPEN:              zEvent = "xOpen"; break; | 
 |     case OS_DELETE:            zEvent = "xDelete"; break; | 
 |     case OS_ACCESS:            zEvent = "xAccess"; break; | 
 |     case OS_FULLPATHNAME:      zEvent = "xFullPathname"; break; | 
 |     case OS_RANDOMNESS:        zEvent = "xRandomness"; break; | 
 |     case OS_SLEEP:             zEvent = "xSleep"; break; | 
 |     case OS_CURRENTTIME:       zEvent = "xCurrentTime"; break; | 
 |  | 
 |     case OS_SHMUNMAP:          zEvent = "xShmUnmap"; break; | 
 |     case OS_SHMLOCK:           zEvent = "xShmLock"; break; | 
 |     case OS_SHMBARRIER:        zEvent = "xShmBarrier"; break; | 
 |     case OS_SHMMAP:            zEvent = "xShmMap"; break; | 
 |  | 
 |     case OS_ANNOTATE:          zEvent = "annotation"; break; | 
 |   } | 
 |  | 
 |   return zEvent; | 
 | } | 
 |  | 
 | typedef struct VfslogVtab VfslogVtab; | 
 | typedef struct VfslogCsr VfslogCsr; | 
 |  | 
 | /* | 
 | ** Virtual table type for the vfslog reader module. | 
 | */ | 
 | struct VfslogVtab { | 
 |   sqlite3_vtab base;              /* Base class */ | 
 |   sqlite3_file *pFd;              /* File descriptor open on vfslog file */ | 
 |   sqlite3_int64 nByte;            /* Size of file in bytes */ | 
 |   char *zFile;                    /* File name for pFd */ | 
 | }; | 
 |  | 
 | /* | 
 | ** Virtual table cursor type for the vfslog reader module. | 
 | */ | 
 | struct VfslogCsr { | 
 |   sqlite3_vtab_cursor base;       /* Base class */ | 
 |   sqlite3_int64 iRowid;           /* Current rowid. */ | 
 |   sqlite3_int64 iOffset;          /* Offset of next record in file */ | 
 |   char *zTransient;               /* Transient 'file' string */ | 
 |   int nFile;                      /* Size of array azFile[] */ | 
 |   char **azFile;                  /* File strings */ | 
 |   unsigned char aBuf[1024];       /* Current vfs log entry (read from file) */ | 
 | }; | 
 |  | 
 | static unsigned int get32bits(unsigned char *p){ | 
 |   return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; | 
 | } | 
 |  | 
 | /* | 
 | ** The argument must point to a buffer containing a nul-terminated string. | 
 | ** If the string begins with an SQL quote character it is overwritten by | 
 | ** the dequoted version. Otherwise the buffer is left unmodified. | 
 | */ | 
 | static void dequote(char *z){ | 
 |   char quote;                     /* Quote character (if any ) */ | 
 |   quote = z[0]; | 
 |   if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){ | 
 |     int iIn = 1;                  /* Index of next byte to read from input */ | 
 |     int iOut = 0;                 /* Index of next byte to write to output */ | 
 |     if( quote=='[' ) quote = ']';   | 
 |     while( z[iIn] ){ | 
 |       if( z[iIn]==quote ){ | 
 |         if( z[iIn+1]!=quote ) break; | 
 |         z[iOut++] = quote; | 
 |         iIn += 2; | 
 |       }else{ | 
 |         z[iOut++] = z[iIn++]; | 
 |       } | 
 |     } | 
 |     z[iOut] = '\0'; | 
 |   } | 
 | } | 
 |  | 
 | #ifndef SQLITE_OMIT_VIRTUALTABLE | 
 | /* | 
 | ** Connect to or create a vfslog virtual table. | 
 | */ | 
 | static int vlogConnect( | 
 |   sqlite3 *db, | 
 |   void *pAux, | 
 |   int argc, const char *const*argv, | 
 |   sqlite3_vtab **ppVtab, | 
 |   char **pzErr | 
 | ){ | 
 |   sqlite3_vfs *pVfs;              /* VFS used to read log file */ | 
 |   int flags;                      /* flags passed to pVfs->xOpen() */ | 
 |   VfslogVtab *p; | 
 |   int rc; | 
 |   int nByte; | 
 |   char *zFile; | 
 |  | 
 |   *ppVtab = 0; | 
 |   pVfs = sqlite3_vfs_find(0); | 
 |   nByte = sizeof(VfslogVtab) + pVfs->szOsFile + pVfs->mxPathname; | 
 |   p = sqlite3_malloc(nByte); | 
 |   if( p==0 ) return SQLITE_NOMEM; | 
 |   memset(p, 0, nByte); | 
 |  | 
 |   p->pFd = (sqlite3_file *)&p[1]; | 
 |   p->zFile = &((char *)p->pFd)[pVfs->szOsFile]; | 
 |  | 
 |   zFile = sqlite3_mprintf("%s", argv[3]); | 
 |   if( !zFile ){ | 
 |     sqlite3_free(p); | 
 |     return SQLITE_NOMEM; | 
 |   } | 
 |   dequote(zFile); | 
 |   pVfs->xFullPathname(pVfs, zFile, pVfs->mxPathname, p->zFile); | 
 |   sqlite3_free(zFile); | 
 |  | 
 |   flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MASTER_JOURNAL; | 
 |   rc = pVfs->xOpen(pVfs, p->zFile, p->pFd, flags, &flags); | 
 |  | 
 |   if( rc==SQLITE_OK ){ | 
 |     p->pFd->pMethods->xFileSize(p->pFd, &p->nByte); | 
 |     sqlite3_declare_vtab(db,  | 
 |         "CREATE TABLE xxx(event, file, time, click, rc, size, offset)" | 
 |     ); | 
 |     *ppVtab = &p->base; | 
 |   }else{ | 
 |     sqlite3_free(p); | 
 |   } | 
 |  | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** There is no "best-index". This virtual table always does a linear | 
 | ** scan of the binary VFS log file. | 
 | */ | 
 | static int vlogBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ | 
 |   pIdxInfo->estimatedCost = 10.0; | 
 |   return SQLITE_OK; | 
 | } | 
 |  | 
 | /* | 
 | ** Disconnect from or destroy a vfslog virtual table. | 
 | */ | 
 | static int vlogDisconnect(sqlite3_vtab *pVtab){ | 
 |   VfslogVtab *p = (VfslogVtab *)pVtab; | 
 |   if( p->pFd->pMethods ){ | 
 |     p->pFd->pMethods->xClose(p->pFd); | 
 |     p->pFd->pMethods = 0; | 
 |   } | 
 |   sqlite3_free(p); | 
 |   return SQLITE_OK; | 
 | } | 
 |  | 
 | /* | 
 | ** Open a new vfslog cursor. | 
 | */ | 
 | static int vlogOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ | 
 |   VfslogCsr *pCsr;                /* Newly allocated cursor object */ | 
 |  | 
 |   pCsr = sqlite3_malloc(sizeof(VfslogCsr)); | 
 |   if( !pCsr ) return SQLITE_NOMEM; | 
 |   memset(pCsr, 0, sizeof(VfslogCsr)); | 
 |   *ppCursor = &pCsr->base; | 
 |   return SQLITE_OK; | 
 | } | 
 |  | 
 | /* | 
 | ** Close a vfslog cursor. | 
 | */ | 
 | static int vlogClose(sqlite3_vtab_cursor *pCursor){ | 
 |   VfslogCsr *p = (VfslogCsr *)pCursor; | 
 |   int i; | 
 |   for(i=0; i<p->nFile; i++){ | 
 |     sqlite3_free(p->azFile[i]); | 
 |   } | 
 |   sqlite3_free(p->azFile); | 
 |   sqlite3_free(p->zTransient); | 
 |   sqlite3_free(p); | 
 |   return SQLITE_OK; | 
 | } | 
 |  | 
 | /* | 
 | ** Move a vfslog cursor to the next entry in the file. | 
 | */ | 
 | static int vlogNext(sqlite3_vtab_cursor *pCursor){ | 
 |   VfslogCsr *pCsr = (VfslogCsr *)pCursor; | 
 |   VfslogVtab *p = (VfslogVtab *)pCursor->pVtab; | 
 |   int rc = SQLITE_OK; | 
 |   int nRead; | 
 |  | 
 |   sqlite3_free(pCsr->zTransient); | 
 |   pCsr->zTransient = 0; | 
 |  | 
 |   nRead = VFSLOG_RECORD_SIZE; | 
 |   if( pCsr->iOffset+nRead<=p->nByte ){ | 
 |     int eEvent; | 
 |     rc = p->pFd->pMethods->xRead(p->pFd, pCsr->aBuf, nRead, pCsr->iOffset); | 
 |  | 
 |     eEvent = get32bits(pCsr->aBuf); | 
 |     if( (rc==SQLITE_OK) | 
 |      && (eEvent==OS_OPEN || eEvent==OS_DELETE || eEvent==OS_ACCESS)  | 
 |     ){ | 
 |       char buf[4]; | 
 |       rc = p->pFd->pMethods->xRead(p->pFd, buf, 4, pCsr->iOffset+nRead); | 
 |       nRead += 4; | 
 |       if( rc==SQLITE_OK ){ | 
 |         int nStr = get32bits((unsigned char *)buf); | 
 |         char *zStr = sqlite3_malloc(nStr+1); | 
 |         if( nStr>0 ){ | 
 |           rc = p->pFd->pMethods->xRead(p->pFd, zStr, nStr, pCsr->iOffset+nRead); | 
 |         } | 
 |         zStr[nStr] = '\0'; | 
 |         nRead += nStr; | 
 |  | 
 |         if( eEvent==OS_OPEN ){ | 
 |           int iFileid = get32bits(&pCsr->aBuf[4]); | 
 |           if( iFileid>=pCsr->nFile ){ | 
 |             int nNew = sizeof(pCsr->azFile[0])*(iFileid+1); | 
 |             pCsr->azFile = (char **)sqlite3_realloc(pCsr->azFile, nNew); | 
 |             nNew -= sizeof(pCsr->azFile[0])*pCsr->nFile; | 
 |             memset(&pCsr->azFile[pCsr->nFile], 0, nNew); | 
 |             pCsr->nFile = iFileid+1; | 
 |           } | 
 |           sqlite3_free(pCsr->azFile[iFileid]); | 
 |           pCsr->azFile[iFileid] = zStr; | 
 |         }else{ | 
 |           pCsr->zTransient = zStr; | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   pCsr->iRowid += 1; | 
 |   pCsr->iOffset += nRead; | 
 |   return rc; | 
 | } | 
 |  | 
 | static int vlogEof(sqlite3_vtab_cursor *pCursor){ | 
 |   VfslogCsr *pCsr = (VfslogCsr *)pCursor; | 
 |   VfslogVtab *p = (VfslogVtab *)pCursor->pVtab; | 
 |   return (pCsr->iOffset>=p->nByte); | 
 | } | 
 |  | 
 | static int vlogFilter( | 
 |   sqlite3_vtab_cursor *pCursor,  | 
 |   int idxNum, const char *idxStr, | 
 |   int argc, sqlite3_value **argv | 
 | ){ | 
 |   VfslogCsr *pCsr = (VfslogCsr *)pCursor; | 
 |   pCsr->iRowid = 0; | 
 |   pCsr->iOffset = 20; | 
 |   return vlogNext(pCursor); | 
 | } | 
 |  | 
 | static int vlogColumn( | 
 |   sqlite3_vtab_cursor *pCursor,  | 
 |   sqlite3_context *ctx,  | 
 |   int i | 
 | ){ | 
 |   VfslogCsr *pCsr = (VfslogCsr *)pCursor; | 
 |  | 
 |   assert( i<7 ); | 
 |  | 
 |   switch( i ){ | 
 |     case 0: { | 
 |       unsigned int val = get32bits(&pCsr->aBuf[0]); | 
 |       sqlite3_result_text(ctx, vfslog_eventname(val), -1, SQLITE_STATIC); | 
 |       break; | 
 |     } | 
 |     case 1: { | 
 |       unsigned int val = get32bits(&pCsr->aBuf[4]); | 
 |       char *zStr = pCsr->zTransient; | 
 |       if( val!=0 && val<(unsigned)pCsr->nFile ){ | 
 |         zStr = pCsr->azFile[val]; | 
 |       } | 
 |       sqlite3_result_text(ctx, zStr, -1, SQLITE_TRANSIENT); | 
 |       break; | 
 |     } | 
 |     case 2: { | 
 |       unsigned int v1 = get32bits(&pCsr->aBuf[8]); | 
 |       unsigned int v2 = get32bits(&pCsr->aBuf[12]); | 
 |       sqlite3_result_int64( | 
 |         ctx,(((sqlite3_int64)v1) << 32) + (sqlite3_int64)v2 | 
 |       ); | 
 |       break; | 
 |     } | 
 |     case 6: { | 
 |       unsigned int v1 = get32bits(&pCsr->aBuf[28]); | 
 |       unsigned int v2 = get32bits(&pCsr->aBuf[32]); | 
 |       sqlite3_result_int64( | 
 |         ctx,(((sqlite3_int64)v1) << 32) + (sqlite3_int64)v2 | 
 |       ); | 
 |       break; | 
 |     } | 
 |     default: { | 
 |       unsigned int val = get32bits(&pCsr->aBuf[4*(i+1)]); | 
 |       sqlite3_result_int(ctx, val); | 
 |       break; | 
 |     } | 
 |   } | 
 |  | 
 |   return SQLITE_OK; | 
 | } | 
 |  | 
 | static int vlogRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ | 
 |   VfslogCsr *pCsr = (VfslogCsr *)pCursor; | 
 |   *pRowid = pCsr->iRowid; | 
 |   return SQLITE_OK; | 
 | } | 
 |  | 
 | int sqlite3_vfslog_register(sqlite3 *db){ | 
 |   static sqlite3_module vfslog_module = { | 
 |     0,                            /* iVersion */ | 
 |     vlogConnect,                /* xCreate */ | 
 |     vlogConnect,                /* xConnect */ | 
 |     vlogBestIndex,              /* xBestIndex */ | 
 |     vlogDisconnect,             /* xDisconnect */ | 
 |     vlogDisconnect,             /* xDestroy */ | 
 |     vlogOpen,                   /* xOpen - open a cursor */ | 
 |     vlogClose,                  /* xClose - close a cursor */ | 
 |     vlogFilter,                 /* xFilter - configure scan constraints */ | 
 |     vlogNext,                   /* xNext - advance a cursor */ | 
 |     vlogEof,                    /* xEof - check for end of scan */ | 
 |     vlogColumn,                 /* xColumn - read data */ | 
 |     vlogRowid,                  /* xRowid - read data */ | 
 |     0,                            /* xUpdate */ | 
 |     0,                            /* xBegin */ | 
 |     0,                            /* xSync */ | 
 |     0,                            /* xCommit */ | 
 |     0,                            /* xRollback */ | 
 |     0,                            /* xFindMethod */ | 
 |     0,                            /* xRename */ | 
 |   }; | 
 |  | 
 |   sqlite3_create_module(db, "vfslog", &vfslog_module, 0); | 
 |   return SQLITE_OK; | 
 | } | 
 | #endif /* SQLITE_OMIT_VIRTUALTABLE */ | 
 |  | 
 | /************************************************************************** | 
 | *************************************************************************** | 
 | ** Tcl interface starts here. | 
 | */ | 
 |  | 
 | #if defined(SQLITE_TEST) || defined(TCLSH) | 
 |  | 
 | #if defined(INCLUDE_SQLITE_TCL_H) | 
 | #  include "sqlite_tcl.h" | 
 | #else | 
 | #  include "tcl.h" | 
 | #  ifndef SQLITE_TCLAPI | 
 | #    define SQLITE_TCLAPI | 
 | #  endif | 
 | #endif | 
 |  | 
 | static int SQLITE_TCLAPI test_vfslog( | 
 |   void *clientData, | 
 |   Tcl_Interp *interp, | 
 |   int objc, | 
 |   Tcl_Obj *CONST objv[] | 
 | ){ | 
 |   struct SqliteDb { sqlite3 *db; }; | 
 |   sqlite3 *db; | 
 |   Tcl_CmdInfo cmdInfo; | 
 |   int rc = SQLITE_ERROR; | 
 |  | 
 |   static const char *strs[] = { "annotate", "finalize", "new",  "register", 0 }; | 
 |   enum VL_enum { VL_ANNOTATE, VL_FINALIZE, VL_NEW, VL_REGISTER }; | 
 |   int iSub; | 
 |  | 
 |   if( objc<2 ){ | 
 |     Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ..."); | 
 |     return TCL_ERROR; | 
 |   } | 
 |   if( Tcl_GetIndexFromObj(interp, objv[1], strs, "sub-command", 0, &iSub) ){ | 
 |     return TCL_ERROR; | 
 |   } | 
 |  | 
 |   switch( (enum VL_enum)iSub ){ | 
 |     case VL_ANNOTATE: { | 
 |       char *zVfs; | 
 |       char *zMsg; | 
 |       if( objc!=4 ){ | 
 |         Tcl_WrongNumArgs(interp, 3, objv, "VFS"); | 
 |         return TCL_ERROR; | 
 |       } | 
 |       zVfs = Tcl_GetString(objv[2]); | 
 |       zMsg = Tcl_GetString(objv[3]); | 
 |       rc = sqlite3_vfslog_annotate(zVfs, zMsg); | 
 |       if( rc!=SQLITE_OK ){ | 
 |         Tcl_AppendResult(interp, "failed", 0); | 
 |         return TCL_ERROR; | 
 |       } | 
 |       break; | 
 |     } | 
 |     case VL_FINALIZE: { | 
 |       char *zVfs; | 
 |       if( objc!=3 ){ | 
 |         Tcl_WrongNumArgs(interp, 2, objv, "VFS"); | 
 |         return TCL_ERROR; | 
 |       } | 
 |       zVfs = Tcl_GetString(objv[2]); | 
 |       rc = sqlite3_vfslog_finalize(zVfs); | 
 |       if( rc!=SQLITE_OK ){ | 
 |         Tcl_AppendResult(interp, "failed", 0); | 
 |         return TCL_ERROR; | 
 |       } | 
 |       break; | 
 |     }; | 
 |  | 
 |     case VL_NEW: { | 
 |       char *zVfs; | 
 |       char *zParent; | 
 |       char *zLog; | 
 |       if( objc!=5 ){ | 
 |         Tcl_WrongNumArgs(interp, 2, objv, "VFS PARENT LOGFILE"); | 
 |         return TCL_ERROR; | 
 |       } | 
 |       zVfs = Tcl_GetString(objv[2]); | 
 |       zParent = Tcl_GetString(objv[3]); | 
 |       zLog = Tcl_GetString(objv[4]); | 
 |       if( *zParent=='\0' ) zParent = 0; | 
 |       rc = sqlite3_vfslog_new(zVfs, zParent, zLog, 0); | 
 |       if( rc!=SQLITE_OK ){ | 
 |         Tcl_AppendResult(interp, "failed", 0); | 
 |         return TCL_ERROR; | 
 |       } | 
 |       sqlite3_vfs_register(sqlite3_vfs_find(zVfs), 1); | 
 |       break; | 
 |     }; | 
 |  | 
 |     case VL_REGISTER: { | 
 |       char *zDb; | 
 |       if( objc!=3 ){ | 
 |         Tcl_WrongNumArgs(interp, 2, objv, "DB"); | 
 |         return TCL_ERROR; | 
 |       } | 
 | #ifdef SQLITE_OMIT_VIRTUALTABLE | 
 |       Tcl_AppendResult(interp, "vfslog not available because of " | 
 |                                "SQLITE_OMIT_VIRTUALTABLE", (void*)0); | 
 |       return TCL_ERROR; | 
 | #else | 
 |       zDb = Tcl_GetString(objv[2]); | 
 |       if( Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){ | 
 |         db = ((struct SqliteDb*)cmdInfo.objClientData)->db; | 
 |         rc = sqlite3_vfslog_register(db); | 
 |       } | 
 |       if( rc!=SQLITE_OK ){ | 
 |         Tcl_AppendResult(interp, "bad sqlite3 handle: ", zDb, (void*)0); | 
 |         return TCL_ERROR; | 
 |       } | 
 |       break; | 
 | #endif | 
 |     } | 
 |   } | 
 |  | 
 |   return TCL_OK; | 
 | } | 
 |  | 
 | int SqlitetestOsinst_Init(Tcl_Interp *interp){ | 
 |   Tcl_CreateObjCommand(interp, "vfslog", test_vfslog, 0, 0); | 
 |   return TCL_OK; | 
 | } | 
 |  | 
 | #endif /* SQLITE_TEST */ |