| /* | 
 | ** 2013-10-09 | 
 | ** | 
 | ** 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 for | 
 | ** unix that generates per-database log files of all disk activity. | 
 | */ | 
 |  | 
 | /* | 
 | ** This module contains code for a wrapper VFS that causes a log of | 
 | ** most VFS calls to be written into a file on disk. | 
 | ** | 
 | ** Each database connection creates a separate log file in the same | 
 | ** directory as the original database and named after the original | 
 | ** database.  A unique suffix is added to avoid name collisions.   | 
 | ** Separate log files are used so that concurrent processes do not | 
 | ** try to write log operations to the same file at the same instant,  | 
 | ** resulting in overwritten or comingled log text. | 
 | ** | 
 | ** Each individual log file records operations by a single database | 
 | ** connection on both the original database and its associated rollback | 
 | ** journal. | 
 | ** | 
 | ** The log files are in the comma-separated-value (CSV) format.  The | 
 | ** log files can be imported into an SQLite database using the ".import" | 
 | ** command of the SQLite command-line shell for analysis. | 
 | ** | 
 | ** One technique for using this module is to append the text of this | 
 | ** module to the end of a standard "sqlite3.c" amalgamation file then | 
 | ** add the following compile-time options: | 
 | ** | 
 | **     -DSQLITE_EXTRA_INIT=sqlite3_register_vfslog | 
 | **     -DSQLITE_USE_FCNTL_TRACE | 
 | ** | 
 | ** The first compile-time option causes the sqlite3_register_vfslog() | 
 | ** function, defined below, to be invoked when SQLite is initialized. | 
 | ** That causes this custom VFS to become the default VFS for all | 
 | ** subsequent connections.  The SQLITE_USE_FCNTL_TRACE option causes | 
 | ** the SQLite core to issue extra sqlite3_file_control() operations | 
 | ** with SQLITE_FCNTL_TRACE to give some indication of what is going | 
 | ** on in the core. | 
 | */ | 
 |  | 
 | #include "sqlite3.h" | 
 | #include <string.h> | 
 | #include <assert.h> | 
 | #include <stdio.h> | 
 | #if SQLITE_OS_UNIX | 
 | # include <unistd.h> | 
 | #endif | 
 |  | 
 | /* | 
 | ** Forward declaration of objects used by this utility | 
 | */ | 
 | typedef struct VLogLog VLogLog; | 
 | typedef struct VLogVfs VLogVfs; | 
 | typedef struct VLogFile VLogFile; | 
 |  | 
 | /* There is a pair (an array of size 2) of the following objects for | 
 | ** each database file being logged.  The first contains the filename | 
 | ** and is used to log I/O with the main database.  The second has | 
 | ** a NULL filename and is used to log I/O for the journal.  Both | 
 | ** out pointers are the same. | 
 | */ | 
 | struct VLogLog { | 
 |   VLogLog *pNext;                 /* Next in a list of all active logs */ | 
 |   VLogLog **ppPrev;               /* Pointer to this in the list */ | 
 |   int nRef;                       /* Number of references to this object */ | 
 |   int nFilename;                  /* Length of zFilename in bytes */ | 
 |   char *zFilename;                /* Name of database file.  NULL for journal */ | 
 |   FILE *out;                      /* Write information here */ | 
 | }; | 
 |  | 
 | struct VLogVfs { | 
 |   sqlite3_vfs base;               /* VFS methods */ | 
 |   sqlite3_vfs *pVfs;              /* Parent VFS */ | 
 | }; | 
 |  | 
 | struct VLogFile { | 
 |   sqlite3_file base;              /* IO methods */ | 
 |   sqlite3_file *pReal;            /* Underlying file handle */ | 
 |   VLogLog *pLog;                  /* The log file for this file */ | 
 | }; | 
 |  | 
 | #define REALVFS(p) (((VLogVfs*)(p))->pVfs) | 
 |  | 
 | /* | 
 | ** Methods for VLogFile | 
 | */ | 
 | static int vlogClose(sqlite3_file*); | 
 | static int vlogRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); | 
 | static int vlogWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); | 
 | static int vlogTruncate(sqlite3_file*, sqlite3_int64 size); | 
 | static int vlogSync(sqlite3_file*, int flags); | 
 | static int vlogFileSize(sqlite3_file*, sqlite3_int64 *pSize); | 
 | static int vlogLock(sqlite3_file*, int); | 
 | static int vlogUnlock(sqlite3_file*, int); | 
 | static int vlogCheckReservedLock(sqlite3_file*, int *pResOut); | 
 | static int vlogFileControl(sqlite3_file*, int op, void *pArg); | 
 | static int vlogSectorSize(sqlite3_file*); | 
 | static int vlogDeviceCharacteristics(sqlite3_file*); | 
 |  | 
 | /* | 
 | ** Methods for VLogVfs | 
 | */ | 
 | static int vlogOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); | 
 | static int vlogDelete(sqlite3_vfs*, const char *zName, int syncDir); | 
 | static int vlogAccess(sqlite3_vfs*, const char *zName, int flags, int *); | 
 | static int vlogFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); | 
 | static void *vlogDlOpen(sqlite3_vfs*, const char *zFilename); | 
 | static void vlogDlError(sqlite3_vfs*, int nByte, char *zErrMsg); | 
 | static void (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void); | 
 | static void vlogDlClose(sqlite3_vfs*, void*); | 
 | static int vlogRandomness(sqlite3_vfs*, int nByte, char *zOut); | 
 | static int vlogSleep(sqlite3_vfs*, int microseconds); | 
 | static int vlogCurrentTime(sqlite3_vfs*, double*); | 
 | static int vlogGetLastError(sqlite3_vfs*, int, char *); | 
 | static int vlogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); | 
 |  | 
 | static VLogVfs vlog_vfs = { | 
 |   { | 
 |     1,                            /* iVersion */ | 
 |     0,                            /* szOsFile (set by register_vlog()) */ | 
 |     1024,                         /* mxPathname */ | 
 |     0,                            /* pNext */ | 
 |     "vfslog",                     /* zName */ | 
 |     0,                            /* pAppData */ | 
 |     vlogOpen,                     /* xOpen */ | 
 |     vlogDelete,                   /* xDelete */ | 
 |     vlogAccess,                   /* xAccess */ | 
 |     vlogFullPathname,             /* xFullPathname */ | 
 |     vlogDlOpen,                   /* xDlOpen */ | 
 |     vlogDlError,                  /* xDlError */ | 
 |     vlogDlSym,                    /* xDlSym */ | 
 |     vlogDlClose,                  /* xDlClose */ | 
 |     vlogRandomness,               /* xRandomness */ | 
 |     vlogSleep,                    /* xSleep */ | 
 |     vlogCurrentTime,              /* xCurrentTime */ | 
 |     vlogGetLastError,             /* xGetLastError */ | 
 |     vlogCurrentTimeInt64          /* xCurrentTimeInt64 */ | 
 |   }, | 
 |   0 | 
 | }; | 
 |  | 
 | static sqlite3_io_methods vlog_io_methods = { | 
 |   1,                              /* iVersion */ | 
 |   vlogClose,                      /* xClose */ | 
 |   vlogRead,                       /* xRead */ | 
 |   vlogWrite,                      /* xWrite */ | 
 |   vlogTruncate,                   /* xTruncate */ | 
 |   vlogSync,                       /* xSync */ | 
 |   vlogFileSize,                   /* xFileSize */ | 
 |   vlogLock,                       /* xLock */ | 
 |   vlogUnlock,                     /* xUnlock */ | 
 |   vlogCheckReservedLock,          /* xCheckReservedLock */ | 
 |   vlogFileControl,                /* xFileControl */ | 
 |   vlogSectorSize,                 /* xSectorSize */ | 
 |   vlogDeviceCharacteristics,      /* xDeviceCharacteristics */ | 
 |   0,                              /* xShmMap */ | 
 |   0,                              /* xShmLock */ | 
 |   0,                              /* xShmBarrier */ | 
 |   0                               /* xShmUnmap */ | 
 | }; | 
 |  | 
 | #if SQLITE_OS_UNIX && !defined(NO_GETTOD) | 
 | #include <sys/time.h> | 
 | static sqlite3_uint64 vlog_time(){ | 
 |   struct timeval sTime; | 
 |   gettimeofday(&sTime, 0); | 
 |   return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000; | 
 | } | 
 | #elif SQLITE_OS_WIN | 
 | #include <windows.h> | 
 | #include <time.h> | 
 | static sqlite3_uint64 vlog_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 vlog_time(){ | 
 |   return 0; | 
 | } | 
 | #endif | 
 |  | 
 |  | 
 | /* | 
 | ** Write a message to the log file | 
 | */ | 
 | static void vlogLogPrint( | 
 |   VLogLog *pLog,                 /* The log file to write into */ | 
 |   sqlite3_int64 tStart,            /* Start time of system call */ | 
 |   sqlite3_int64 tElapse,           /* Elapse time of system call */ | 
 |   const char *zOp,                 /* Type of system call */ | 
 |   sqlite3_int64 iArg1,             /* First argument */ | 
 |   sqlite3_int64 iArg2,             /* Second argument */ | 
 |   const char *zArg3,               /* Third argument */ | 
 |   int iRes                         /* Result */ | 
 | ){ | 
 |   char z1[40], z2[40], z3[2000]; | 
 |   if( pLog==0 ) return; | 
 |   if( iArg1>=0 ){ | 
 |     sqlite3_snprintf(sizeof(z1), z1, "%lld", iArg1); | 
 |   }else{ | 
 |     z1[0] = 0; | 
 |   } | 
 |   if( iArg2>=0 ){ | 
 |     sqlite3_snprintf(sizeof(z2), z2, "%lld", iArg2); | 
 |   }else{ | 
 |     z2[0] = 0; | 
 |   } | 
 |   if( zArg3 ){ | 
 |     sqlite3_snprintf(sizeof(z3), z3, "\"%.*w\"", sizeof(z3)-4, zArg3); | 
 |   }else{ | 
 |     z3[0] = 0; | 
 |   } | 
 |   fprintf(pLog->out,"%lld,%lld,%s,%d,%s,%s,%s,%d\n", | 
 |       tStart, tElapse, zOp, pLog->zFilename==0, z1, z2, z3, iRes); | 
 | } | 
 |  | 
 | /* | 
 | ** List of all active log connections.  Protected by the master mutex. | 
 | */ | 
 | static VLogLog *allLogs = 0; | 
 |  | 
 | /* | 
 | ** Close a VLogLog object | 
 | */ | 
 | static void vlogLogClose(VLogLog *p){ | 
 |   if( p ){ | 
 |     sqlite3_mutex *pMutex; | 
 |     p->nRef--; | 
 |     if( p->nRef>0 || p->zFilename==0 ) return; | 
 |     pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER); | 
 |     sqlite3_mutex_enter(pMutex); | 
 |     *p->ppPrev = p->pNext; | 
 |     if( p->pNext ) p->pNext->ppPrev = p->ppPrev; | 
 |     sqlite3_mutex_leave(pMutex); | 
 |     fclose(p->out); | 
 |     sqlite3_free(p); | 
 |   } | 
 | } | 
 |  | 
 | /* | 
 | ** Open a VLogLog object on the given file | 
 | */ | 
 | static VLogLog *vlogLogOpen(const char *zFilename){ | 
 |   int nName = (int)strlen(zFilename); | 
 |   int isJournal = 0; | 
 |   sqlite3_mutex *pMutex; | 
 |   VLogLog *pLog, *pTemp; | 
 |   sqlite3_int64 tNow = 0; | 
 |   if( nName>4 && strcmp(zFilename+nName-4,"-wal")==0 ){ | 
 |     return 0;  /* Do not log wal files */ | 
 |   }else | 
 |   if( nName>8 && strcmp(zFilename+nName-8,"-journal")==0 ){ | 
 |     nName -= 8; | 
 |     isJournal = 1; | 
 |   }else if( nName>12  | 
 |          && sqlite3_strglob("-mj??????9??", zFilename+nName-12)==0 ){ | 
 |     return 0;  /* Do not log master journal files */ | 
 |   } | 
 |   pTemp = sqlite3_malloc( sizeof(*pLog)*2 + nName + 60 ); | 
 |   if( pTemp==0 ) return 0; | 
 |   pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER); | 
 |   sqlite3_mutex_enter(pMutex); | 
 |   for(pLog=allLogs; pLog; pLog=pLog->pNext){ | 
 |     if( pLog->nFilename==nName && !memcmp(pLog->zFilename, zFilename, nName) ){ | 
 |       break; | 
 |     } | 
 |   } | 
 |   if( pLog==0 ){ | 
 |     pLog = pTemp; | 
 |     pTemp = 0; | 
 |     memset(pLog, 0, sizeof(*pLog)*2); | 
 |     pLog->zFilename = (char*)&pLog[2]; | 
 |     tNow = vlog_time(); | 
 |     sqlite3_snprintf(nName+60, pLog->zFilename, "%.*s-debuglog-%lld", | 
 |                      nName, zFilename, tNow); | 
 |     pLog->out = fopen(pLog->zFilename, "a"); | 
 |     if( pLog->out==0 ){ | 
 |       sqlite3_mutex_leave(pMutex); | 
 |       sqlite3_free(pLog); | 
 |       return 0; | 
 |     } | 
 |     pLog->nFilename = nName; | 
 |     pLog[1].out = pLog[0].out; | 
 |     pLog->ppPrev = &allLogs; | 
 |     if( allLogs ) allLogs->ppPrev = &pLog->pNext; | 
 |     pLog->pNext = allLogs; | 
 |     allLogs = pLog; | 
 |   } | 
 |   sqlite3_mutex_leave(pMutex); | 
 |   if( pTemp ){ | 
 |     sqlite3_free(pTemp); | 
 |   }else{ | 
 | #if SQLITE_OS_UNIX | 
 |     char zHost[200]; | 
 |     zHost[0] = 0; | 
 |     gethostname(zHost, sizeof(zHost)-1); | 
 |     zHost[sizeof(zHost)-1] = 0; | 
 |     vlogLogPrint(pLog, tNow, 0, "IDENT", getpid(), -1, zHost, 0); | 
 | #endif | 
 |   } | 
 |   if( pLog && isJournal ) pLog++; | 
 |   pLog->nRef++; | 
 |   return pLog; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 | ** Close an vlog-file. | 
 | */ | 
 | static int vlogClose(sqlite3_file *pFile){ | 
 |   sqlite3_uint64 tStart, tElapse; | 
 |   int rc = SQLITE_OK; | 
 |   VLogFile *p = (VLogFile *)pFile; | 
 |  | 
 |   tStart = vlog_time(); | 
 |   if( p->pReal->pMethods ){ | 
 |     rc = p->pReal->pMethods->xClose(p->pReal); | 
 |   } | 
 |   tElapse = vlog_time() - tStart; | 
 |   vlogLogPrint(p->pLog, tStart, tElapse, "CLOSE", -1, -1, 0, rc); | 
 |   vlogLogClose(p->pLog); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Compute signature for a block of content. | 
 | ** | 
 | ** For blocks of 16 or fewer bytes, the signature is just a hex dump of | 
 | ** the entire block. | 
 | ** | 
 | ** For blocks of more than 16 bytes, the signature is a hex dump of the | 
 | ** first 8 bytes followed by a 64-bit has of the entire block. | 
 | */ | 
 | static void vlogSignature(unsigned char *p, int n, char *zCksum){ | 
 |   unsigned int s0 = 0, s1 = 0; | 
 |   unsigned int *pI; | 
 |   int i; | 
 |   if( n<=16 ){ | 
 |     for(i=0; i<n; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]); | 
 |   }else{  | 
 |     pI = (unsigned int*)p; | 
 |     for(i=0; i<n-7; i+=8){ | 
 |       s0 += pI[0] + s1; | 
 |       s1 += pI[1] + s0; | 
 |       pI += 2; | 
 |     } | 
 |     for(i=0; i<8; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]); | 
 |     sqlite3_snprintf(18, zCksum+i*2, "-%08x%08x", s0, s1); | 
 |   } | 
 | } | 
 |  | 
 | /* | 
 | ** Convert a big-endian 32-bit integer into a native integer | 
 | */ | 
 | static int bigToNative(const unsigned char *x){ | 
 |   return (x[0]<<24) + (x[1]<<16) + (x[2]<<8) + x[3]; | 
 | } | 
 |  | 
 | /* | 
 | ** Read data from an vlog-file. | 
 | */ | 
 | static int vlogRead( | 
 |   sqlite3_file *pFile,  | 
 |   void *zBuf,  | 
 |   int iAmt,  | 
 |   sqlite_int64 iOfst | 
 | ){ | 
 |   int rc; | 
 |   sqlite3_uint64 tStart, tElapse; | 
 |   VLogFile *p = (VLogFile *)pFile; | 
 |   char zSig[40]; | 
 |  | 
 |   tStart = vlog_time(); | 
 |   rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst); | 
 |   tElapse = vlog_time() - tStart; | 
 |   if( rc==SQLITE_OK ){ | 
 |     vlogSignature(zBuf, iAmt, zSig); | 
 |   }else{ | 
 |     zSig[0] = 0; | 
 |   } | 
 |   vlogLogPrint(p->pLog, tStart, tElapse, "READ", iAmt, iOfst, zSig, rc); | 
 |   if( rc==SQLITE_OK | 
 |    && p->pLog | 
 |    && p->pLog->zFilename | 
 |    && iOfst<=24 | 
 |    && iOfst+iAmt>=28 | 
 |   ){ | 
 |     unsigned char *x = ((unsigned char*)zBuf)+(24-iOfst); | 
 |     unsigned iCtr, nFree = -1; | 
 |     char *zFree = 0; | 
 |     char zStr[12]; | 
 |     iCtr = bigToNative(x); | 
 |     if( iOfst+iAmt>=40 ){ | 
 |       zFree = zStr; | 
 |       sqlite3_snprintf(sizeof(zStr), zStr, "%d", bigToNative(x+8)); | 
 |       nFree = bigToNative(x+12); | 
 |     } | 
 |     vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-READ", iCtr, nFree, zFree, 0); | 
 |   } | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Write data to an vlog-file. | 
 | */ | 
 | static int vlogWrite( | 
 |   sqlite3_file *pFile, | 
 |   const void *z, | 
 |   int iAmt, | 
 |   sqlite_int64 iOfst | 
 | ){ | 
 |   int rc; | 
 |   sqlite3_uint64 tStart, tElapse; | 
 |   VLogFile *p = (VLogFile *)pFile; | 
 |   char zSig[40]; | 
 |  | 
 |   tStart = vlog_time(); | 
 |   vlogSignature((unsigned char*)z, iAmt, zSig); | 
 |   rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst); | 
 |   tElapse = vlog_time() - tStart; | 
 |   vlogLogPrint(p->pLog, tStart, tElapse, "WRITE", iAmt, iOfst, zSig, rc); | 
 |   if( rc==SQLITE_OK | 
 |    && p->pLog | 
 |    && p->pLog->zFilename | 
 |    && iOfst<=24 | 
 |    && iOfst+iAmt>=28 | 
 |   ){ | 
 |     unsigned char *x = ((unsigned char*)z)+(24-iOfst); | 
 |     unsigned iCtr, nFree = -1; | 
 |     char *zFree = 0; | 
 |     char zStr[12]; | 
 |     iCtr = bigToNative(x); | 
 |     if( iOfst+iAmt>=40 ){ | 
 |       zFree = zStr; | 
 |       sqlite3_snprintf(sizeof(zStr), zStr, "%d", bigToNative(x+8)); | 
 |       nFree = bigToNative(x+12); | 
 |     } | 
 |     vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-WRITE", iCtr, nFree, zFree, 0); | 
 |   } | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Truncate an vlog-file. | 
 | */ | 
 | static int vlogTruncate(sqlite3_file *pFile, sqlite_int64 size){ | 
 |   int rc; | 
 |   sqlite3_uint64 tStart, tElapse; | 
 |   VLogFile *p = (VLogFile *)pFile; | 
 |   tStart = vlog_time(); | 
 |   rc = p->pReal->pMethods->xTruncate(p->pReal, size); | 
 |   tElapse = vlog_time() - tStart; | 
 |   vlogLogPrint(p->pLog, tStart, tElapse, "TRUNCATE", size, -1, 0, rc); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Sync an vlog-file. | 
 | */ | 
 | static int vlogSync(sqlite3_file *pFile, int flags){ | 
 |   int rc; | 
 |   sqlite3_uint64 tStart, tElapse; | 
 |   VLogFile *p = (VLogFile *)pFile; | 
 |   tStart = vlog_time(); | 
 |   rc = p->pReal->pMethods->xSync(p->pReal, flags); | 
 |   tElapse = vlog_time() - tStart; | 
 |   vlogLogPrint(p->pLog, tStart, tElapse, "SYNC", flags, -1, 0, rc); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Return the current file-size of an vlog-file. | 
 | */ | 
 | static int vlogFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ | 
 |   int rc; | 
 |   sqlite3_uint64 tStart, tElapse; | 
 |   VLogFile *p = (VLogFile *)pFile; | 
 |   tStart = vlog_time(); | 
 |   rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); | 
 |   tElapse = vlog_time() - tStart; | 
 |   vlogLogPrint(p->pLog, tStart, tElapse, "FILESIZE", *pSize, -1, 0, rc); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Lock an vlog-file. | 
 | */ | 
 | static int vlogLock(sqlite3_file *pFile, int eLock){ | 
 |   int rc; | 
 |   sqlite3_uint64 tStart, tElapse; | 
 |   VLogFile *p = (VLogFile *)pFile; | 
 |   tStart = vlog_time(); | 
 |   rc = p->pReal->pMethods->xLock(p->pReal, eLock); | 
 |   tElapse = vlog_time() - tStart; | 
 |   vlogLogPrint(p->pLog, tStart, tElapse, "LOCK", eLock, -1, 0, rc); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Unlock an vlog-file. | 
 | */ | 
 | static int vlogUnlock(sqlite3_file *pFile, int eLock){ | 
 |   int rc; | 
 |   sqlite3_uint64 tStart; | 
 |   VLogFile *p = (VLogFile *)pFile; | 
 |   tStart = vlog_time(); | 
 |   vlogLogPrint(p->pLog, tStart, 0, "UNLOCK", eLock, -1, 0, 0); | 
 |   rc = p->pReal->pMethods->xUnlock(p->pReal, eLock); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Check if another file-handle holds a RESERVED lock on an vlog-file. | 
 | */ | 
 | static int vlogCheckReservedLock(sqlite3_file *pFile, int *pResOut){ | 
 |   int rc; | 
 |   sqlite3_uint64 tStart, tElapse; | 
 |   VLogFile *p = (VLogFile *)pFile; | 
 |   tStart = vlog_time(); | 
 |   rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut); | 
 |   tElapse = vlog_time() - tStart; | 
 |   vlogLogPrint(p->pLog, tStart, tElapse, "CHECKRESERVEDLOCK", | 
 |                  *pResOut, -1, "", rc); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** File control method. For custom operations on an vlog-file. | 
 | */ | 
 | static int vlogFileControl(sqlite3_file *pFile, int op, void *pArg){ | 
 |   VLogFile *p = (VLogFile *)pFile; | 
 |   sqlite3_uint64 tStart, tElapse; | 
 |   int rc; | 
 |   tStart = vlog_time(); | 
 |   rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg); | 
 |   if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){ | 
 |     *(char**)pArg = sqlite3_mprintf("vlog/%z", *(char**)pArg); | 
 |   } | 
 |   tElapse = vlog_time() - tStart; | 
 |   if( op==SQLITE_FCNTL_TRACE ){ | 
 |     vlogLogPrint(p->pLog, tStart, tElapse, "TRACE", op, -1, pArg, rc); | 
 |   }else if( op==SQLITE_FCNTL_PRAGMA ){ | 
 |     const char **azArg = (const char **)pArg; | 
 |     vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, azArg[1], rc); | 
 |   }else if( op==SQLITE_FCNTL_SIZE_HINT ){ | 
 |     sqlite3_int64 sz = *(sqlite3_int64*)pArg; | 
 |     vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, sz, 0, rc); | 
 |   }else{ | 
 |     vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, 0, rc); | 
 |   } | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Return the sector-size in bytes for an vlog-file. | 
 | */ | 
 | static int vlogSectorSize(sqlite3_file *pFile){ | 
 |   int rc; | 
 |   sqlite3_uint64 tStart, tElapse; | 
 |   VLogFile *p = (VLogFile *)pFile; | 
 |   tStart = vlog_time(); | 
 |   rc = p->pReal->pMethods->xSectorSize(p->pReal); | 
 |   tElapse = vlog_time() - tStart; | 
 |   vlogLogPrint(p->pLog, tStart, tElapse, "SECTORSIZE", -1, -1, 0, rc); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Return the device characteristic flags supported by an vlog-file. | 
 | */ | 
 | static int vlogDeviceCharacteristics(sqlite3_file *pFile){ | 
 |   int rc; | 
 |   sqlite3_uint64 tStart, tElapse; | 
 |   VLogFile *p = (VLogFile *)pFile; | 
 |   tStart = vlog_time(); | 
 |   rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal); | 
 |   tElapse = vlog_time() - tStart; | 
 |   vlogLogPrint(p->pLog, tStart, tElapse, "DEVCHAR", -1, -1, 0, rc); | 
 |   return rc; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 | ** Open an vlog file handle. | 
 | */ | 
 | static int vlogOpen( | 
 |   sqlite3_vfs *pVfs, | 
 |   const char *zName, | 
 |   sqlite3_file *pFile, | 
 |   int flags, | 
 |   int *pOutFlags | 
 | ){ | 
 |   int rc; | 
 |   sqlite3_uint64 tStart, tElapse; | 
 |   sqlite3_int64 iArg2; | 
 |   VLogFile *p = (VLogFile*)pFile; | 
 |  | 
 |   p->pReal = (sqlite3_file*)&p[1]; | 
 |   if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_MAIN_JOURNAL))!=0 ){ | 
 |     p->pLog = vlogLogOpen(zName); | 
 |   }else{ | 
 |     p->pLog = 0; | 
 |   } | 
 |   tStart = vlog_time(); | 
 |   rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags); | 
 |   tElapse = vlog_time() - tStart; | 
 |   iArg2 = pOutFlags ? *pOutFlags : -1; | 
 |   vlogLogPrint(p->pLog, tStart, tElapse, "OPEN", flags, iArg2, 0, rc); | 
 |   if( rc==SQLITE_OK ){ | 
 |     pFile->pMethods = &vlog_io_methods; | 
 |   }else{ | 
 |     if( p->pLog ) vlogLogClose(p->pLog); | 
 |     p->pLog = 0; | 
 |   } | 
 |   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 vlogDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ | 
 |   int rc; | 
 |   sqlite3_uint64 tStart, tElapse; | 
 |   VLogLog *pLog; | 
 |   tStart = vlog_time(); | 
 |   rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync); | 
 |   tElapse = vlog_time() - tStart; | 
 |   pLog = vlogLogOpen(zPath); | 
 |   vlogLogPrint(pLog, tStart, tElapse, "DELETE", dirSync, -1, 0, rc); | 
 |   vlogLogClose(pLog); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** Test for access permissions. Return true if the requested permission | 
 | ** is available, or false otherwise. | 
 | */ | 
 | static int vlogAccess( | 
 |   sqlite3_vfs *pVfs,  | 
 |   const char *zPath,  | 
 |   int flags,  | 
 |   int *pResOut | 
 | ){ | 
 |   int rc; | 
 |   sqlite3_uint64 tStart, tElapse; | 
 |   VLogLog *pLog; | 
 |   tStart = vlog_time(); | 
 |   rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut); | 
 |   tElapse = vlog_time() - tStart; | 
 |   pLog = vlogLogOpen(zPath); | 
 |   vlogLogPrint(pLog, tStart, tElapse, "ACCESS", flags, *pResOut, 0, rc); | 
 |   vlogLogClose(pLog); | 
 |   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 vlogFullPathname( | 
 |   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 *vlogDlOpen(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 vlogDlError(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 (*vlogDlSym(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 vlogDlClose(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 vlogRandomness(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 vlogSleep(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 vlogCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ | 
 |   return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut); | 
 | } | 
 |  | 
 | static int vlogGetLastError(sqlite3_vfs *pVfs, int a, char *b){ | 
 |   return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b); | 
 | } | 
 | static int vlogCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){ | 
 |   return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p); | 
 | } | 
 |  | 
 | /* | 
 | ** Register debugvfs as the default VFS for this process. | 
 | */ | 
 | int sqlite3_register_vfslog(const char *zArg){ | 
 |   vlog_vfs.pVfs = sqlite3_vfs_find(0); | 
 |   vlog_vfs.base.szOsFile = sizeof(VLogFile) + vlog_vfs.pVfs->szOsFile; | 
 |   return sqlite3_vfs_register(&vlog_vfs.base, 1); | 
 | } |