]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the vfsstat.c loadable extension - a VFS shim that measures the amount
authordrh <drh@noemail.net>
Sat, 28 May 2016 14:53:48 +0000 (14:53 +0000)
committerdrh <drh@noemail.net>
Sat, 28 May 2016 14:53:48 +0000 (14:53 +0000)
of I/O, and an eponymous virtual table that is used to extract and view
the measurements.

FossilOrigin-Name: 0987487dd4ebfcf66ddeec8ceca47775216a0887

ext/misc/vfsstat.c [new file with mode: 0644]
manifest
manifest.uuid

diff --git a/ext/misc/vfsstat.c b/ext/misc/vfsstat.c
new file mode 100644 (file)
index 0000000..e519de6
--- /dev/null
@@ -0,0 +1,819 @@
+/*
+** 2016-05-27
+**
+** 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 shim that
+** tracks I/O.  Access to the accumulated status counts is provided using
+** an eponymous virtual table.
+*/
+#include <sqlite3ext.h>
+SQLITE_EXTENSION_INIT1
+
+/*
+** This module contains code for a wrapper VFS that cause stats for
+** most VFS calls to be recorded.
+**
+** To use this module, first compile it as a loadable extension.  See
+** https://www.sqlite.org/loadext.html#build for compilations instructions.
+**
+** After compliing, load this extension, then open database connections to be
+** measured.  Query usages status using the vfsstat virtual table:
+**
+**         SELECT * FROM vfsstat;
+**
+** Reset counters using UPDATE statements against vfsstat:
+**
+**         UPDATE vfsstat SET count=0;
+**
+** EXAMPLE SCRIPT:
+**
+**      .load ./vfsstat
+**      .open test.db
+**      DROP TABLE IF EXISTS t1;
+**      CREATE TABLE t1(x,y);
+**      INSERT INTO t1 VALUES(123, randomblob(5000));
+**      CREATE INDEX t1x ON t1(x);
+**      DROP TABLE t1;
+**      VACUUM;
+**      SELECT * FROM vfsstat WHERE count>0;
+**
+** LIMITATIONS:
+** 
+** This module increments counters without using mutex protection.  So if
+** two or more threads try to use this module at the same time, race conditions
+** may occur which mess up the counts.  This is harmless, other than giving
+** incorrect statistics.
+*/
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+/*
+** File types
+*/
+#define VFSSTAT_MAIN         0   /* Main database file */
+#define VFSSTAT_JOURNAL      1   /* Rollback journal */
+#define VFSSTAT_WAL          2   /* Write-ahead log file */
+#define VFSSTAT_MASTERJRNL   3   /* Master journal */
+#define VFSSTAT_SUBJRNL      4   /* Subjournal */
+#define VFSSTAT_TEMPDB       5   /* TEMP database */
+#define VFSSTAT_TEMPJRNL     6   /* Journal for TEMP database */
+#define VFSSTAT_TRANSIENT    7   /* Transient database */
+#define VFSSTAT_ANY          8   /* Unspecified file type */
+#define VFSSTAT_nFile        9   /* This many file types */
+
+/* Names of the file types.  These are allowed values for the
+** first column of the vfsstat virtual table.
+*/
+static const char *azFile[] = {
+  "database", "journal", "wal", "master-journal", "sub-journal",
+  "temp-database", "temp-journal", "transient-db", "*"
+};
+
+/*
+** Stat types
+*/
+#define VFSSTAT_BYTESIN      0   /* Bytes read in */
+#define VFSSTAT_BYTESOUT     1   /* Bytes written out */   
+#define VFSSTAT_READ         2   /* Read requests */
+#define VFSSTAT_WRITE        3   /* Write requests */
+#define VFSSTAT_SYNC         4   /* Syncs */
+#define VFSSTAT_OPEN         5   /* File opens */
+#define VFSSTAT_LOCK         6   /* Lock requests */
+#define VFSSTAT_ACCESS       0   /* xAccess calls.  filetype==ANY only */
+#define VFSSTAT_DELETE       1   /* xDelete calls.  filetype==ANY only */
+#define VFSSTAT_FULLPATH     2   /* xFullPathname calls.  ANY only */
+#define VFSSTAT_RANDOM       3   /* xRandomness calls.    ANY only */
+#define VFSSTAT_SLEEP        4   /* xSleep calls.         ANY only */
+#define VFSSTAT_CURTIME      5   /* xCurrentTime calls.   ANY only */
+#define VFSSTAT_nStat        7   /* This many stat types */
+
+
+/* Names for the second column of the vfsstat virtual table for all
+** cases except when the first column is "*" or VFSSTAT_ANY. */
+static const char *azStat[] = {
+  "bytes-in", "bytes-out", "read", "write", "sync", "open", "lock",
+};
+static const char *azStatAny[] = {
+  "access", "delete", "fullpathname", "randomness", "sleep", "currenttimestamp",
+  "not-used"
+};
+
+/* Total number of counters */
+#define VFSSTAT_MXCNT  (VFSSTAT_nStat*VFSSTAT_nFile)
+
+/*
+** Performance stats are collected in an instance of the following
+** global array.
+*/
+static sqlite3_uint64 aVfsCnt[VFSSTAT_MXCNT];
+
+/*
+** Access to a specific counter
+*/
+#define STATCNT(filetype,stat) (aVfsCnt[(filetype)*VFSSTAT_nStat+(stat)])
+
+/*
+** Forward declaration of objects used by this utility
+*/
+typedef struct VStatVfs VStatVfs;
+typedef struct VStatFile VStatFile;
+
+/* An instance of the VFS */
+struct VStatVfs {
+  sqlite3_vfs base;               /* VFS methods */
+  sqlite3_vfs *pVfs;              /* Parent VFS */
+};
+
+/* An open file */
+struct VStatFile {
+  sqlite3_file base;              /* IO methods */
+  sqlite3_file *pReal;            /* Underlying file handle */
+  unsigned char eFiletype;        /* What type of file is this */
+};
+
+#define REALVFS(p) (((VStatVfs*)(p))->pVfs)
+
+/*
+** Methods for VStatFile
+*/
+static int vstatClose(sqlite3_file*);
+static int vstatRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int vstatWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
+static int vstatTruncate(sqlite3_file*, sqlite3_int64 size);
+static int vstatSync(sqlite3_file*, int flags);
+static int vstatFileSize(sqlite3_file*, sqlite3_int64 *pSize);
+static int vstatLock(sqlite3_file*, int);
+static int vstatUnlock(sqlite3_file*, int);
+static int vstatCheckReservedLock(sqlite3_file*, int *pResOut);
+static int vstatFileControl(sqlite3_file*, int op, void *pArg);
+static int vstatSectorSize(sqlite3_file*);
+static int vstatDeviceCharacteristics(sqlite3_file*);
+static int vstatShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
+static int vstatShmLock(sqlite3_file*, int offset, int n, int flags);
+static void vstatShmBarrier(sqlite3_file*);
+static int vstatShmUnmap(sqlite3_file*, int deleteFlag);
+static int vstatFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
+static int vstatUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
+
+/*
+** Methods for VStatVfs
+*/
+static int vstatOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
+static int vstatDelete(sqlite3_vfs*, const char *zName, int syncDir);
+static int vstatAccess(sqlite3_vfs*, const char *zName, int flags, int *);
+static int vstatFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
+static void *vstatDlOpen(sqlite3_vfs*, const char *zFilename);
+static void vstatDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
+static void (*vstatDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
+static void vstatDlClose(sqlite3_vfs*, void*);
+static int vstatRandomness(sqlite3_vfs*, int nByte, char *zOut);
+static int vstatSleep(sqlite3_vfs*, int microseconds);
+static int vstatCurrentTime(sqlite3_vfs*, double*);
+static int vstatGetLastError(sqlite3_vfs*, int, char *);
+static int vstatCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
+
+static VStatVfs vstat_vfs = {
+  {
+    2,                            /* iVersion */
+    0,                            /* szOsFile (set by register_vstat()) */
+    1024,                         /* mxPathname */
+    0,                            /* pNext */
+    "vfslog",                     /* zName */
+    0,                            /* pAppData */
+    vstatOpen,                     /* xOpen */
+    vstatDelete,                   /* xDelete */
+    vstatAccess,                   /* xAccess */
+    vstatFullPathname,             /* xFullPathname */
+    vstatDlOpen,                   /* xDlOpen */
+    vstatDlError,                  /* xDlError */
+    vstatDlSym,                    /* xDlSym */
+    vstatDlClose,                  /* xDlClose */
+    vstatRandomness,               /* xRandomness */
+    vstatSleep,                    /* xSleep */
+    vstatCurrentTime,              /* xCurrentTime */
+    vstatGetLastError,             /* xGetLastError */
+    vstatCurrentTimeInt64          /* xCurrentTimeInt64 */
+  },
+  0
+};
+
+static const sqlite3_io_methods vstat_io_methods = {
+  3,                              /* iVersion */
+  vstatClose,                      /* xClose */
+  vstatRead,                       /* xRead */
+  vstatWrite,                      /* xWrite */
+  vstatTruncate,                   /* xTruncate */
+  vstatSync,                       /* xSync */
+  vstatFileSize,                   /* xFileSize */
+  vstatLock,                       /* xLock */
+  vstatUnlock,                     /* xUnlock */
+  vstatCheckReservedLock,          /* xCheckReservedLock */
+  vstatFileControl,                /* xFileControl */
+  vstatSectorSize,                 /* xSectorSize */
+  vstatDeviceCharacteristics,      /* xDeviceCharacteristics */
+  vstatShmMap,                     /* xShmMap */
+  vstatShmLock,                    /* xShmLock */
+  vstatShmBarrier,                 /* xShmBarrier */
+  vstatShmUnmap,                   /* xShmUnmap */
+  vstatFetch,                      /* xFetch */
+  vstatUnfetch                     /* xUnfetch */
+};
+
+
+
+/*
+** Close an vstat-file.
+*/
+static int vstatClose(sqlite3_file *pFile){
+  VStatFile *p = (VStatFile *)pFile;
+  int rc = SQLITE_OK;
+
+  if( p->pReal->pMethods ){
+    rc = p->pReal->pMethods->xClose(p->pReal);
+  }
+  return rc;
+}
+
+
+/*
+** Read data from an vstat-file.
+*/
+static int vstatRead(
+  sqlite3_file *pFile, 
+  void *zBuf, 
+  int iAmt, 
+  sqlite_int64 iOfst
+){
+  int rc;
+  VStatFile *p = (VStatFile *)pFile;
+
+  rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
+  STATCNT(p->eFiletype,VFSSTAT_READ)++;
+  if( rc==SQLITE_OK ){
+    STATCNT(p->eFiletype,VFSSTAT_BYTESIN) += iAmt;
+  }
+  return rc;
+}
+
+/*
+** Write data to an vstat-file.
+*/
+static int vstatWrite(
+  sqlite3_file *pFile,
+  const void *z,
+  int iAmt,
+  sqlite_int64 iOfst
+){
+  int rc;
+  VStatFile *p = (VStatFile *)pFile;
+
+  rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst);
+  STATCNT(p->eFiletype,VFSSTAT_WRITE)++;
+  if( rc==SQLITE_OK ){
+    STATCNT(p->eFiletype,VFSSTAT_BYTESOUT) += iAmt;
+  }
+  return rc;
+}
+
+/*
+** Truncate an vstat-file.
+*/
+static int vstatTruncate(sqlite3_file *pFile, sqlite_int64 size){
+  int rc;
+  VStatFile *p = (VStatFile *)pFile;
+  rc = p->pReal->pMethods->xTruncate(p->pReal, size);
+  return rc;
+}
+
+/*
+** Sync an vstat-file.
+*/
+static int vstatSync(sqlite3_file *pFile, int flags){
+  int rc;
+  VStatFile *p = (VStatFile *)pFile;
+  rc = p->pReal->pMethods->xSync(p->pReal, flags);
+  STATCNT(p->eFiletype,VFSSTAT_SYNC)++;
+  return rc;
+}
+
+/*
+** Return the current file-size of an vstat-file.
+*/
+static int vstatFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+  int rc;
+  VStatFile *p = (VStatFile *)pFile;
+  rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
+  return rc;
+}
+
+/*
+** Lock an vstat-file.
+*/
+static int vstatLock(sqlite3_file *pFile, int eLock){
+  int rc;
+  VStatFile *p = (VStatFile *)pFile;
+  rc = p->pReal->pMethods->xLock(p->pReal, eLock);
+  STATCNT(p->eFiletype,VFSSTAT_LOCK)++;
+  return rc;
+}
+
+/*
+** Unlock an vstat-file.
+*/
+static int vstatUnlock(sqlite3_file *pFile, int eLock){
+  int rc;
+  VStatFile *p = (VStatFile *)pFile;
+  rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
+  STATCNT(p->eFiletype,VFSSTAT_LOCK)++;
+  return rc;
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an vstat-file.
+*/
+static int vstatCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+  int rc;
+  VStatFile *p = (VStatFile *)pFile;
+  rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
+  STATCNT(p->eFiletype,VFSSTAT_LOCK)++;
+  return rc;
+}
+
+/*
+** File control method. For custom operations on an vstat-file.
+*/
+static int vstatFileControl(sqlite3_file *pFile, int op, void *pArg){
+  VStatFile *p = (VStatFile *)pFile;
+  int rc;
+  rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
+  if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
+    *(char**)pArg = sqlite3_mprintf("vstat/%z", *(char**)pArg);
+  }
+  return rc;
+}
+
+/*
+** Return the sector-size in bytes for an vstat-file.
+*/
+static int vstatSectorSize(sqlite3_file *pFile){
+  int rc;
+  VStatFile *p = (VStatFile *)pFile;
+  rc = p->pReal->pMethods->xSectorSize(p->pReal);
+  return rc;
+}
+
+/*
+** Return the device characteristic flags supported by an vstat-file.
+*/
+static int vstatDeviceCharacteristics(sqlite3_file *pFile){
+  int rc;
+  VStatFile *p = (VStatFile *)pFile;
+  rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
+  return rc;
+}
+
+/* Create a shared memory file mapping */
+static int vstatShmMap(
+  sqlite3_file *pFile,
+  int iPg,
+  int pgsz,
+  int bExtend,
+  void volatile **pp
+){
+  VStatFile *p = (VStatFile *)pFile;
+  return p->pReal->pMethods->xShmMap(p->pReal, iPg, pgsz, bExtend, pp);
+}
+
+/* Perform locking on a shared-memory segment */
+static int vstatShmLock(sqlite3_file *pFile, int offset, int n, int flags){
+  VStatFile *p = (VStatFile *)pFile;
+  return p->pReal->pMethods->xShmLock(p->pReal, offset, n, flags);
+}
+
+/* Memory barrier operation on shared memory */
+static void vstatShmBarrier(sqlite3_file *pFile){
+  VStatFile *p = (VStatFile *)pFile;
+  return p->pReal->pMethods->xShmBarrier(p->pReal);
+}
+
+/* Unmap a shared memory segment */
+static int vstatShmUnmap(sqlite3_file *pFile, int deleteFlag){
+  VStatFile *p = (VStatFile *)pFile;
+  return p->pReal->pMethods->xShmUnmap(p->pReal, deleteFlag);
+}
+
+/* Fetch a page of a memory-mapped file */
+static int vstatFetch(
+  sqlite3_file *pFile,
+  sqlite3_int64 iOfst,
+  int iAmt,
+  void **pp
+){
+  VStatFile *p = (VStatFile *)pFile;
+  return p->pReal->pMethods->xFetch(p->pReal, iOfst, iAmt, pp);
+}
+
+/* Release a memory-mapped page */
+static int vstatUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
+  VStatFile *p = (VStatFile *)pFile;
+  return p->pReal->pMethods->xUnfetch(p->pReal, iOfst, pPage);
+}
+
+/*
+** Open an vstat file handle.
+*/
+static int vstatOpen(
+  sqlite3_vfs *pVfs,
+  const char *zName,
+  sqlite3_file *pFile,
+  int flags,
+  int *pOutFlags
+){
+  int rc;
+  VStatFile *p = (VStatFile*)pFile;
+
+  p->pReal = (sqlite3_file*)&p[1];
+  rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags);
+  if( flags & SQLITE_OPEN_MAIN_DB ){
+    p->eFiletype = VFSSTAT_MAIN;
+  }else if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
+    p->eFiletype = VFSSTAT_JOURNAL;
+  }else if( flags & SQLITE_OPEN_WAL ){
+    p->eFiletype = VFSSTAT_WAL;
+  }else if( flags & SQLITE_OPEN_MASTER_JOURNAL ){
+    p->eFiletype = VFSSTAT_MASTERJRNL;
+  }else if( flags & SQLITE_OPEN_SUBJOURNAL ){
+    p->eFiletype = VFSSTAT_SUBJRNL;
+  }else if( flags & SQLITE_OPEN_TEMP_DB ){
+    p->eFiletype = VFSSTAT_TEMPDB;
+  }else if( flags & SQLITE_OPEN_TEMP_JOURNAL ){
+    p->eFiletype = VFSSTAT_TEMPJRNL;
+  }else{
+    p->eFiletype = VFSSTAT_TRANSIENT;
+  }
+  STATCNT(p->eFiletype,VFSSTAT_OPEN)++;
+  pFile->pMethods = rc ? 0 : &vstat_io_methods;
+  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 vstatDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+  int rc;
+  rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync);
+  STATCNT(VFSSTAT_ANY,VFSSTAT_DELETE)++;
+  return rc;
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int vstatAccess(
+  sqlite3_vfs *pVfs, 
+  const char *zPath, 
+  int flags, 
+  int *pResOut
+){
+  int rc;
+  rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut);
+  STATCNT(VFSSTAT_ANY,VFSSTAT_ACCESS)++;
+  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 vstatFullPathname(
+  sqlite3_vfs *pVfs, 
+  const char *zPath, 
+  int nOut, 
+  char *zOut
+){
+  STATCNT(VFSSTAT_ANY,VFSSTAT_FULLPATH)++;
+  return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
+}
+
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *vstatDlOpen(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 vstatDlError(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 (*vstatDlSym(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 vstatDlClose(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 vstatRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+  STATCNT(VFSSTAT_ANY,VFSSTAT_RANDOM)++;
+  return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds 
+** actually slept.
+*/
+static int vstatSleep(sqlite3_vfs *pVfs, int nMicro){
+  STATCNT(VFSSTAT_ANY,VFSSTAT_SLEEP)++;
+  return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro);
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int vstatCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+  STATCNT(VFSSTAT_ANY,VFSSTAT_CURTIME)++;
+  return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut);
+}
+
+static int vstatGetLastError(sqlite3_vfs *pVfs, int a, char *b){
+  return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b);
+}
+static int vstatCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
+  STATCNT(VFSSTAT_ANY,VFSSTAT_CURTIME)++;
+  return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p);
+}
+
+/*
+** A virtual table for accessing the stats collected by this VFS shim
+*/
+static int vstattabConnect(sqlite3*, void*, int, const char*const*, 
+                           sqlite3_vtab**,char**);
+static int vstattabBestIndex(sqlite3_vtab*,sqlite3_index_info*);
+static int vstattabDisconnect(sqlite3_vtab*);
+static int vstattabOpen(sqlite3_vtab*, sqlite3_vtab_cursor**);
+static int vstattabClose(sqlite3_vtab_cursor*);
+static int vstattabFilter(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
+                          int argc, sqlite3_value **argv);
+static int vstattabNext(sqlite3_vtab_cursor*);
+static int vstattabEof(sqlite3_vtab_cursor*);
+static int vstattabColumn(sqlite3_vtab_cursor*,sqlite3_context*,int);
+static int vstattabRowid(sqlite3_vtab_cursor*,sqlite3_int64*);
+static int vstattabUpdate(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*);
+
+/* A cursor for the vfsstat virtual table */
+typedef struct VfsStatCursor {
+  sqlite3_vtab_cursor base;       /* Base class.  Must be first */
+  int i;                          /* Pointing to this aVfsCnt[] value */
+} VfsStatCursor;
+
+
+static int vstattabConnect(
+  sqlite3 *db,
+  void *pAux,
+  int argc, const char *const*argv,
+  sqlite3_vtab **ppVtab,
+  char **pzErr
+){
+  sqlite3_vtab *pNew;
+  int rc;
+
+/* Column numbers */
+#define VSTAT_COLUMN_FILE  0 
+#define VSTAT_COLUMN_STAT  1
+#define VSTAT_COLUMN_COUNT 2
+
+  rc = sqlite3_declare_vtab(db,"CREATE TABLE x(file,stat,count)");
+  if( rc==SQLITE_OK ){
+    pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
+    if( pNew==0 ) return SQLITE_NOMEM;
+    memset(pNew, 0, sizeof(*pNew));
+  }
+  return rc;
+}
+
+/*
+** This method is the destructor for vstat table object.
+*/
+static int vstattabDisconnect(sqlite3_vtab *pVtab){
+  sqlite3_free(pVtab);
+  return SQLITE_OK;
+}
+
+/*
+** Constructor for a new vstat table cursor object.
+*/
+static int vstattabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+  VfsStatCursor *pCur;
+  pCur = sqlite3_malloc( sizeof(*pCur) );
+  if( pCur==0 ) return SQLITE_NOMEM;
+  memset(pCur, 0, sizeof(*pCur));
+  *ppCursor = &pCur->base;
+  return SQLITE_OK;
+}
+
+
+/*
+** Destructor for a VfsStatCursor.
+*/
+static int vstattabClose(sqlite3_vtab_cursor *cur){
+  sqlite3_free(cur);
+  return SQLITE_OK;
+}
+
+
+/*
+** Advance a VfsStatCursor to its next row of output.
+*/
+static int vstattabNext(sqlite3_vtab_cursor *cur){
+  ((VfsStatCursor*)cur)->i++;
+  return SQLITE_OK;
+}
+
+/*
+** Return values of columns for the row at which the VfsStatCursor
+** is currently pointing.
+*/
+static int vstattabColumn(
+  sqlite3_vtab_cursor *cur,   /* The cursor */
+  sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
+  int i                       /* Which column to return */
+){
+  VfsStatCursor *pCur = (VfsStatCursor*)cur;
+  switch( i ){
+    case VSTAT_COLUMN_FILE: {
+      sqlite3_result_text(ctx, azFile[pCur->i/VFSSTAT_nStat], -1, SQLITE_STATIC);
+      break;
+    }
+    case VSTAT_COLUMN_STAT: {
+      const char **az;
+      az = (pCur->i/VFSSTAT_nStat)==VFSSTAT_ANY ? azStatAny : azStat;
+      sqlite3_result_text(ctx, az[pCur->i%VFSSTAT_nStat], -1, SQLITE_STATIC);
+      break;
+    }
+    case VSTAT_COLUMN_COUNT: {
+      sqlite3_result_int64(ctx, aVfsCnt[pCur->i]);
+      break;
+    }
+  }
+  return SQLITE_OK;
+}
+
+/*
+** Return the rowid for the current row.
+*/
+static int vstattabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+  VfsStatCursor *pCur = (VfsStatCursor*)cur;
+  *pRowid = pCur->i;
+  return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the cursor has been moved off of the last
+** row of output.
+*/
+static int vstattabEof(sqlite3_vtab_cursor *cur){
+  VfsStatCursor *pCur = (VfsStatCursor*)cur;
+  return pCur->i >= VFSSTAT_MXCNT;
+}
+
+/*
+** Only a full table scan is supported.  So xFilter simply rewinds to
+** the beginning.
+*/
+static int vstattabFilter(
+  sqlite3_vtab_cursor *pVtabCursor, 
+  int idxNum, const char *idxStr,
+  int argc, sqlite3_value **argv
+){
+  VfsStatCursor *pCur = (VfsStatCursor*)pVtabCursor;
+  pCur->i = 0;
+  return SQLITE_OK;
+}
+
+/*
+** Only a forwards full table scan is supported.  xBestIndex is a no-op.
+*/
+static int vstattabBestIndex(
+  sqlite3_vtab *tab,
+  sqlite3_index_info *pIdxInfo
+){
+  return SQLITE_OK;
+}
+
+/*
+** Any VSTAT_COLUMN_COUNT can be changed to a positive integer.
+** No deletions or insertions are allowed.  No changes to other
+** columns are allowed.
+*/
+static int vstattabUpdate(
+  sqlite3_vtab *tab,
+  int argc, sqlite3_value **argv,
+  sqlite3_int64 *pRowid
+){
+  sqlite3_int64 iRowid, x;
+  if( argc==1 ) return SQLITE_ERROR;
+  if( sqlite3_value_type(argv[0])!=SQLITE_INTEGER ) return SQLITE_ERROR;
+  iRowid = sqlite3_value_int64(argv[0]);
+  if( iRowid!=sqlite3_value_int64(argv[1]) ) return SQLITE_ERROR;
+  if( iRowid<0 || iRowid>=VFSSTAT_MXCNT ) return SQLITE_ERROR;
+  if( sqlite3_value_type(argv[VSTAT_COLUMN_COUNT+2])!=SQLITE_INTEGER ){
+    return SQLITE_ERROR;
+  }
+  x = sqlite3_value_int64(argv[VSTAT_COLUMN_COUNT+2]);
+  if( x<0 ) return SQLITE_ERROR;
+  aVfsCnt[iRowid] = x;
+  return SQLITE_OK;
+}
+
+static sqlite3_module VfsStatModule = {
+  0,                         /* iVersion */
+  0,                         /* xCreate */
+  vstattabConnect,           /* xConnect */
+  vstattabBestIndex,         /* xBestIndex */
+  vstattabDisconnect,        /* xDisconnect */
+  0,                         /* xDestroy */
+  vstattabOpen,              /* xOpen - open a cursor */
+  vstattabClose,             /* xClose - close a cursor */
+  vstattabFilter,            /* xFilter - configure scan constraints */
+  vstattabNext,              /* xNext - advance a cursor */
+  vstattabEof,               /* xEof - check for end of scan */
+  vstattabColumn,            /* xColumn - read data */
+  vstattabRowid,             /* xRowid - read data */
+  vstattabUpdate,            /* xUpdate */
+  0,                         /* xBegin */
+  0,                         /* xSync */
+  0,                         /* xCommit */
+  0,                         /* xRollback */
+  0,                         /* xFindMethod */
+  0,                         /* xRename */
+};
+
+/*
+** This routine is an sqlite3_auto_extension() callback, invoked to register
+** the vfsstat virtual table for all new database connections.
+*/
+static int vstatRegister(
+  sqlite3 *db,
+  const char **pzErrMsg,
+  const struct sqlite3_api_routines *pThunk
+){
+  return sqlite3_create_module(db, "vfsstat", &VfsStatModule, 0);
+}
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+/* 
+** This routine is called when the extension is loaded.
+**
+** Register the new VFS.  Make arrangement to register the virtual table
+** for each new database connection.
+*/
+int sqlite3_vfsstat_init(
+  sqlite3 *db, 
+  char **pzErrMsg, 
+  const sqlite3_api_routines *pApi
+){
+  int rc = SQLITE_OK;
+  VStatVfs *pNew;
+  SQLITE_EXTENSION_INIT2(pApi);
+  pNew = sqlite3_malloc(sizeof(vstat_vfs));
+  if( pNew==0 ) return SQLITE_NOMEM;
+  memcpy(&pNew->base, &vstat_vfs, sizeof(vstat_vfs));
+  pNew->pVfs = sqlite3_vfs_find(0);
+  pNew->base.szOsFile = sizeof(VStatFile) + pNew->pVfs->szOsFile;
+  rc = sqlite3_vfs_register(&pNew->base, 1);
+  if( rc==SQLITE_OK ){
+    rc = sqlite3_auto_extension((void(*)(void))vstatRegister);
+  }
+  return rc;
+}
index 8c4974db294333a820ac632958d9ef6862c66955..98076dc0c34cb029091a4d3c286929af99afebcc 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Improvements\sto\sWHERE-clause\sdebug\stracing.\s\sShow\sTK_MATCH\sexpressions\sand\nshow\smore\sdetails\son\sWhereTerm\straces.
-D 2016-05-27T12:30:20.162
+C Add\sthe\svfsstat.c\sloadable\sextension\s-\sa\sVFS\sshim\sthat\smeasures\sthe\samount\nof\sI/O,\sand\san\seponymous\svirtual\stable\sthat\sis\sused\sto\sextract\sand\sview\nthe\smeasurements.
+D 2016-05-28T14:53:48.676
 F Makefile.in f59e0763ff448719fc1bd25513882b0567286317
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc 306d73e854b1a92ea06e5d1e637faa5c44de53c7
@@ -221,6 +221,7 @@ F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
 F ext/misc/spellfix.c bf1b922c2750698e9a3d4c50cce6974adb7e93be
 F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
 F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
+F ext/misc/vfsstat.c 318bb69270291bfff3d5bb58b46397ed7b507589
 F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
 F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
 F ext/rbu/rbu.c b2c0b5e6ae1a89affc0edfc127ebfa5f637a0ce4
@@ -1495,7 +1496,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P a9be4c2d56d08fea2cd1aab20b19092a45ef7620
-R 2beba98fee0a4944905820065cc75fbf
+P 71087c12bc75a82f5d1051600a442ef6efc5e899
+R 3d452eed48a36d85ceafabad1e13a1b0
 U drh
-Z 8d7a3e9d02c8c2b09e27f05110ad65d3
+Z 226f41fb99617919dc67fcf97f9a5431
index 2f2802b83d528eff2f95cf8bac1a275e286b5dc2..4270f95914dab8ff6f73a2f3043d4d938d4f1d81 100644 (file)
@@ -1 +1 @@
-71087c12bc75a82f5d1051600a442ef6efc5e899
\ No newline at end of file
+0987487dd4ebfcf66ddeec8ceca47775216a0887
\ No newline at end of file