diff options
Diffstat (limited to '')
-rw-r--r-- | src/test_osinst.c | 1224 |
1 files changed, 1224 insertions, 0 deletions
diff --git a/src/test_osinst.c b/src/test_osinst.c new file mode 100644 index 0000000..3e698c0 --- /dev/null +++ b/src/test_osinst.c @@ -0,0 +1,1224 @@ +/* +** 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 <string.h> +#include <assert.h> + + +/* +** Maximum pathname length supported by the vfslog backend. +*/ +#define INST_MAX_PATHNAME 512 + +#define OS_ACCESS 1 +#define OS_CHECKRESERVEDLOCK 2 +#define OS_CLOSE 3 +#define OS_CURRENTTIME 4 +#define OS_DELETE 5 +#define OS_DEVCHAR 6 +#define OS_FILECONTROL 7 +#define OS_FILESIZE 8 +#define OS_FULLPATHNAME 9 +#define OS_LOCK 11 +#define OS_OPEN 12 +#define OS_RANDOMNESS 13 +#define OS_READ 14 +#define OS_SECTORSIZE 15 +#define OS_SLEEP 16 +#define OS_SYNC 17 +#define OS_TRUNCATE 18 +#define OS_UNLOCK 19 +#define OS_WRITE 20 +#define OS_SHMUNMAP 22 +#define OS_SHMMAP 23 +#define OS_SHMLOCK 25 +#define OS_SHMBARRIER 26 +#define OS_ANNOTATE 28 + +#define OS_NUMEVENTS 29 + +#define VFSLOG_BUFFERSIZE 8192 + +typedef struct VfslogVfs VfslogVfs; +typedef struct VfslogFile VfslogFile; + +struct VfslogVfs { + sqlite3_vfs base; /* VFS methods */ + sqlite3_vfs *pVfs; /* Parent VFS */ + int iNextFileId; /* Next file id */ + sqlite3_file *pLog; /* Log file handle */ + sqlite3_int64 iOffset; /* Log file offset of start of write buffer */ + int nBuf; /* Number of valid bytes in aBuf[] */ + char aBuf[VFSLOG_BUFFERSIZE]; /* Write buffer */ +}; + +struct VfslogFile { + sqlite3_file base; /* IO methods */ + sqlite3_file *pReal; /* Underlying file handle */ + sqlite3_vfs *pVfslog; /* Associated VsflogVfs object */ + int iFileId; /* File id number */ +}; + +#define REALVFS(p) (((VfslogVfs *)(p))->pVfs) + + + +/* +** Method declarations for vfslog_file. +*/ +static int vfslogClose(sqlite3_file*); +static int vfslogRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); +static int vfslogWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); +static int vfslogTruncate(sqlite3_file*, sqlite3_int64 size); +static int vfslogSync(sqlite3_file*, int flags); +static int vfslogFileSize(sqlite3_file*, sqlite3_int64 *pSize); +static int vfslogLock(sqlite3_file*, int); +static int vfslogUnlock(sqlite3_file*, int); +static int vfslogCheckReservedLock(sqlite3_file*, int *pResOut); +static int vfslogFileControl(sqlite3_file*, int op, void *pArg); +static int vfslogSectorSize(sqlite3_file*); +static int vfslogDeviceCharacteristics(sqlite3_file*); + +static int vfslogShmLock(sqlite3_file *pFile, int ofst, int n, int flags); +static int vfslogShmMap(sqlite3_file *pFile,int,int,int,volatile void **); +static void vfslogShmBarrier(sqlite3_file*); +static int vfslogShmUnmap(sqlite3_file *pFile, int deleteFlag); + +/* +** Method declarations for vfslog_vfs. +*/ +static int vfslogOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); +static int vfslogDelete(sqlite3_vfs*, const char *zName, int syncDir); +static int vfslogAccess(sqlite3_vfs*, const char *zName, int flags, int *); +static int vfslogFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); +static void *vfslogDlOpen(sqlite3_vfs*, const char *zFilename); +static void vfslogDlError(sqlite3_vfs*, int nByte, char *zErrMsg); +static void (*vfslogDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void); +static void vfslogDlClose(sqlite3_vfs*, void*); +static int vfslogRandomness(sqlite3_vfs*, int nByte, char *zOut); +static int vfslogSleep(sqlite3_vfs*, int microseconds); +static int vfslogCurrentTime(sqlite3_vfs*, double*); + +static int vfslogGetLastError(sqlite3_vfs*, int, char *); +static int vfslogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); + +static sqlite3_vfs vfslog_vfs = { + 1, /* iVersion */ + sizeof(VfslogFile), /* szOsFile */ + INST_MAX_PATHNAME, /* mxPathname */ + 0, /* pNext */ + 0, /* zName */ + 0, /* pAppData */ + vfslogOpen, /* xOpen */ + vfslogDelete, /* xDelete */ + vfslogAccess, /* xAccess */ + vfslogFullPathname, /* xFullPathname */ + vfslogDlOpen, /* xDlOpen */ + vfslogDlError, /* xDlError */ + vfslogDlSym, /* xDlSym */ + vfslogDlClose, /* xDlClose */ + vfslogRandomness, /* xRandomness */ + vfslogSleep, /* xSleep */ + vfslogCurrentTime, /* xCurrentTime */ + vfslogGetLastError, /* xGetLastError */ + vfslogCurrentTimeInt64 /* xCurrentTime */ +}; + +static sqlite3_io_methods vfslog_io_methods = { + 2, /* iVersion */ + vfslogClose, /* xClose */ + vfslogRead, /* xRead */ + vfslogWrite, /* xWrite */ + vfslogTruncate, /* xTruncate */ + vfslogSync, /* xSync */ + vfslogFileSize, /* xFileSize */ + vfslogLock, /* xLock */ + vfslogUnlock, /* xUnlock */ + vfslogCheckReservedLock, /* xCheckReservedLock */ + vfslogFileControl, /* xFileControl */ + vfslogSectorSize, /* xSectorSize */ + vfslogDeviceCharacteristics, /* xDeviceCharacteristics */ + vfslogShmMap, /* xShmMap */ + vfslogShmLock, /* xShmLock */ + vfslogShmBarrier, /* xShmBarrier */ + vfslogShmUnmap /* xShmUnmap */ +}; + +#if SQLITE_OS_UNIX && !defined(NO_GETTOD) +#include <sys/time.h> +static sqlite3_uint64 vfslog_time(){ + struct timeval sTime; + gettimeofday(&sTime, 0); + return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000; +} +#elif SQLITE_OS_WIN +#include <time.h> +static sqlite3_uint64 vfslog_time(){ + FILETIME ft; + sqlite3_uint64 u64time = 0; + + GetSystemTimeAsFileTime(&ft); + + u64time |= ft.dwHighDateTime; + u64time <<= 32; + u64time |= ft.dwLowDateTime; + + /* ft is 100-nanosecond intervals, we want microseconds */ + return u64time /(sqlite3_uint64)10; +} +#else +static sqlite3_uint64 vfslog_time(){ + return 0; +} +#endif + +static void vfslog_call(sqlite3_vfs *, int, int, sqlite3_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_SUPER_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_SUPER_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; i<p->nFile; i++){ + sqlite3_free(p->azFile[i]); + } + sqlite3_free(p->azFile); + sqlite3_free(p->zTransient); + sqlite3_free(p); + return SQLITE_OK; +} + +/* +** Move a vfslog cursor to the next entry in the file. +*/ +static int vlogNext(sqlite3_vtab_cursor *pCursor){ + VfslogCsr *pCsr = (VfslogCsr *)pCursor; + VfslogVtab *p = (VfslogVtab *)pCursor->pVtab; + int rc = SQLITE_OK; + int nRead; + + sqlite3_free(pCsr->zTransient); + pCsr->zTransient = 0; + + nRead = 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 */ |