/* ** 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. */ #ifdef SQLITE_ENABLE_INSTVFS /* ** C interface: ** ** sqlite3_instvfs_create() ** sqlite3_instvfs_destroy() ** sqlite3_instvfs_configure() ** ** sqlite3_instvfs_reset() ** sqlite3_instvfs_get() ** ** sqlite3_instvfs_binarylog ** sqlite3_instvfs_binarylog_marker ** ** Tcl interface (omitted if SQLITE_TEST is not set): ** ** sqlite3_instvfs create NAME ?PARENT? ** ** Create and register new vfs called $NAME, which is a wrapper around ** the existing vfs $PARENT. If the PARENT argument is omitted, the ** new vfs is a wrapper around the current default vfs. ** ** sqlite3_instvfs destroy NAME ** ** Deregister and destroy the vfs named $NAME, which must have been ** created by an earlier invocation of [sqlite3_instvfs create]. ** ** sqlite3_instvfs configure NAME SCRIPT ** ** Configure the callback script for the vfs $NAME, which much have ** been created by an earlier invocation of [sqlite3_instvfs create]. ** After a callback script has been configured, it is invoked each ** time a vfs or file method is called by SQLite. Before invoking ** the callback script, five arguments are appended to it: ** ** * The name of the invoked method - i.e. "xRead". ** ** * The time consumed by the method call as measured by ** sqlite3Hwtime() (an integer value) ** ** * A string value with a different meaning for different calls. ** For file methods, the name of the file being operated on. For ** other methods it is the filename argument, if any. ** ** * A 32-bit integer value with a call-specific meaning. ** ** * A 64-bit integer value. For xRead() and xWrite() calls this ** is the file offset being written to or read from. Unused by ** all other calls. ** ** sqlite3_instvfs reset NAME ** ** Zero the internal event counters associated with vfs $NAME, ** which must have been created by an earlier invocation of ** [sqlite3_instvfs create]. ** ** sqlite3_instvfs report NAME ** ** Return the values of the internal event counters associated ** with vfs $NAME. The report format is a list with one element ** for each method call (xWrite, xRead etc.). Each element is ** itself a list with three elements: ** ** * The name of the method call - i.e. "xWrite", ** * The total number of calls to the method (an integer). ** * The aggregate time consumed by all calls to the method as ** measured by sqlite3Hwtime() (an integer). */ #include "sqlite3.h" #include #include /* ** Maximum pathname length supported by the inst backend. */ #define INST_MAX_PATHNAME 512 /* File methods */ /* Vfs methods */ #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_NUMEVENTS 21 #define BINARYLOG_STRING 30 #define BINARYLOG_MARKER 31 #define BINARYLOG_PREPARE_V2 64 #define BINARYLOG_STEP 65 #define BINARYLOG_FINALIZE 66 struct InstVfs { sqlite3_vfs base; sqlite3_vfs *pVfs; void *pClient; void (*xDel)(void *); void (*xCall)(void *, int, int, sqlite3_int64, int, const char *, int, int, sqlite3_int64); /* Counters */ sqlite3_int64 aTime[OS_NUMEVENTS]; int aCount[OS_NUMEVENTS]; int iNextFileId; }; typedef struct InstVfs InstVfs; #define REALVFS(p) (((InstVfs *)(p))->pVfs) typedef struct inst_file inst_file; struct inst_file { sqlite3_file base; sqlite3_file *pReal; InstVfs *pInstVfs; const char *zName; int iFileId; /* File id number */ int flags; }; /* ** Method declarations for inst_file. */ static int instClose(sqlite3_file*); static int instRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); static int instWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); static int instTruncate(sqlite3_file*, sqlite3_int64 size); static int instSync(sqlite3_file*, int flags); static int instFileSize(sqlite3_file*, sqlite3_int64 *pSize); static int instLock(sqlite3_file*, int); static int instUnlock(sqlite3_file*, int); static int instCheckReservedLock(sqlite3_file*, int *pResOut); static int instFileControl(sqlite3_file*, int op, void *pArg); static int instSectorSize(sqlite3_file*); static int instDeviceCharacteristics(sqlite3_file*); /* ** Method declarations for inst_vfs. */ static int instOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); static int instDelete(sqlite3_vfs*, const char *zName, int syncDir); static int instAccess(sqlite3_vfs*, const char *zName, int flags, int *); static int instFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); static void *instDlOpen(sqlite3_vfs*, const char *zFilename); static void instDlError(sqlite3_vfs*, int nByte, char *zErrMsg); static void (*instDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void); static void instDlClose(sqlite3_vfs*, void*); static int instRandomness(sqlite3_vfs*, int nByte, char *zOut); static int instSleep(sqlite3_vfs*, int microseconds); static int instCurrentTime(sqlite3_vfs*, double*); static void binarylog_blob(sqlite3_vfs *, const char *, int, int); static sqlite3_vfs inst_vfs = { 1, /* iVersion */ sizeof(inst_file), /* szOsFile */ INST_MAX_PATHNAME, /* mxPathname */ 0, /* pNext */ 0, /* zName */ 0, /* pAppData */ instOpen, /* xOpen */ instDelete, /* xDelete */ instAccess, /* xAccess */ instFullPathname, /* xFullPathname */ instDlOpen, /* xDlOpen */ instDlError, /* xDlError */ instDlSym, /* xDlSym */ instDlClose, /* xDlClose */ instRandomness, /* xRandomness */ instSleep, /* xSleep */ instCurrentTime /* xCurrentTime */ }; static sqlite3_io_methods inst_io_methods = { 1, /* iVersion */ instClose, /* xClose */ instRead, /* xRead */ instWrite, /* xWrite */ instTruncate, /* xTruncate */ instSync, /* xSync */ instFileSize, /* xFileSize */ instLock, /* xLock */ instUnlock, /* xUnlock */ instCheckReservedLock, /* xCheckReservedLock */ instFileControl, /* xFileControl */ instSectorSize, /* xSectorSize */ instDeviceCharacteristics /* xDeviceCharacteristics */ }; /* ** hwtime.h contains inline assembler code for implementing ** high-performance timing routines. */ #include "hwtime.h" #define OS_TIME_IO(eEvent, A, B, Call) { \ inst_file *p = (inst_file *)pFile; \ InstVfs *pInstVfs = p->pInstVfs; \ int rc; \ sqlite_uint64 t = sqlite3Hwtime(); \ rc = Call; \ t = sqlite3Hwtime() - t; \ pInstVfs->aTime[eEvent] += t; \ pInstVfs->aCount[eEvent] += 1; \ if( pInstVfs->xCall ){ \ pInstVfs->xCall( \ pInstVfs->pClient,eEvent,p->iFileId,t,rc,p->zName,p->flags,A,B \ ); \ } \ return rc; \ } #define OS_TIME_VFS(eEvent, Z, flags, A, B, Call) { \ InstVfs *pInstVfs = (InstVfs *)pVfs; \ int rc; \ sqlite_uint64 t = sqlite3Hwtime(); \ rc = Call; \ t = sqlite3Hwtime() - t; \ pInstVfs->aTime[eEvent] += t; \ pInstVfs->aCount[eEvent] += 1; \ if( pInstVfs->xCall ){ \ pInstVfs->xCall(pInstVfs->pClient,eEvent,0, t, rc, Z, flags, A, B); \ } \ return rc; \ } /* ** Close an inst-file. */ static int instClose(sqlite3_file *pFile){ OS_TIME_IO(OS_CLOSE, 0, 0, (p->pReal->pMethods ? p->pReal->pMethods->xClose(p->pReal) : SQLITE_OK) ); } /* ** Read data from an inst-file. */ static int instRead( sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst ){ sqlite3_vfs *pVfs = (sqlite3_vfs *)(((inst_file *)pFile)->pInstVfs); OS_TIME_IO(OS_READ, iAmt, (binarylog_blob(pVfs, zBuf, iAmt, 1), iOfst), p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst) ); } /* ** Write data to an inst-file. */ static int instWrite( sqlite3_file *pFile, const void *z, int iAmt, sqlite_int64 iOfst ){ sqlite3_vfs *pVfs = (sqlite3_vfs *)(((inst_file *)pFile)->pInstVfs); binarylog_blob(pVfs, z, iAmt, 1); OS_TIME_IO(OS_WRITE, iAmt, iOfst, p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst) ); } /* ** Truncate an inst-file. */ static int instTruncate(sqlite3_file *pFile, sqlite_int64 size){ OS_TIME_IO(OS_TRUNCATE, 0, (int)size, p->pReal->pMethods->xTruncate(p->pReal, size) ); } /* ** Sync an inst-file. */ static int instSync(sqlite3_file *pFile, int flags){ OS_TIME_IO(OS_SYNC, flags, 0, p->pReal->pMethods->xSync(p->pReal, flags)); } /* ** Return the current file-size of an inst-file. */ static int instFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ OS_TIME_IO(OS_FILESIZE, (int)(*pSize), 0, p->pReal->pMethods->xFileSize(p->pReal, pSize) ); } /* ** Lock an inst-file. */ static int instLock(sqlite3_file *pFile, int eLock){ OS_TIME_IO(OS_LOCK, eLock, 0, p->pReal->pMethods->xLock(p->pReal, eLock)); } /* ** Unlock an inst-file. */ static int instUnlock(sqlite3_file *pFile, int eLock){ OS_TIME_IO(OS_UNLOCK, eLock, 0, p->pReal->pMethods->xUnlock(p->pReal, eLock)); } /* ** Check if another file-handle holds a RESERVED lock on an inst-file. */ static int instCheckReservedLock(sqlite3_file *pFile, int *pResOut){ OS_TIME_IO(OS_CHECKRESERVEDLOCK, 0, 0, p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut) ); } /* ** File control method. For custom operations on an inst-file. */ static int instFileControl(sqlite3_file *pFile, int op, void *pArg){ OS_TIME_IO(OS_FILECONTROL, 0, 0, p->pReal->pMethods->xFileControl(p->pReal, op, pArg)); } /* ** Return the sector-size in bytes for an inst-file. */ static int instSectorSize(sqlite3_file *pFile){ OS_TIME_IO(OS_SECTORSIZE, 0, 0, p->pReal->pMethods->xSectorSize(p->pReal)); } /* ** Return the device characteristic flags supported by an inst-file. */ static int instDeviceCharacteristics(sqlite3_file *pFile){ OS_TIME_IO(OS_DEVCHAR, 0, 0, p->pReal->pMethods->xDeviceCharacteristics(p->pReal)); } /* ** Open an inst file handle. */ static int instOpen( sqlite3_vfs *pVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags ){ inst_file *p = (inst_file *)pFile; pFile->pMethods = &inst_io_methods; p->pReal = (sqlite3_file *)&p[1]; p->pInstVfs = (InstVfs *)pVfs; p->zName = zName; p->flags = flags; p->iFileId = ++p->pInstVfs->iNextFileId; binarylog_blob(pVfs, zName, -1, 0); OS_TIME_VFS(OS_OPEN, zName, flags, p->iFileId, 0, REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags) ); } /* ** 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 instDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ binarylog_blob(pVfs, zPath, -1, 0); OS_TIME_VFS(OS_DELETE, zPath, 0, dirSync, 0, REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync) ); } /* ** Test for access permissions. Return true if the requested permission ** is available, or false otherwise. */ static int instAccess( sqlite3_vfs *pVfs, const char *zPath, int flags, int *pResOut ){ binarylog_blob(pVfs, zPath, -1, 0); OS_TIME_VFS(OS_ACCESS, zPath, 0, flags, *pResOut, REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut) ); } /* ** 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 instFullPathname( sqlite3_vfs *pVfs, const char *zPath, int nOut, char *zOut ){ OS_TIME_VFS( OS_FULLPATHNAME, zPath, 0, 0, 0, REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut); ); } /* ** Open the dynamic library located at zPath and return a handle. */ static void *instDlOpen(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 instDlError(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 (*instDlSym(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 instDlClose(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 instRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ OS_TIME_VFS( OS_RANDOMNESS, 0, 0, nByte, 0, REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut); ); } /* ** Sleep for nMicro microseconds. Return the number of microseconds ** actually slept. */ static int instSleep(sqlite3_vfs *pVfs, int nMicro){ OS_TIME_VFS( OS_SLEEP, 0, 0, nMicro, 0, REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro) ); } /* ** Return the current time as a Julian Day number in *pTimeOut. */ static int instCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ OS_TIME_VFS( OS_CURRENTTIME, 0, 0, 0, 0, REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut) ); } sqlite3_vfs *sqlite3_instvfs_create(const char *zName, const char *zParent){ int nByte; InstVfs *p; sqlite3_vfs *pParent; pParent = sqlite3_vfs_find(zParent); if( !pParent ){ return 0; } nByte = strlen(zName) + 1 + sizeof(InstVfs); p = (InstVfs *)sqlite3_malloc(nByte); if( p ){ char *zCopy = (char *)&p[1]; memset(p, 0, nByte); memcpy(p, &inst_vfs, sizeof(sqlite3_vfs)); p->pVfs = pParent; memcpy(zCopy, zName, strlen(zName)); p->base.zName = (const char *)zCopy; p->base.szOsFile += pParent->szOsFile; sqlite3_vfs_register((sqlite3_vfs *)p, 0); } return (sqlite3_vfs *)p; } void sqlite3_instvfs_configure( sqlite3_vfs *pVfs, void (*xCall)( void*, int, /* File id */ int, /* Event code */ sqlite3_int64, int, /* Return code */ const char*, /* File name */ int, int, sqlite3_int64 ), void *pClient, void (*xDel)(void *) ){ InstVfs *p = (InstVfs *)pVfs; assert( pVfs->xOpen==instOpen ); if( p->xDel ){ p->xDel(p->pClient); } p->xCall = xCall; p->xDel = xDel; p->pClient = pClient; } void sqlite3_instvfs_destroy(sqlite3_vfs *pVfs){ if( pVfs ){ sqlite3_vfs_unregister(pVfs); sqlite3_instvfs_configure(pVfs, 0, 0, 0); sqlite3_free(pVfs); } } void sqlite3_instvfs_reset(sqlite3_vfs *pVfs){ InstVfs *p = (InstVfs *)pVfs; assert( pVfs->xOpen==instOpen ); memset(p->aTime, 0, sizeof(sqlite3_int64)*OS_NUMEVENTS); memset(p->aCount, 0, sizeof(int)*OS_NUMEVENTS); } const char *sqlite3_instvfs_name(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 = "xCheckReservedLock"; break; case OS_FILECONTROL: zEvent = "xFileControl"; break; case OS_SECTORSIZE: zEvent = "xSectorSize"; break; case OS_DEVCHAR: zEvent = "xDeviceCharacteristics"; 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; } return zEvent; } void sqlite3_instvfs_get( sqlite3_vfs *pVfs, int eEvent, const char **pzEvent, sqlite3_int64 *pnClick, int *pnCall ){ InstVfs *p = (InstVfs *)pVfs; assert( pVfs->xOpen==instOpen ); if( eEvent<1 || eEvent>=OS_NUMEVENTS ){ *pzEvent = 0; *pnClick = 0; *pnCall = 0; return; } *pzEvent = sqlite3_instvfs_name(eEvent); *pnClick = p->aTime[eEvent]; *pnCall = p->aCount[eEvent]; } #define BINARYLOG_BUFFERSIZE 8192 struct InstVfsBinaryLog { int nBuf; char *zBuf; sqlite3_int64 iOffset; int log_data; sqlite3_file *pOut; char *zOut; /* Log file name */ }; typedef struct InstVfsBinaryLog InstVfsBinaryLog; static void put32bits(unsigned char *p, unsigned int v){ p[0] = v>>24; p[1] = v>>16; p[2] = v>>8; p[3] = v; } static void binarylog_flush(InstVfsBinaryLog *pLog){ sqlite3_file *pFile = pLog->pOut; #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 pFile->pMethods->xWrite(pFile, pLog->zBuf, pLog->nBuf, pLog->iOffset); pLog->iOffset += pLog->nBuf; pLog->nBuf = 0; #ifdef SQLITE_TEST sqlite3_io_error_pending = pending; sqlite3_io_error_persist = persist; sqlite3_diskfull_pending = diskfull; #endif } static void binarylog_xcall( void *p, int eEvent, int iFileId, sqlite3_int64 nClick, int return_code, const char *zName, int flags, int nByte, sqlite3_int64 iOffset ){ InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)p; unsigned char *zRec; if( (28+pLog->nBuf)>BINARYLOG_BUFFERSIZE ){ binarylog_flush(pLog); } zRec = (unsigned char *)&pLog->zBuf[pLog->nBuf]; put32bits(&zRec[0], eEvent); put32bits(&zRec[4], (int)iFileId); put32bits(&zRec[8], (int)nClick); put32bits(&zRec[12], return_code); put32bits(&zRec[16], flags); put32bits(&zRec[20], nByte); put32bits(&zRec[24], (int)iOffset); pLog->nBuf += 28; } static void binarylog_xdel(void *p){ /* Close the log file and free the memory allocated for the ** InstVfsBinaryLog structure. */ InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)p; sqlite3_file *pFile = pLog->pOut; if( pLog->nBuf ){ binarylog_flush(pLog); } pFile->pMethods->xClose(pFile); sqlite3_free(pLog->pOut); sqlite3_free(pLog->zBuf); sqlite3_free(pLog); } static void binarylog_blob( sqlite3_vfs *pVfs, const char *zBlob, int nBlob, int isBinary ){ InstVfsBinaryLog *pLog; InstVfs *pInstVfs = (InstVfs *)pVfs; if( pVfs->xOpen!=instOpen || pInstVfs->xCall!=binarylog_xcall ){ return; } pLog = (InstVfsBinaryLog *)pInstVfs->pClient; if( zBlob && (!isBinary || pLog->log_data) ){ unsigned char *zRec; int nWrite; if( nBlob<0 ){ nBlob = strlen(zBlob); } nWrite = nBlob + 28; if( (nWrite+pLog->nBuf)>BINARYLOG_BUFFERSIZE ){ binarylog_flush(pLog); } zRec = (unsigned char *)&pLog->zBuf[pLog->nBuf]; memset(zRec, 0, nWrite); put32bits(&zRec[0], BINARYLOG_STRING); put32bits(&zRec[4], (int)nBlob); put32bits(&zRec[8], (int)isBinary); memcpy(&zRec[28], zBlob, nBlob); pLog->nBuf += nWrite; } } void sqlite3_instvfs_binarylog_call( sqlite3_vfs *pVfs, int eEvent, sqlite3_int64 nClick, int return_code, const char *zString ){ InstVfs *pInstVfs = (InstVfs *)pVfs; InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)pInstVfs->pClient; if( zString ){ binarylog_blob(pVfs, zString, -1, 0); } binarylog_xcall(pLog, eEvent, 0, nClick, return_code, 0, 0, 0, 0); } void sqlite3_instvfs_binarylog_marker( sqlite3_vfs *pVfs, const char *zMarker ){ InstVfs *pInstVfs = (InstVfs *)pVfs; InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)pInstVfs->pClient; binarylog_blob(pVfs, zMarker, -1, 0); binarylog_xcall(pLog, BINARYLOG_MARKER, 0, 0, 0, 0, 0, 0, 0); } sqlite3_vfs *sqlite3_instvfs_binarylog( const char *zVfs, const char *zParentVfs, const char *zLog, int log_data ){ InstVfsBinaryLog *p; sqlite3_vfs *pVfs; sqlite3_vfs *pParent; int nByte; int flags; int rc; pParent = sqlite3_vfs_find(zParentVfs); if( !pParent ){ return 0; } nByte = sizeof(InstVfsBinaryLog) + pParent->mxPathname+1; p = (InstVfsBinaryLog *)sqlite3_malloc(nByte); memset(p, 0, nByte); p->zBuf = sqlite3_malloc(BINARYLOG_BUFFERSIZE); p->zOut = (char *)&p[1]; p->pOut = (sqlite3_file *)sqlite3_malloc(pParent->szOsFile); p->log_data = log_data; pParent->xFullPathname(pParent, zLog, pParent->mxPathname, p->zOut); flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MASTER_JOURNAL; pParent->xDelete(pParent, p->zOut, 0); rc = pParent->xOpen(pParent, p->zOut, p->pOut, flags, &flags); if( rc==SQLITE_OK ){ memcpy(p->zBuf, "sqlite_ostrace1.....", 20); p->iOffset = 0; p->nBuf = 20; } if( rc ){ binarylog_xdel(p); return 0; } pVfs = sqlite3_instvfs_create(zVfs, zParentVfs); if( pVfs ){ sqlite3_instvfs_configure(pVfs, binarylog_xcall, p, binarylog_xdel); } return pVfs; } #endif /* SQLITE_ENABLE_INSTVFS */ /************************************************************************** *************************************************************************** ** Tcl interface starts here. */ #if SQLITE_TEST #include #ifdef SQLITE_ENABLE_INSTVFS struct InstVfsCall { Tcl_Interp *interp; Tcl_Obj *pScript; }; typedef struct InstVfsCall InstVfsCall; static void test_instvfs_xcall( void *p, int eEvent, int iFileId, sqlite3_int64 nClick, int return_code, const char *zName, int flags, int nByte, sqlite3_int64 iOffset ){ int rc; InstVfsCall *pCall = (InstVfsCall *)p; Tcl_Obj *pObj = Tcl_DuplicateObj( pCall->pScript); const char *zEvent = sqlite3_instvfs_name(eEvent); Tcl_IncrRefCount(pObj); Tcl_ListObjAppendElement(0, pObj, Tcl_NewStringObj(zEvent, -1)); Tcl_ListObjAppendElement(0, pObj, Tcl_NewWideIntObj(nClick)); Tcl_ListObjAppendElement(0, pObj, Tcl_NewStringObj(zName, -1)); Tcl_ListObjAppendElement(0, pObj, Tcl_NewIntObj(nByte)); Tcl_ListObjAppendElement(0, pObj, Tcl_NewWideIntObj(iOffset)); rc = Tcl_EvalObjEx(pCall->interp, pObj, TCL_EVAL_GLOBAL|TCL_EVAL_DIRECT); if( rc ){ Tcl_BackgroundError(pCall->interp); } Tcl_DecrRefCount(pObj); } static void test_instvfs_xdel(void *p){ InstVfsCall *pCall = (InstVfsCall *)p; Tcl_DecrRefCount(pCall->pScript); sqlite3_free(pCall); } static int test_sqlite3_instvfs( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ static const char *IV_strs[] = { "create", "destroy", "reset", "report", "configure", "binarylog", "marker", 0 }; enum IV_enum { IV_CREATE, IV_DESTROY, IV_RESET, IV_REPORT, IV_CONFIGURE, IV_BINARYLOG, IV_MARKER }; int iSub; if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ..."); } if( Tcl_GetIndexFromObj(interp, objv[1], IV_strs, "sub-command", 0, &iSub) ){ return TCL_ERROR; } switch( (enum IV_enum)iSub ){ case IV_CREATE: { char *zParent = 0; sqlite3_vfs *p; int isDefault = 0; if( objc>2 && 0==strcmp("-default", Tcl_GetString(objv[2])) ){ isDefault = 1; } if( (objc-isDefault)!=4 && (objc-isDefault)!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "?-default? NAME ?PARENT-VFS?"); return TCL_ERROR; } if( objc==(4+isDefault) ){ zParent = Tcl_GetString(objv[3+isDefault]); } p = sqlite3_instvfs_create(Tcl_GetString(objv[2+isDefault]), zParent); if( !p ){ Tcl_AppendResult(interp, "error creating vfs ", 0); return TCL_ERROR; } if( isDefault ){ sqlite3_vfs_register(p, 1); } Tcl_SetObjResult(interp, objv[2]); break; } case IV_BINARYLOG: { char *zName = 0; char *zLog = 0; char *zParent = 0; sqlite3_vfs *p; int isDefault = 0; int isLogdata = 0; int argbase = 2; for(argbase=2; argbase<(objc-2); argbase++){ if( 0==strcmp("-default", Tcl_GetString(objv[argbase])) ){ isDefault = 1; } else if( 0==strcmp("-parent", Tcl_GetString(objv[argbase])) ){ argbase++; zParent = Tcl_GetString(objv[argbase]); } else if( 0==strcmp("-logdata", Tcl_GetString(objv[argbase])) ){ isLogdata = 1; }else{ break; } } if( (objc-argbase)!=2 ){ Tcl_WrongNumArgs( interp, 2, objv, "?-default? ?-parent VFS? ?-logdata? NAME LOGFILE" ); return TCL_ERROR; } zName = Tcl_GetString(objv[argbase]); zLog = Tcl_GetString(objv[argbase+1]); p = sqlite3_instvfs_binarylog(zName, zParent, zLog, isLogdata); if( !p ){ Tcl_AppendResult(interp, "error creating vfs ", 0); return TCL_ERROR; } if( isDefault ){ sqlite3_vfs_register(p, 1); } Tcl_SetObjResult(interp, objv[2]); break; } case IV_MARKER: { sqlite3_vfs *p; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 2, objv, "VFS MARKER"); return TCL_ERROR; } p = sqlite3_vfs_find(Tcl_GetString(objv[2])); if( !p || p->xOpen!=instOpen ){ Tcl_AppendResult(interp, "no such vfs: ", Tcl_GetString(objv[2]), 0); return TCL_ERROR; } sqlite3_instvfs_binarylog_marker(p, Tcl_GetString(objv[3])); Tcl_ResetResult(interp); break; } case IV_CONFIGURE: { InstVfsCall *pCall; sqlite3_vfs *p; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT"); return TCL_ERROR; } p = sqlite3_vfs_find(Tcl_GetString(objv[2])); if( !p || p->xOpen!=instOpen ){ Tcl_AppendResult(interp, "no such vfs: ", Tcl_GetString(objv[2]), 0); return TCL_ERROR; } if( strlen(Tcl_GetString(objv[3])) ){ pCall = (InstVfsCall *)sqlite3_malloc(sizeof(InstVfsCall)); pCall->interp = interp; pCall->pScript = Tcl_DuplicateObj(objv[3]); Tcl_IncrRefCount(pCall->pScript); sqlite3_instvfs_configure(p, test_instvfs_xcall, (void *)pCall, test_instvfs_xdel ); }else{ sqlite3_instvfs_configure(p, 0, 0, 0); } break; } case IV_REPORT: case IV_DESTROY: case IV_RESET: { sqlite3_vfs *p; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "NAME"); return TCL_ERROR; } p = sqlite3_vfs_find(Tcl_GetString(objv[2])); if( !p || p->xOpen!=instOpen ){ Tcl_AppendResult(interp, "no such vfs: ", Tcl_GetString(objv[2]), 0); return TCL_ERROR; } if( ((enum IV_enum)iSub)==IV_DESTROY ){ sqlite3_instvfs_destroy(p); } if( ((enum IV_enum)iSub)==IV_RESET ){ sqlite3_instvfs_reset(p); } if( ((enum IV_enum)iSub)==IV_REPORT ){ int ii; Tcl_Obj *pRet = Tcl_NewObj(); const char *zName = (char *)1; sqlite3_int64 nClick; int nCall; for(ii=1; zName; ii++){ sqlite3_instvfs_get(p, ii, &zName, &nClick, &nCall); if( zName ){ Tcl_Obj *pElem = Tcl_NewObj(); Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zName, -1)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(nCall)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewWideIntObj(nClick)); Tcl_ListObjAppendElement(0, pRet, pElem); } } Tcl_SetObjResult(interp, pRet); } break; } } return TCL_OK; } #endif /* SQLITE_ENABLE_INSTVFS */ /* Alternative implementation of sqlite3_instvfs when the real ** implementation is unavailable. */ #ifndef SQLITE_ENABLE_INSTVFS static int test_sqlite3_instvfs( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ Tcl_AppendResult(interp, "not compiled with -DSQLITE_ENABLE_INSTVFS; sqlite3_instvfs is " "unavailable", (char*)0); return TCL_ERROR; } #endif /* !defined(SQLITE_ENABLE_INSTVFS) */ int SqlitetestOsinst_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "sqlite3_instvfs", test_sqlite3_instvfs, 0, 0); return TCL_OK; } #endif /* SQLITE_TEST */