/ Check-in [0987487d]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add the vfsstat.c loadable extension - a VFS shim that measures the amount of I/O, and an eponymous virtual table that is used to extract and view the measurements.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 0987487dd4ebfcf66ddeec8ceca47775216a0887
User & Date: drh 2016-05-28 14:53:48
Context
2016-05-28
15:09
Update the amalgamation-tarball configure script so that it can use header file "readline/readline.h" with library file "libedit". check-in: cbf72b04 user: dan tags: trunk
15:03
Enhance the sqlite3_load_extension() API so that the first parameter (the "db" parameter) can be NULL. An extension that is not associated with any database connection remains loaded for the life of the process. Closed-Leaf check-in: 3a461043 user: drh tags: persistent-extensions
14:53
Add the vfsstat.c loadable extension - a VFS shim that measures the amount of I/O, and an eponymous virtual table that is used to extract and view the measurements. check-in: 0987487d user: drh tags: trunk
2016-05-27
12:30
Improvements to WHERE-clause debug tracing. Show TK_MATCH expressions and show more details on WhereTerm traces. check-in: 71087c12 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Added ext/misc/vfsstat.c.

            1  +/*
            2  +** 2016-05-27
            3  +**
            4  +** The author disclaims copyright to this source code.  In place of
            5  +** a legal notice, here is a blessing:
            6  +**
            7  +**    May you do good and not evil.
            8  +**    May you find forgiveness for yourself and forgive others.
            9  +**    May you share freely, never taking more than you give.
           10  +**
           11  +******************************************************************************
           12  +**
           13  +** This file contains the implementation of an SQLite vfs shim that
           14  +** tracks I/O.  Access to the accumulated status counts is provided using
           15  +** an eponymous virtual table.
           16  +*/
           17  +#include <sqlite3ext.h>
           18  +SQLITE_EXTENSION_INIT1
           19  +
           20  +/*
           21  +** This module contains code for a wrapper VFS that cause stats for
           22  +** most VFS calls to be recorded.
           23  +**
           24  +** To use this module, first compile it as a loadable extension.  See
           25  +** https://www.sqlite.org/loadext.html#build for compilations instructions.
           26  +**
           27  +** After compliing, load this extension, then open database connections to be
           28  +** measured.  Query usages status using the vfsstat virtual table:
           29  +**
           30  +**         SELECT * FROM vfsstat;
           31  +**
           32  +** Reset counters using UPDATE statements against vfsstat:
           33  +**
           34  +**         UPDATE vfsstat SET count=0;
           35  +**
           36  +** EXAMPLE SCRIPT:
           37  +**
           38  +**      .load ./vfsstat
           39  +**      .open test.db
           40  +**      DROP TABLE IF EXISTS t1;
           41  +**      CREATE TABLE t1(x,y);
           42  +**      INSERT INTO t1 VALUES(123, randomblob(5000));
           43  +**      CREATE INDEX t1x ON t1(x);
           44  +**      DROP TABLE t1;
           45  +**      VACUUM;
           46  +**      SELECT * FROM vfsstat WHERE count>0;
           47  +**
           48  +** LIMITATIONS:
           49  +** 
           50  +** This module increments counters without using mutex protection.  So if
           51  +** two or more threads try to use this module at the same time, race conditions
           52  +** may occur which mess up the counts.  This is harmless, other than giving
           53  +** incorrect statistics.
           54  +*/
           55  +#include <string.h>
           56  +#include <stdlib.h>
           57  +#include <assert.h>
           58  +
           59  +/*
           60  +** File types
           61  +*/
           62  +#define VFSSTAT_MAIN         0   /* Main database file */
           63  +#define VFSSTAT_JOURNAL      1   /* Rollback journal */
           64  +#define VFSSTAT_WAL          2   /* Write-ahead log file */
           65  +#define VFSSTAT_MASTERJRNL   3   /* Master journal */
           66  +#define VFSSTAT_SUBJRNL      4   /* Subjournal */
           67  +#define VFSSTAT_TEMPDB       5   /* TEMP database */
           68  +#define VFSSTAT_TEMPJRNL     6   /* Journal for TEMP database */
           69  +#define VFSSTAT_TRANSIENT    7   /* Transient database */
           70  +#define VFSSTAT_ANY          8   /* Unspecified file type */
           71  +#define VFSSTAT_nFile        9   /* This many file types */
           72  +
           73  +/* Names of the file types.  These are allowed values for the
           74  +** first column of the vfsstat virtual table.
           75  +*/
           76  +static const char *azFile[] = {
           77  +  "database", "journal", "wal", "master-journal", "sub-journal",
           78  +  "temp-database", "temp-journal", "transient-db", "*"
           79  +};
           80  +
           81  +/*
           82  +** Stat types
           83  +*/
           84  +#define VFSSTAT_BYTESIN      0   /* Bytes read in */
           85  +#define VFSSTAT_BYTESOUT     1   /* Bytes written out */   
           86  +#define VFSSTAT_READ         2   /* Read requests */
           87  +#define VFSSTAT_WRITE        3   /* Write requests */
           88  +#define VFSSTAT_SYNC         4   /* Syncs */
           89  +#define VFSSTAT_OPEN         5   /* File opens */
           90  +#define VFSSTAT_LOCK         6   /* Lock requests */
           91  +#define VFSSTAT_ACCESS       0   /* xAccess calls.  filetype==ANY only */
           92  +#define VFSSTAT_DELETE       1   /* xDelete calls.  filetype==ANY only */
           93  +#define VFSSTAT_FULLPATH     2   /* xFullPathname calls.  ANY only */
           94  +#define VFSSTAT_RANDOM       3   /* xRandomness calls.    ANY only */
           95  +#define VFSSTAT_SLEEP        4   /* xSleep calls.         ANY only */
           96  +#define VFSSTAT_CURTIME      5   /* xCurrentTime calls.   ANY only */
           97  +#define VFSSTAT_nStat        7   /* This many stat types */
           98  +
           99  +
          100  +/* Names for the second column of the vfsstat virtual table for all
          101  +** cases except when the first column is "*" or VFSSTAT_ANY. */
          102  +static const char *azStat[] = {
          103  +  "bytes-in", "bytes-out", "read", "write", "sync", "open", "lock",
          104  +};
          105  +static const char *azStatAny[] = {
          106  +  "access", "delete", "fullpathname", "randomness", "sleep", "currenttimestamp",
          107  +  "not-used"
          108  +};
          109  +
          110  +/* Total number of counters */
          111  +#define VFSSTAT_MXCNT  (VFSSTAT_nStat*VFSSTAT_nFile)
          112  +
          113  +/*
          114  +** Performance stats are collected in an instance of the following
          115  +** global array.
          116  +*/
          117  +static sqlite3_uint64 aVfsCnt[VFSSTAT_MXCNT];
          118  +
          119  +/*
          120  +** Access to a specific counter
          121  +*/
          122  +#define STATCNT(filetype,stat) (aVfsCnt[(filetype)*VFSSTAT_nStat+(stat)])
          123  +
          124  +/*
          125  +** Forward declaration of objects used by this utility
          126  +*/
          127  +typedef struct VStatVfs VStatVfs;
          128  +typedef struct VStatFile VStatFile;
          129  +
          130  +/* An instance of the VFS */
          131  +struct VStatVfs {
          132  +  sqlite3_vfs base;               /* VFS methods */
          133  +  sqlite3_vfs *pVfs;              /* Parent VFS */
          134  +};
          135  +
          136  +/* An open file */
          137  +struct VStatFile {
          138  +  sqlite3_file base;              /* IO methods */
          139  +  sqlite3_file *pReal;            /* Underlying file handle */
          140  +  unsigned char eFiletype;        /* What type of file is this */
          141  +};
          142  +
          143  +#define REALVFS(p) (((VStatVfs*)(p))->pVfs)
          144  +
          145  +/*
          146  +** Methods for VStatFile
          147  +*/
          148  +static int vstatClose(sqlite3_file*);
          149  +static int vstatRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
          150  +static int vstatWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
          151  +static int vstatTruncate(sqlite3_file*, sqlite3_int64 size);
          152  +static int vstatSync(sqlite3_file*, int flags);
          153  +static int vstatFileSize(sqlite3_file*, sqlite3_int64 *pSize);
          154  +static int vstatLock(sqlite3_file*, int);
          155  +static int vstatUnlock(sqlite3_file*, int);
          156  +static int vstatCheckReservedLock(sqlite3_file*, int *pResOut);
          157  +static int vstatFileControl(sqlite3_file*, int op, void *pArg);
          158  +static int vstatSectorSize(sqlite3_file*);
          159  +static int vstatDeviceCharacteristics(sqlite3_file*);
          160  +static int vstatShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
          161  +static int vstatShmLock(sqlite3_file*, int offset, int n, int flags);
          162  +static void vstatShmBarrier(sqlite3_file*);
          163  +static int vstatShmUnmap(sqlite3_file*, int deleteFlag);
          164  +static int vstatFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
          165  +static int vstatUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
          166  +
          167  +/*
          168  +** Methods for VStatVfs
          169  +*/
          170  +static int vstatOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
          171  +static int vstatDelete(sqlite3_vfs*, const char *zName, int syncDir);
          172  +static int vstatAccess(sqlite3_vfs*, const char *zName, int flags, int *);
          173  +static int vstatFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
          174  +static void *vstatDlOpen(sqlite3_vfs*, const char *zFilename);
          175  +static void vstatDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
          176  +static void (*vstatDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
          177  +static void vstatDlClose(sqlite3_vfs*, void*);
          178  +static int vstatRandomness(sqlite3_vfs*, int nByte, char *zOut);
          179  +static int vstatSleep(sqlite3_vfs*, int microseconds);
          180  +static int vstatCurrentTime(sqlite3_vfs*, double*);
          181  +static int vstatGetLastError(sqlite3_vfs*, int, char *);
          182  +static int vstatCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
          183  +
          184  +static VStatVfs vstat_vfs = {
          185  +  {
          186  +    2,                            /* iVersion */
          187  +    0,                            /* szOsFile (set by register_vstat()) */
          188  +    1024,                         /* mxPathname */
          189  +    0,                            /* pNext */
          190  +    "vfslog",                     /* zName */
          191  +    0,                            /* pAppData */
          192  +    vstatOpen,                     /* xOpen */
          193  +    vstatDelete,                   /* xDelete */
          194  +    vstatAccess,                   /* xAccess */
          195  +    vstatFullPathname,             /* xFullPathname */
          196  +    vstatDlOpen,                   /* xDlOpen */
          197  +    vstatDlError,                  /* xDlError */
          198  +    vstatDlSym,                    /* xDlSym */
          199  +    vstatDlClose,                  /* xDlClose */
          200  +    vstatRandomness,               /* xRandomness */
          201  +    vstatSleep,                    /* xSleep */
          202  +    vstatCurrentTime,              /* xCurrentTime */
          203  +    vstatGetLastError,             /* xGetLastError */
          204  +    vstatCurrentTimeInt64          /* xCurrentTimeInt64 */
          205  +  },
          206  +  0
          207  +};
          208  +
          209  +static const sqlite3_io_methods vstat_io_methods = {
          210  +  3,                              /* iVersion */
          211  +  vstatClose,                      /* xClose */
          212  +  vstatRead,                       /* xRead */
          213  +  vstatWrite,                      /* xWrite */
          214  +  vstatTruncate,                   /* xTruncate */
          215  +  vstatSync,                       /* xSync */
          216  +  vstatFileSize,                   /* xFileSize */
          217  +  vstatLock,                       /* xLock */
          218  +  vstatUnlock,                     /* xUnlock */
          219  +  vstatCheckReservedLock,          /* xCheckReservedLock */
          220  +  vstatFileControl,                /* xFileControl */
          221  +  vstatSectorSize,                 /* xSectorSize */
          222  +  vstatDeviceCharacteristics,      /* xDeviceCharacteristics */
          223  +  vstatShmMap,                     /* xShmMap */
          224  +  vstatShmLock,                    /* xShmLock */
          225  +  vstatShmBarrier,                 /* xShmBarrier */
          226  +  vstatShmUnmap,                   /* xShmUnmap */
          227  +  vstatFetch,                      /* xFetch */
          228  +  vstatUnfetch                     /* xUnfetch */
          229  +};
          230  +
          231  +
          232  +
          233  +/*
          234  +** Close an vstat-file.
          235  +*/
          236  +static int vstatClose(sqlite3_file *pFile){
          237  +  VStatFile *p = (VStatFile *)pFile;
          238  +  int rc = SQLITE_OK;
          239  +
          240  +  if( p->pReal->pMethods ){
          241  +    rc = p->pReal->pMethods->xClose(p->pReal);
          242  +  }
          243  +  return rc;
          244  +}
          245  +
          246  +
          247  +/*
          248  +** Read data from an vstat-file.
          249  +*/
          250  +static int vstatRead(
          251  +  sqlite3_file *pFile, 
          252  +  void *zBuf, 
          253  +  int iAmt, 
          254  +  sqlite_int64 iOfst
          255  +){
          256  +  int rc;
          257  +  VStatFile *p = (VStatFile *)pFile;
          258  +
          259  +  rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
          260  +  STATCNT(p->eFiletype,VFSSTAT_READ)++;
          261  +  if( rc==SQLITE_OK ){
          262  +    STATCNT(p->eFiletype,VFSSTAT_BYTESIN) += iAmt;
          263  +  }
          264  +  return rc;
          265  +}
          266  +
          267  +/*
          268  +** Write data to an vstat-file.
          269  +*/
          270  +static int vstatWrite(
          271  +  sqlite3_file *pFile,
          272  +  const void *z,
          273  +  int iAmt,
          274  +  sqlite_int64 iOfst
          275  +){
          276  +  int rc;
          277  +  VStatFile *p = (VStatFile *)pFile;
          278  +
          279  +  rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst);
          280  +  STATCNT(p->eFiletype,VFSSTAT_WRITE)++;
          281  +  if( rc==SQLITE_OK ){
          282  +    STATCNT(p->eFiletype,VFSSTAT_BYTESOUT) += iAmt;
          283  +  }
          284  +  return rc;
          285  +}
          286  +
          287  +/*
          288  +** Truncate an vstat-file.
          289  +*/
          290  +static int vstatTruncate(sqlite3_file *pFile, sqlite_int64 size){
          291  +  int rc;
          292  +  VStatFile *p = (VStatFile *)pFile;
          293  +  rc = p->pReal->pMethods->xTruncate(p->pReal, size);
          294  +  return rc;
          295  +}
          296  +
          297  +/*
          298  +** Sync an vstat-file.
          299  +*/
          300  +static int vstatSync(sqlite3_file *pFile, int flags){
          301  +  int rc;
          302  +  VStatFile *p = (VStatFile *)pFile;
          303  +  rc = p->pReal->pMethods->xSync(p->pReal, flags);
          304  +  STATCNT(p->eFiletype,VFSSTAT_SYNC)++;
          305  +  return rc;
          306  +}
          307  +
          308  +/*
          309  +** Return the current file-size of an vstat-file.
          310  +*/
          311  +static int vstatFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
          312  +  int rc;
          313  +  VStatFile *p = (VStatFile *)pFile;
          314  +  rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
          315  +  return rc;
          316  +}
          317  +
          318  +/*
          319  +** Lock an vstat-file.
          320  +*/
          321  +static int vstatLock(sqlite3_file *pFile, int eLock){
          322  +  int rc;
          323  +  VStatFile *p = (VStatFile *)pFile;
          324  +  rc = p->pReal->pMethods->xLock(p->pReal, eLock);
          325  +  STATCNT(p->eFiletype,VFSSTAT_LOCK)++;
          326  +  return rc;
          327  +}
          328  +
          329  +/*
          330  +** Unlock an vstat-file.
          331  +*/
          332  +static int vstatUnlock(sqlite3_file *pFile, int eLock){
          333  +  int rc;
          334  +  VStatFile *p = (VStatFile *)pFile;
          335  +  rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
          336  +  STATCNT(p->eFiletype,VFSSTAT_LOCK)++;
          337  +  return rc;
          338  +}
          339  +
          340  +/*
          341  +** Check if another file-handle holds a RESERVED lock on an vstat-file.
          342  +*/
          343  +static int vstatCheckReservedLock(sqlite3_file *pFile, int *pResOut){
          344  +  int rc;
          345  +  VStatFile *p = (VStatFile *)pFile;
          346  +  rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
          347  +  STATCNT(p->eFiletype,VFSSTAT_LOCK)++;
          348  +  return rc;
          349  +}
          350  +
          351  +/*
          352  +** File control method. For custom operations on an vstat-file.
          353  +*/
          354  +static int vstatFileControl(sqlite3_file *pFile, int op, void *pArg){
          355  +  VStatFile *p = (VStatFile *)pFile;
          356  +  int rc;
          357  +  rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
          358  +  if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
          359  +    *(char**)pArg = sqlite3_mprintf("vstat/%z", *(char**)pArg);
          360  +  }
          361  +  return rc;
          362  +}
          363  +
          364  +/*
          365  +** Return the sector-size in bytes for an vstat-file.
          366  +*/
          367  +static int vstatSectorSize(sqlite3_file *pFile){
          368  +  int rc;
          369  +  VStatFile *p = (VStatFile *)pFile;
          370  +  rc = p->pReal->pMethods->xSectorSize(p->pReal);
          371  +  return rc;
          372  +}
          373  +
          374  +/*
          375  +** Return the device characteristic flags supported by an vstat-file.
          376  +*/
          377  +static int vstatDeviceCharacteristics(sqlite3_file *pFile){
          378  +  int rc;
          379  +  VStatFile *p = (VStatFile *)pFile;
          380  +  rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
          381  +  return rc;
          382  +}
          383  +
          384  +/* Create a shared memory file mapping */
          385  +static int vstatShmMap(
          386  +  sqlite3_file *pFile,
          387  +  int iPg,
          388  +  int pgsz,
          389  +  int bExtend,
          390  +  void volatile **pp
          391  +){
          392  +  VStatFile *p = (VStatFile *)pFile;
          393  +  return p->pReal->pMethods->xShmMap(p->pReal, iPg, pgsz, bExtend, pp);
          394  +}
          395  +
          396  +/* Perform locking on a shared-memory segment */
          397  +static int vstatShmLock(sqlite3_file *pFile, int offset, int n, int flags){
          398  +  VStatFile *p = (VStatFile *)pFile;
          399  +  return p->pReal->pMethods->xShmLock(p->pReal, offset, n, flags);
          400  +}
          401  +
          402  +/* Memory barrier operation on shared memory */
          403  +static void vstatShmBarrier(sqlite3_file *pFile){
          404  +  VStatFile *p = (VStatFile *)pFile;
          405  +  return p->pReal->pMethods->xShmBarrier(p->pReal);
          406  +}
          407  +
          408  +/* Unmap a shared memory segment */
          409  +static int vstatShmUnmap(sqlite3_file *pFile, int deleteFlag){
          410  +  VStatFile *p = (VStatFile *)pFile;
          411  +  return p->pReal->pMethods->xShmUnmap(p->pReal, deleteFlag);
          412  +}
          413  +
          414  +/* Fetch a page of a memory-mapped file */
          415  +static int vstatFetch(
          416  +  sqlite3_file *pFile,
          417  +  sqlite3_int64 iOfst,
          418  +  int iAmt,
          419  +  void **pp
          420  +){
          421  +  VStatFile *p = (VStatFile *)pFile;
          422  +  return p->pReal->pMethods->xFetch(p->pReal, iOfst, iAmt, pp);
          423  +}
          424  +
          425  +/* Release a memory-mapped page */
          426  +static int vstatUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
          427  +  VStatFile *p = (VStatFile *)pFile;
          428  +  return p->pReal->pMethods->xUnfetch(p->pReal, iOfst, pPage);
          429  +}
          430  +
          431  +/*
          432  +** Open an vstat file handle.
          433  +*/
          434  +static int vstatOpen(
          435  +  sqlite3_vfs *pVfs,
          436  +  const char *zName,
          437  +  sqlite3_file *pFile,
          438  +  int flags,
          439  +  int *pOutFlags
          440  +){
          441  +  int rc;
          442  +  VStatFile *p = (VStatFile*)pFile;
          443  +
          444  +  p->pReal = (sqlite3_file*)&p[1];
          445  +  rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags);
          446  +  if( flags & SQLITE_OPEN_MAIN_DB ){
          447  +    p->eFiletype = VFSSTAT_MAIN;
          448  +  }else if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
          449  +    p->eFiletype = VFSSTAT_JOURNAL;
          450  +  }else if( flags & SQLITE_OPEN_WAL ){
          451  +    p->eFiletype = VFSSTAT_WAL;
          452  +  }else if( flags & SQLITE_OPEN_MASTER_JOURNAL ){
          453  +    p->eFiletype = VFSSTAT_MASTERJRNL;
          454  +  }else if( flags & SQLITE_OPEN_SUBJOURNAL ){
          455  +    p->eFiletype = VFSSTAT_SUBJRNL;
          456  +  }else if( flags & SQLITE_OPEN_TEMP_DB ){
          457  +    p->eFiletype = VFSSTAT_TEMPDB;
          458  +  }else if( flags & SQLITE_OPEN_TEMP_JOURNAL ){
          459  +    p->eFiletype = VFSSTAT_TEMPJRNL;
          460  +  }else{
          461  +    p->eFiletype = VFSSTAT_TRANSIENT;
          462  +  }
          463  +  STATCNT(p->eFiletype,VFSSTAT_OPEN)++;
          464  +  pFile->pMethods = rc ? 0 : &vstat_io_methods;
          465  +  return rc;
          466  +}
          467  +
          468  +/*
          469  +** Delete the file located at zPath. If the dirSync argument is true,
          470  +** ensure the file-system modifications are synced to disk before
          471  +** returning.
          472  +*/
          473  +static int vstatDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
          474  +  int rc;
          475  +  rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync);
          476  +  STATCNT(VFSSTAT_ANY,VFSSTAT_DELETE)++;
          477  +  return rc;
          478  +}
          479  +
          480  +/*
          481  +** Test for access permissions. Return true if the requested permission
          482  +** is available, or false otherwise.
          483  +*/
          484  +static int vstatAccess(
          485  +  sqlite3_vfs *pVfs, 
          486  +  const char *zPath, 
          487  +  int flags, 
          488  +  int *pResOut
          489  +){
          490  +  int rc;
          491  +  rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut);
          492  +  STATCNT(VFSSTAT_ANY,VFSSTAT_ACCESS)++;
          493  +  return rc;
          494  +}
          495  +
          496  +/*
          497  +** Populate buffer zOut with the full canonical pathname corresponding
          498  +** to the pathname in zPath. zOut is guaranteed to point to a buffer
          499  +** of at least (INST_MAX_PATHNAME+1) bytes.
          500  +*/
          501  +static int vstatFullPathname(
          502  +  sqlite3_vfs *pVfs, 
          503  +  const char *zPath, 
          504  +  int nOut, 
          505  +  char *zOut
          506  +){
          507  +  STATCNT(VFSSTAT_ANY,VFSSTAT_FULLPATH)++;
          508  +  return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
          509  +}
          510  +
          511  +/*
          512  +** Open the dynamic library located at zPath and return a handle.
          513  +*/
          514  +static void *vstatDlOpen(sqlite3_vfs *pVfs, const char *zPath){
          515  +  return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath);
          516  +}
          517  +
          518  +/*
          519  +** Populate the buffer zErrMsg (size nByte bytes) with a human readable
          520  +** utf-8 string describing the most recent error encountered associated 
          521  +** with dynamic libraries.
          522  +*/
          523  +static void vstatDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
          524  +  REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg);
          525  +}
          526  +
          527  +/*
          528  +** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
          529  +*/
          530  +static void (*vstatDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
          531  +  return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym);
          532  +}
          533  +
          534  +/*
          535  +** Close the dynamic library handle pHandle.
          536  +*/
          537  +static void vstatDlClose(sqlite3_vfs *pVfs, void *pHandle){
          538  +  REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle);
          539  +}
          540  +
          541  +/*
          542  +** Populate the buffer pointed to by zBufOut with nByte bytes of 
          543  +** random data.
          544  +*/
          545  +static int vstatRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
          546  +  STATCNT(VFSSTAT_ANY,VFSSTAT_RANDOM)++;
          547  +  return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
          548  +}
          549  +
          550  +/*
          551  +** Sleep for nMicro microseconds. Return the number of microseconds 
          552  +** actually slept.
          553  +*/
          554  +static int vstatSleep(sqlite3_vfs *pVfs, int nMicro){
          555  +  STATCNT(VFSSTAT_ANY,VFSSTAT_SLEEP)++;
          556  +  return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro);
          557  +}
          558  +
          559  +/*
          560  +** Return the current time as a Julian Day number in *pTimeOut.
          561  +*/
          562  +static int vstatCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
          563  +  STATCNT(VFSSTAT_ANY,VFSSTAT_CURTIME)++;
          564  +  return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut);
          565  +}
          566  +
          567  +static int vstatGetLastError(sqlite3_vfs *pVfs, int a, char *b){
          568  +  return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b);
          569  +}
          570  +static int vstatCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
          571  +  STATCNT(VFSSTAT_ANY,VFSSTAT_CURTIME)++;
          572  +  return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p);
          573  +}
          574  +
          575  +/*
          576  +** A virtual table for accessing the stats collected by this VFS shim
          577  +*/
          578  +static int vstattabConnect(sqlite3*, void*, int, const char*const*, 
          579  +                           sqlite3_vtab**,char**);
          580  +static int vstattabBestIndex(sqlite3_vtab*,sqlite3_index_info*);
          581  +static int vstattabDisconnect(sqlite3_vtab*);
          582  +static int vstattabOpen(sqlite3_vtab*, sqlite3_vtab_cursor**);
          583  +static int vstattabClose(sqlite3_vtab_cursor*);
          584  +static int vstattabFilter(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
          585  +                          int argc, sqlite3_value **argv);
          586  +static int vstattabNext(sqlite3_vtab_cursor*);
          587  +static int vstattabEof(sqlite3_vtab_cursor*);
          588  +static int vstattabColumn(sqlite3_vtab_cursor*,sqlite3_context*,int);
          589  +static int vstattabRowid(sqlite3_vtab_cursor*,sqlite3_int64*);
          590  +static int vstattabUpdate(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*);
          591  +
          592  +/* A cursor for the vfsstat virtual table */
          593  +typedef struct VfsStatCursor {
          594  +  sqlite3_vtab_cursor base;       /* Base class.  Must be first */
          595  +  int i;                          /* Pointing to this aVfsCnt[] value */
          596  +} VfsStatCursor;
          597  +
          598  +
          599  +static int vstattabConnect(
          600  +  sqlite3 *db,
          601  +  void *pAux,
          602  +  int argc, const char *const*argv,
          603  +  sqlite3_vtab **ppVtab,
          604  +  char **pzErr
          605  +){
          606  +  sqlite3_vtab *pNew;
          607  +  int rc;
          608  +
          609  +/* Column numbers */
          610  +#define VSTAT_COLUMN_FILE  0 
          611  +#define VSTAT_COLUMN_STAT  1
          612  +#define VSTAT_COLUMN_COUNT 2
          613  +
          614  +  rc = sqlite3_declare_vtab(db,"CREATE TABLE x(file,stat,count)");
          615  +  if( rc==SQLITE_OK ){
          616  +    pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
          617  +    if( pNew==0 ) return SQLITE_NOMEM;
          618  +    memset(pNew, 0, sizeof(*pNew));
          619  +  }
          620  +  return rc;
          621  +}
          622  +
          623  +/*
          624  +** This method is the destructor for vstat table object.
          625  +*/
          626  +static int vstattabDisconnect(sqlite3_vtab *pVtab){
          627  +  sqlite3_free(pVtab);
          628  +  return SQLITE_OK;
          629  +}
          630  +
          631  +/*
          632  +** Constructor for a new vstat table cursor object.
          633  +*/
          634  +static int vstattabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
          635  +  VfsStatCursor *pCur;
          636  +  pCur = sqlite3_malloc( sizeof(*pCur) );
          637  +  if( pCur==0 ) return SQLITE_NOMEM;
          638  +  memset(pCur, 0, sizeof(*pCur));
          639  +  *ppCursor = &pCur->base;
          640  +  return SQLITE_OK;
          641  +}
          642  +
          643  +
          644  +/*
          645  +** Destructor for a VfsStatCursor.
          646  +*/
          647  +static int vstattabClose(sqlite3_vtab_cursor *cur){
          648  +  sqlite3_free(cur);
          649  +  return SQLITE_OK;
          650  +}
          651  +
          652  +
          653  +/*
          654  +** Advance a VfsStatCursor to its next row of output.
          655  +*/
          656  +static int vstattabNext(sqlite3_vtab_cursor *cur){
          657  +  ((VfsStatCursor*)cur)->i++;
          658  +  return SQLITE_OK;
          659  +}
          660  +
          661  +/*
          662  +** Return values of columns for the row at which the VfsStatCursor
          663  +** is currently pointing.
          664  +*/
          665  +static int vstattabColumn(
          666  +  sqlite3_vtab_cursor *cur,   /* The cursor */
          667  +  sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
          668  +  int i                       /* Which column to return */
          669  +){
          670  +  VfsStatCursor *pCur = (VfsStatCursor*)cur;
          671  +  switch( i ){
          672  +    case VSTAT_COLUMN_FILE: {
          673  +      sqlite3_result_text(ctx, azFile[pCur->i/VFSSTAT_nStat], -1, SQLITE_STATIC);
          674  +      break;
          675  +    }
          676  +    case VSTAT_COLUMN_STAT: {
          677  +      const char **az;
          678  +      az = (pCur->i/VFSSTAT_nStat)==VFSSTAT_ANY ? azStatAny : azStat;
          679  +      sqlite3_result_text(ctx, az[pCur->i%VFSSTAT_nStat], -1, SQLITE_STATIC);
          680  +      break;
          681  +    }
          682  +    case VSTAT_COLUMN_COUNT: {
          683  +      sqlite3_result_int64(ctx, aVfsCnt[pCur->i]);
          684  +      break;
          685  +    }
          686  +  }
          687  +  return SQLITE_OK;
          688  +}
          689  +
          690  +/*
          691  +** Return the rowid for the current row.
          692  +*/
          693  +static int vstattabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
          694  +  VfsStatCursor *pCur = (VfsStatCursor*)cur;
          695  +  *pRowid = pCur->i;
          696  +  return SQLITE_OK;
          697  +}
          698  +
          699  +/*
          700  +** Return TRUE if the cursor has been moved off of the last
          701  +** row of output.
          702  +*/
          703  +static int vstattabEof(sqlite3_vtab_cursor *cur){
          704  +  VfsStatCursor *pCur = (VfsStatCursor*)cur;
          705  +  return pCur->i >= VFSSTAT_MXCNT;
          706  +}
          707  +
          708  +/*
          709  +** Only a full table scan is supported.  So xFilter simply rewinds to
          710  +** the beginning.
          711  +*/
          712  +static int vstattabFilter(
          713  +  sqlite3_vtab_cursor *pVtabCursor, 
          714  +  int idxNum, const char *idxStr,
          715  +  int argc, sqlite3_value **argv
          716  +){
          717  +  VfsStatCursor *pCur = (VfsStatCursor*)pVtabCursor;
          718  +  pCur->i = 0;
          719  +  return SQLITE_OK;
          720  +}
          721  +
          722  +/*
          723  +** Only a forwards full table scan is supported.  xBestIndex is a no-op.
          724  +*/
          725  +static int vstattabBestIndex(
          726  +  sqlite3_vtab *tab,
          727  +  sqlite3_index_info *pIdxInfo
          728  +){
          729  +  return SQLITE_OK;
          730  +}
          731  +
          732  +/*
          733  +** Any VSTAT_COLUMN_COUNT can be changed to a positive integer.
          734  +** No deletions or insertions are allowed.  No changes to other
          735  +** columns are allowed.
          736  +*/
          737  +static int vstattabUpdate(
          738  +  sqlite3_vtab *tab,
          739  +  int argc, sqlite3_value **argv,
          740  +  sqlite3_int64 *pRowid
          741  +){
          742  +  sqlite3_int64 iRowid, x;
          743  +  if( argc==1 ) return SQLITE_ERROR;
          744  +  if( sqlite3_value_type(argv[0])!=SQLITE_INTEGER ) return SQLITE_ERROR;
          745  +  iRowid = sqlite3_value_int64(argv[0]);
          746  +  if( iRowid!=sqlite3_value_int64(argv[1]) ) return SQLITE_ERROR;
          747  +  if( iRowid<0 || iRowid>=VFSSTAT_MXCNT ) return SQLITE_ERROR;
          748  +  if( sqlite3_value_type(argv[VSTAT_COLUMN_COUNT+2])!=SQLITE_INTEGER ){
          749  +    return SQLITE_ERROR;
          750  +  }
          751  +  x = sqlite3_value_int64(argv[VSTAT_COLUMN_COUNT+2]);
          752  +  if( x<0 ) return SQLITE_ERROR;
          753  +  aVfsCnt[iRowid] = x;
          754  +  return SQLITE_OK;
          755  +}
          756  +
          757  +static sqlite3_module VfsStatModule = {
          758  +  0,                         /* iVersion */
          759  +  0,                         /* xCreate */
          760  +  vstattabConnect,           /* xConnect */
          761  +  vstattabBestIndex,         /* xBestIndex */
          762  +  vstattabDisconnect,        /* xDisconnect */
          763  +  0,                         /* xDestroy */
          764  +  vstattabOpen,              /* xOpen - open a cursor */
          765  +  vstattabClose,             /* xClose - close a cursor */
          766  +  vstattabFilter,            /* xFilter - configure scan constraints */
          767  +  vstattabNext,              /* xNext - advance a cursor */
          768  +  vstattabEof,               /* xEof - check for end of scan */
          769  +  vstattabColumn,            /* xColumn - read data */
          770  +  vstattabRowid,             /* xRowid - read data */
          771  +  vstattabUpdate,            /* xUpdate */
          772  +  0,                         /* xBegin */
          773  +  0,                         /* xSync */
          774  +  0,                         /* xCommit */
          775  +  0,                         /* xRollback */
          776  +  0,                         /* xFindMethod */
          777  +  0,                         /* xRename */
          778  +};
          779  +
          780  +/*
          781  +** This routine is an sqlite3_auto_extension() callback, invoked to register
          782  +** the vfsstat virtual table for all new database connections.
          783  +*/
          784  +static int vstatRegister(
          785  +  sqlite3 *db,
          786  +  const char **pzErrMsg,
          787  +  const struct sqlite3_api_routines *pThunk
          788  +){
          789  +  return sqlite3_create_module(db, "vfsstat", &VfsStatModule, 0);
          790  +}
          791  +
          792  +#ifdef _WIN32
          793  +__declspec(dllexport)
          794  +#endif
          795  +/* 
          796  +** This routine is called when the extension is loaded.
          797  +**
          798  +** Register the new VFS.  Make arrangement to register the virtual table
          799  +** for each new database connection.
          800  +*/
          801  +int sqlite3_vfsstat_init(
          802  +  sqlite3 *db, 
          803  +  char **pzErrMsg, 
          804  +  const sqlite3_api_routines *pApi
          805  +){
          806  +  int rc = SQLITE_OK;
          807  +  VStatVfs *pNew;
          808  +  SQLITE_EXTENSION_INIT2(pApi);
          809  +  pNew = sqlite3_malloc(sizeof(vstat_vfs));
          810  +  if( pNew==0 ) return SQLITE_NOMEM;
          811  +  memcpy(&pNew->base, &vstat_vfs, sizeof(vstat_vfs));
          812  +  pNew->pVfs = sqlite3_vfs_find(0);
          813  +  pNew->base.szOsFile = sizeof(VStatFile) + pNew->pVfs->szOsFile;
          814  +  rc = sqlite3_vfs_register(&pNew->base, 1);
          815  +  if( rc==SQLITE_OK ){
          816  +    rc = sqlite3_auto_extension((void(*)(void))vstatRegister);
          817  +  }
          818  +  return rc;
          819  +}