#include "sqlite3.h"
#include <string.h>
#include <assert.h>
#include <stdio.h>
#if SQLITE_OS_UNIX
# include <unistd.h>
#endif
typedef struct VLogLog VLogLog;
typedef struct VLogVfs VLogVfs;
typedef struct VLogFile VLogFile;
struct VLogLog {
VLogLog *pNext;
VLogLog **ppPrev;
int nRef;
int nFilename;
char *zFilename;
FILE *out;
};
struct VLogVfs {
sqlite3_vfs base;
sqlite3_vfs *pVfs;
};
struct VLogFile {
sqlite3_file base;
sqlite3_file *pReal;
VLogLog *pLog;
};
#define REALVFS(p) (((VLogVfs*)(p))->pVfs)
static int vlogClose(sqlite3_file*);
static int vlogRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
static int vlogWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
static int vlogTruncate(sqlite3_file*, sqlite3_int64 size);
static int vlogSync(sqlite3_file*, int flags);
static int vlogFileSize(sqlite3_file*, sqlite3_int64 *pSize);
static int vlogLock(sqlite3_file*, int);
static int vlogUnlock(sqlite3_file*, int);
static int vlogCheckReservedLock(sqlite3_file*, int *pResOut);
static int vlogFileControl(sqlite3_file*, int op, void *pArg);
static int vlogSectorSize(sqlite3_file*);
static int vlogDeviceCharacteristics(sqlite3_file*);
static int vlogOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
static int vlogDelete(sqlite3_vfs*, const char *zName, int syncDir);
static int vlogAccess(sqlite3_vfs*, const char *zName, int flags, int *);
static int vlogFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
static void *vlogDlOpen(sqlite3_vfs*, const char *zFilename);
static void vlogDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
static void (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
static void vlogDlClose(sqlite3_vfs*, void*);
static int vlogRandomness(sqlite3_vfs*, int nByte, char *zOut);
static int vlogSleep(sqlite3_vfs*, int microseconds);
static int vlogCurrentTime(sqlite3_vfs*, double*);
static int vlogGetLastError(sqlite3_vfs*, int, char *);
static int vlogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
static VLogVfs vlog_vfs = {
{
1,
0,
1024,
0,
"vfslog",
0,
vlogOpen,
vlogDelete,
vlogAccess,
vlogFullPathname,
vlogDlOpen,
vlogDlError,
vlogDlSym,
vlogDlClose,
vlogRandomness,
vlogSleep,
vlogCurrentTime,
vlogGetLastError,
vlogCurrentTimeInt64
},
0
};
static sqlite3_io_methods vlog_io_methods = {
1,
vlogClose,
vlogRead,
vlogWrite,
vlogTruncate,
vlogSync,
vlogFileSize,
vlogLock,
vlogUnlock,
vlogCheckReservedLock,
vlogFileControl,
vlogSectorSize,
vlogDeviceCharacteristics,
0,
0,
0,
0
};
#if SQLITE_OS_UNIX && !defined(NO_GETTOD)
#include <sys/time.h>
static sqlite3_uint64 vlog_time(){
struct timeval sTime;
gettimeofday(&sTime, 0);
return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000;
}
#elif SQLITE_OS_WIN
#include <windows.h>
#include <time.h>
static sqlite3_uint64 vlog_time(){
FILETIME ft;
sqlite3_uint64 u64time = 0;
GetSystemTimeAsFileTime(&ft);
u64time |= ft.dwHighDateTime;
u64time <<= 32;
u64time |= ft.dwLowDateTime;
return u64time /(sqlite3_uint64)10;
}
#else
static sqlite3_uint64 vlog_time(){
return 0;
}
#endif
static void vlogLogPrint(
VLogLog *pLog,
sqlite3_int64 tStart,
sqlite3_int64 tElapse,
const char *zOp,
sqlite3_int64 iArg1,
sqlite3_int64 iArg2,
const char *zArg3,
int iRes
){
char z1[40], z2[40], z3[2000];
if( pLog==0 ) return;
if( iArg1>=0 ){
sqlite3_snprintf(sizeof(z1), z1, "%lld", iArg1);
}else{
z1[0] = 0;
}
if( iArg2>=0 ){
sqlite3_snprintf(sizeof(z2), z2, "%lld", iArg2);
}else{
z2[0] = 0;
}
if( zArg3 ){
sqlite3_snprintf(sizeof(z3), z3, "\"%.*w\"", sizeof(z3)-4, zArg3);
}else{
z3[0] = 0;
}
fprintf(pLog->out,"%lld,%lld,%s,%d,%s,%s,%s,%d\n",
tStart, tElapse, zOp, pLog->zFilename==0, z1, z2, z3, iRes);
}
static VLogLog *allLogs = 0;
static void vlogLogClose(VLogLog *p){
if( p ){
sqlite3_mutex *pMutex;
p->nRef--;
if( p->nRef>0 || p->zFilename==0 ) return;
pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
sqlite3_mutex_enter(pMutex);
*p->ppPrev = p->pNext;
if( p->pNext ) p->pNext->ppPrev = p->ppPrev;
sqlite3_mutex_leave(pMutex);
fclose(p->out);
sqlite3_free(p);
}
}
static VLogLog *vlogLogOpen(const char *zFilename){
int nName = (int)strlen(zFilename);
int isJournal = 0;
sqlite3_mutex *pMutex;
VLogLog *pLog, *pTemp;
sqlite3_int64 tNow = 0;
if( nName>4 && strcmp(zFilename+nName-4,"-wal")==0 ){
return 0;
}else
if( nName>8 && strcmp(zFilename+nName-8,"-journal")==0 ){
nName -= 8;
isJournal = 1;
}else if( nName>12
&& sqlite3_strglob("-mj??????9??", zFilename+nName-12)==0 ){
return 0;
}
pTemp = sqlite3_malloc64( sizeof(*pLog)*2 + nName + 60 );
if( pTemp==0 ) return 0;
pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
sqlite3_mutex_enter(pMutex);
for(pLog=allLogs; pLog; pLog=pLog->pNext){
if( pLog->nFilename==nName && !memcmp(pLog->zFilename, zFilename, nName) ){
break;
}
}
if( pLog==0 ){
pLog = pTemp;
pTemp = 0;
memset(pLog, 0, sizeof(*pLog)*2);
pLog->zFilename = (char*)&pLog[2];
tNow = vlog_time();
sqlite3_snprintf(nName+60, pLog->zFilename, "%.*s-debuglog-%lld",
nName, zFilename, tNow);
pLog->out = fopen(pLog->zFilename, "a");
if( pLog->out==0 ){
sqlite3_mutex_leave(pMutex);
sqlite3_free(pLog);
return 0;
}
pLog->nFilename = nName;
pLog[1].out = pLog[0].out;
pLog->ppPrev = &allLogs;
if( allLogs ) allLogs->ppPrev = &pLog->pNext;
pLog->pNext = allLogs;
allLogs = pLog;
}
sqlite3_mutex_leave(pMutex);
if( pTemp ){
sqlite3_free(pTemp);
}else{
#if SQLITE_OS_UNIX
char zHost[200];
zHost[0] = 0;
gethostname(zHost, sizeof(zHost)-1);
zHost[sizeof(zHost)-1] = 0;
vlogLogPrint(pLog, tNow, 0, "IDENT", getpid(), -1, zHost, 0);
#endif
}
if( pLog && isJournal ) pLog++;
pLog->nRef++;
return pLog;
}
static int vlogClose(sqlite3_file *pFile){
sqlite3_uint64 tStart, tElapse;
int rc = SQLITE_OK;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
if( p->pReal->pMethods ){
rc = p->pReal->pMethods->xClose(p->pReal);
}
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "CLOSE", -1, -1, 0, rc);
vlogLogClose(p->pLog);
return rc;
}
static void vlogSignature(unsigned char *p, int n, char *zCksum){
unsigned int s0 = 0, s1 = 0;
unsigned int *pI;
int i;
if( n<=16 ){
for(i=0; i<n; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]);
}else{
pI = (unsigned int*)p;
for(i=0; i<n-7; i+=8){
s0 += pI[0] + s1;
s1 += pI[1] + s0;
pI += 2;
}
for(i=0; i<8; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]);
sqlite3_snprintf(18, zCksum+i*2, "-%08x%08x", s0, s1);
}
}
static int bigToNative(const unsigned char *x){
return (x[0]<<24) + (x[1]<<16) + (x[2]<<8) + x[3];
}
static int vlogRead(
sqlite3_file *pFile,
void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
char zSig[40];
tStart = vlog_time();
rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
tElapse = vlog_time() - tStart;
if( rc==SQLITE_OK ){
vlogSignature(zBuf, iAmt, zSig);
}else{
zSig[0] = 0;
}
vlogLogPrint(p->pLog, tStart, tElapse, "READ", iAmt, iOfst, zSig, rc);
if( rc==SQLITE_OK
&& p->pLog
&& p->pLog->zFilename
&& iOfst<=24
&& iOfst+iAmt>=28
){
unsigned char *x = ((unsigned char*)zBuf)+(24-iOfst);
unsigned iCtr, nFree = -1;
char *zFree = 0;
char zStr[12];
iCtr = bigToNative(x);
if( iOfst+iAmt>=40 ){
zFree = zStr;
sqlite3_snprintf(sizeof(zStr), zStr, "%d", bigToNative(x+8));
nFree = bigToNative(x+12);
}
vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-READ", iCtr, nFree, zFree, 0);
}
return rc;
}
static int vlogWrite(
sqlite3_file *pFile,
const void *z,
int iAmt,
sqlite_int64 iOfst
){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
char zSig[40];
tStart = vlog_time();
vlogSignature((unsigned char*)z, iAmt, zSig);
rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "WRITE", iAmt, iOfst, zSig, rc);
if( rc==SQLITE_OK
&& p->pLog
&& p->pLog->zFilename
&& iOfst<=24
&& iOfst+iAmt>=28
){
unsigned char *x = ((unsigned char*)z)+(24-iOfst);
unsigned iCtr, nFree = -1;
char *zFree = 0;
char zStr[12];
iCtr = bigToNative(x);
if( iOfst+iAmt>=40 ){
zFree = zStr;
sqlite3_snprintf(sizeof(zStr), zStr, "%d", bigToNative(x+8));
nFree = bigToNative(x+12);
}
vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-WRITE", iCtr, nFree, zFree, 0);
}
return rc;
}
static int vlogTruncate(sqlite3_file *pFile, sqlite_int64 size){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xTruncate(p->pReal, size);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "TRUNCATE", size, -1, 0, rc);
return rc;
}
static int vlogSync(sqlite3_file *pFile, int flags){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xSync(p->pReal, flags);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "SYNC", flags, -1, 0, rc);
return rc;
}
static int vlogFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "FILESIZE", *pSize, -1, 0, rc);
return rc;
}
static int vlogLock(sqlite3_file *pFile, int eLock){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xLock(p->pReal, eLock);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "LOCK", eLock, -1, 0, rc);
return rc;
}
static int vlogUnlock(sqlite3_file *pFile, int eLock){
int rc;
sqlite3_uint64 tStart;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
vlogLogPrint(p->pLog, tStart, 0, "UNLOCK", eLock, -1, 0, 0);
rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
return rc;
}
static int vlogCheckReservedLock(sqlite3_file *pFile, int *pResOut){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "CHECKRESERVEDLOCK",
*pResOut, -1, "", rc);
return rc;
}
static int vlogFileControl(sqlite3_file *pFile, int op, void *pArg){
VLogFile *p = (VLogFile *)pFile;
sqlite3_uint64 tStart, tElapse;
int rc;
tStart = vlog_time();
rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
*(char**)pArg = sqlite3_mprintf("vlog/%z", *(char**)pArg);
}
tElapse = vlog_time() - tStart;
if( op==SQLITE_FCNTL_TRACE ){
vlogLogPrint(p->pLog, tStart, tElapse, "TRACE", op, -1, pArg, rc);
}else if( op==SQLITE_FCNTL_PRAGMA ){
const char **azArg = (const char **)pArg;
vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, azArg[1], rc);
}else if( op==SQLITE_FCNTL_SIZE_HINT ){
sqlite3_int64 sz = *(sqlite3_int64*)pArg;
vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, sz, 0, rc);
}else{
vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, 0, rc);
}
return rc;
}
static int vlogSectorSize(sqlite3_file *pFile){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xSectorSize(p->pReal);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "SECTORSIZE", -1, -1, 0, rc);
return rc;
}
static int vlogDeviceCharacteristics(sqlite3_file *pFile){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "DEVCHAR", -1, -1, 0, rc);
return rc;
}
static int vlogOpen(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_file *pFile,
int flags,
int *pOutFlags
){
int rc;
sqlite3_uint64 tStart, tElapse;
sqlite3_int64 iArg2;
VLogFile *p = (VLogFile*)pFile;
p->pReal = (sqlite3_file*)&p[1];
if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_MAIN_JOURNAL))!=0 ){
p->pLog = vlogLogOpen(zName);
}else{
p->pLog = 0;
}
tStart = vlog_time();
rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags);
tElapse = vlog_time() - tStart;
iArg2 = pOutFlags ? *pOutFlags : -1;
vlogLogPrint(p->pLog, tStart, tElapse, "OPEN", flags, iArg2, 0, rc);
if( rc==SQLITE_OK ){
pFile->pMethods = &vlog_io_methods;
}else{
if( p->pLog ) vlogLogClose(p->pLog);
p->pLog = 0;
}
return rc;
}
static int vlogDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogLog *pLog;
tStart = vlog_time();
rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync);
tElapse = vlog_time() - tStart;
pLog = vlogLogOpen(zPath);
vlogLogPrint(pLog, tStart, tElapse, "DELETE", dirSync, -1, 0, rc);
vlogLogClose(pLog);
return rc;
}
static int vlogAccess(
sqlite3_vfs *pVfs,
const char *zPath,
int flags,
int *pResOut
){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogLog *pLog;
tStart = vlog_time();
rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut);
tElapse = vlog_time() - tStart;
pLog = vlogLogOpen(zPath);
vlogLogPrint(pLog, tStart, tElapse, "ACCESS", flags, *pResOut, 0, rc);
vlogLogClose(pLog);
return rc;
}
static int vlogFullPathname(
sqlite3_vfs *pVfs,
const char *zPath,
int nOut,
char *zOut
){
return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
}
static void *vlogDlOpen(sqlite3_vfs *pVfs, const char *zPath){
return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath);
}
static void vlogDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg);
}
static void (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym);
}
static void vlogDlClose(sqlite3_vfs *pVfs, void *pHandle){
REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle);
}
static int vlogRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
}
static int vlogSleep(sqlite3_vfs *pVfs, int nMicro){
return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro);
}
static int vlogCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut);
}
static int vlogGetLastError(sqlite3_vfs *pVfs, int a, char *b){
return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b);
}
static int vlogCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p);
}
int sqlite3_register_vfslog(const char *zArg){
vlog_vfs.pVfs = sqlite3_vfs_find(0);
if( vlog_vfs.pVfs==0 ) return SQLITE_ERROR;
vlog_vfs.base.szOsFile = sizeof(VLogFile) + vlog_vfs.pVfs->szOsFile;
return sqlite3_vfs_register(&vlog_vfs.base, 1);
}