| /* |
| ** 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 <string.h> |
| #include <assert.h> |
| |
| /* |
| ** 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 */ |
| 0, /* xShmOpen */ |
| 0, /* xShmSize */ |
| 0, /* xShmPush */ |
| 0, /* xShmPull */ |
| 0, /* xShmLock */ |
| 0, /* xShmClose */ |
| 0, /* xShmDelete */ |
| 0, /* xRename */ |
| 0 /* xCurrentTimeInt64 */ |
| }; |
| |
| 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 <tcl.h> |
| |
| #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 */ |