/* ** 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 ** 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 #include /* ** 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 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 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_int64, int, int, int); static void vfslog_string(sqlite3_vfs *, const char *); /* ** Close an vfslog-file. */ static int vfslogClose(sqlite3_file *pFile){ sqlite3_uint64 t; int rc = SQLITE_OK; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); if( p->pReal->pMethods ){ rc = p->pReal->pMethods->xClose(p->pReal); } t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_CLOSE, p->iFileId, t, 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; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_READ, p->iFileId, t, rc, iAmt, (int)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; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_WRITE, p->iFileId, t, rc, iAmt, (int)iOfst); return rc; } /* ** Truncate an vfslog-file. */ static int vfslogTruncate(sqlite3_file *pFile, sqlite_int64 size){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xTruncate(p->pReal, size); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_TRUNCATE, p->iFileId, t, rc, 0, (int)size); return rc; } /* ** Sync an vfslog-file. */ static int vfslogSync(sqlite3_file *pFile, int flags){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xSync(p->pReal, flags); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_SYNC, p->iFileId, t, 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; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_FILESIZE, p->iFileId, t, rc, 0, (int)*pSize); return rc; } /* ** Lock an vfslog-file. */ static int vfslogLock(sqlite3_file *pFile, int eLock){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xLock(p->pReal, eLock); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_LOCK, p->iFileId, t, rc, eLock, 0); return rc; } /* ** Unlock an vfslog-file. */ static int vfslogUnlock(sqlite3_file *pFile, int eLock){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xUnlock(p->pReal, eLock); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_UNLOCK, p->iFileId, t, 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; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_CHECKRESERVEDLOCK, p->iFileId, t, 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; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xSectorSize(p->pReal); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_SECTORSIZE, p->iFileId, t, 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; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_DEVCHAR, p->iFileId, t, rc, 0, 0); return rc; } static int vfslogShmLock(sqlite3_file *pFile, int ofst, int n, int flags){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_SHMLOCK, p->iFileId, t, 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; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_SHMMAP, p->iFileId, t, rc, 0, 0); return rc; } static void vfslogShmBarrier(sqlite3_file *pFile){ sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); p->pReal->pMethods->xShmBarrier(p->pReal); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_SHMBARRIER, p->iFileId, t, SQLITE_OK, 0, 0); } static int vfslogShmUnmap(sqlite3_file *pFile, int deleteFlag){ int rc; sqlite3_uint64 t; VfslogFile *p = (VfslogFile *)pFile; t = vfslog_time(); rc = p->pReal->pMethods->xShmUnmap(p->pReal, deleteFlag); t = vfslog_time() - t; vfslog_call(p->pVfslog, OS_SHMUNMAP, p->iFileId, t, 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; 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); t = vfslog_time() - t; vfslog_call(pVfs, OS_OPEN, p->iFileId, t, 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; t = vfslog_time(); rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync); t = vfslog_time() - t; vfslog_call(pVfs, OS_DELETE, 0, t, 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; t = vfslog_time(); rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut); t = vfslog_time() - t; vfslog_call(pVfs, OS_ACCESS, 0, t, rc, flags, *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 put32bits(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 vfslog_call( sqlite3_vfs *pVfs, int eEvent, int iFileid, sqlite3_int64 nClick, int return_code, int size, int offset ){ VfslogVfs *p = (VfslogVfs *)pVfs; unsigned char *zRec; if( (24+p->nBuf)>sizeof(p->aBuf) ){ vfslog_flush(p); } zRec = (unsigned char *)&p->aBuf[p->nBuf]; put32bits(&zRec[0], eEvent); put32bits(&zRec[4], iFileid); put32bits(&zRec[8], (unsigned int)(nClick&0xffff)); put32bits(&zRec[12], return_code); put32bits(&zRec[16], size); put32bits(&zRec[20], offset); p->nBuf += 24; } 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]; put32bits(&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 */ ){ 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, 1); } if( rc ){ vfslog_finalize(p); } return rc; } int sqlite3_vfslog_annotate(const char *zVfs, const char *zMsg){ sqlite3_vfs *pVfs; pVfs = sqlite3_vfs_find(zVfs); if( !pVfs || pVfs->xOpen!=vfslogOpen ){ return SQLITE_ERROR; } vfslog_call(pVfs, OS_ANNOTATE, 0, 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, 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; inFile; 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 = 24; 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); 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 ){ unsigned int val; VfslogCsr *pCsr = (VfslogCsr *)pCursor; assert( i<7 ); val = get32bits(&pCsr->aBuf[4*i]); switch( i ){ case 0: { sqlite3_result_text(ctx, vfslog_eventname(val), -1, SQLITE_STATIC); break; } case 1: { char *zStr = pCsr->zTransient; if( val!=0 && val<(unsigned)pCsr->nFile ){ zStr = pCsr->azFile[val]; } sqlite3_result_text(ctx, zStr, -1, SQLITE_TRANSIENT); break; } default: 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); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, "failed", 0); return TCL_ERROR; } 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 */