SQLITE_NOTICE(283): recovered 1 frames from WAL file /fossil/sqlite.fossil-wal

SQLite: Check-in [9e6efcf0]
/ Check-in [9e6efcf0]
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:Initial check-in of a test VFS designed to simulate a NAND-flash SSD for the purpose of measuring and subsequently minimizing write amplification caused by SQLite. The code in this check-in compiles but does not run.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | ssdsim
Files: files | file ages | folders
SHA1: 9e6efcf05402cd0b259c9b2ae57e349800650bd7
User & Date: drh 2012-10-25 01:50:59
Context
2012-10-25
15:32
Merge the command-line shell enhancements from trunk. Other edits toward trying to get ssdsim to run. check-in: 848f87e2 user: drh tags: ssdsim
01:50
Initial check-in of a test VFS designed to simulate a NAND-flash SSD for the purpose of measuring and subsequently minimizing write amplification caused by SQLite. The code in this check-in compiles but does not run. check-in: 9e6efcf0 user: drh tags: ssdsim
2012-10-19
02:10
Make sure substructure elements have proper alignment in the ICU tokenizers of FTS2 and FTS3. check-in: aaa2d9b0 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Show Whitespace Changes Patch

Changes to src/shell.c.

  1012   1012     char *zErrMsg = sqlite3_malloc(nErrMsg);
  1013   1013     if( zErrMsg ){
  1014   1014       memcpy(zErrMsg, sqlite3_errmsg(db), nErrMsg);
  1015   1015     }
  1016   1016     return zErrMsg;
  1017   1017   }
  1018   1018   
         1019  +#ifdef SQLITE_ENABLE_SSDSIM
         1020  +extern void ssdsim_report(FILE*, int);
         1021  +#endif
         1022  +
  1019   1023   /*
  1020   1024   ** Display memory stats.
  1021   1025   */
  1022   1026   static int display_stats(
  1023   1027     sqlite3 *db,                /* Database to query */
  1024   1028     struct callback_data *pArg, /* Pointer to struct callback_data */
  1025   1029     int bReset                  /* True to reset the stats */
................................................................................
  2096   2100       }
  2097   2101       if( nArg >= 3) {
  2098   2102         strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1);
  2099   2103       }
  2100   2104     }else
  2101   2105   
  2102   2106     if( c=='q' && strncmp(azArg[0], "quit", n)==0 && nArg==1 ){
         2107  +#ifdef SQLITE_ENABLE_SSDSIM
         2108  +    ssdsim_report(p->out, 0);
         2109  +#endif
  2103   2110       rc = 2;
  2104   2111     }else
  2105   2112   
  2106   2113     if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 && nArg==2 ){
  2107   2114       FILE *alt = fopen(azArg[1], "rb");
  2108   2115       if( alt==0 ){
  2109   2116         fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
................................................................................
  2950   2957         vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1);
  2951   2958   #endif
  2952   2959   #ifdef SQLITE_ENABLE_MULTIPLEX
  2953   2960       }else if( strcmp(z,"-multiplex")==0 ){
  2954   2961         extern int sqlite3_multiple_initialize(const char*,int);
  2955   2962         sqlite3_multiplex_initialize(0, 1);
  2956   2963   #endif
         2964  +#ifdef SQLITE_ENABLE_SSDSIM
         2965  +    }else if( strcmp(z, "-ssdsim")==0 ){
         2966  +      extern int ssdsim_register(const char*,const char*,int);
         2967  +      ssdsim_register(0, argv[++i], 1);
         2968  +#endif
  2957   2969       }else if( strcmp(z,"-vfs")==0 ){
  2958   2970         sqlite3_vfs *pVfs = sqlite3_vfs_find(argv[++i]);
  2959   2971         if( pVfs ){
  2960   2972           sqlite3_vfs_register(pVfs, 1);
  2961   2973         }else{
  2962   2974           fprintf(stderr, "no such VFS: \"%s\"\n", argv[i]);
  2963   2975           exit(1);
................................................................................
  3073   3085   #ifdef SQLITE_ENABLE_VFSTRACE
  3074   3086       }else if( strcmp(z,"-vfstrace")==0 ){
  3075   3087         i++;
  3076   3088   #endif
  3077   3089   #ifdef SQLITE_ENABLE_MULTIPLEX
  3078   3090       }else if( strcmp(z,"-multiplex")==0 ){
  3079   3091         i++;
         3092  +#endif
         3093  +#ifdef SQLITE_ENABLE_SSDSIM
         3094  +    }else if( strcmp(z,"-ssdsim")==0 ){
         3095  +      i++;
  3080   3096   #endif
  3081   3097       }else if( strcmp(z,"-help")==0 ){
  3082   3098         usage(1);
  3083   3099       }else if( strcmp(z,"-cmd")==0 ){
  3084   3100         if( i==argc-1 ) break;
  3085   3101         i++;
  3086   3102         z = argv[i];

Added src/test_ssdsim.c.

            1  +/*
            2  +** 2012 October 23
            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 code implements a VFS shim that attempts to simulate
           14  +** a NAND-flash SSD in order to estimate the Write Amplification Factor
           15  +** (WAF) for a typical SQLite workload.
           16  +**
           17  +** This simulator is single-threaded, for simplicity.
           18  +**
           19  +** USAGE:
           20  +**
           21  +** This source file exports a single symbol which is the name of a
           22  +** function:
           23  +**
           24  +**   int ssdsim_register(
           25  +**     const char *zBaseVfsName,     // Name of the underlying real VFS
           26  +**     int makeDefault               // Make the new VFS the default
           27  +**   );
           28  +*/
           29  +#include <stdlib.h>
           30  +#include <string.h>
           31  +#include <stdio.h>
           32  +#include <assert.h>
           33  +#include "sqlite3.h"
           34  +
           35  +/* Forward declaration of objects */
           36  +typedef struct ssdsim_state ssdsim_state;
           37  +typedef struct ssdsim_inode ssdsim_inode;
           38  +typedef struct ssdsim_file ssdsim_file;
           39  +
           40  +/*
           41  +** Each file on disk
           42  +*/
           43  +struct ssdsim_inode {
           44  +  ssdsim_inode *pNext;      /* Next inode in a list of them all */
           45  +  char *zPath;              /* Full pathname of the file */
           46  +  sqlite3_int64 len;        /* Size of the file in bytes */
           47  +  int *aiPage;               /* Array of logical page numbers */
           48  +  ssdsim_file *pFiles;      /* List of open file descriptors */
           49  +  int inodeFlags;           /* SSDSIM_* flags */
           50  +  int nShmRegion;           /* Number of allocated shared memory regions */
           51  +  int szShmRegion;          /* Size of each shared-memory region */
           52  +  char **apShm;             /* Shared memory regions */
           53  +};
           54  +
           55  +#define SSDSIM_DELETEONCLOSE   0x0001
           56  +
           57  +/*
           58  +** Each open file
           59  +*/
           60  +struct ssdsim_file {
           61  +  sqlite3_file base;            /* Base class.  Must be first */
           62  +  ssdsim_file *pNext;           /* Next opening of the same inode */
           63  +  ssdsim_inode *pInode;         /* The file */
           64  +  signed char eLock;            /* Lock state for this connection */
           65  +  unsigned char shmOpen;        /* True if SHM is open */
           66  +  unsigned short shmReadLock;   /* Shared locks held by the shared memory */
           67  +  unsigned short shmWriteLock;  /* Exclusive locks held by the shared memory */
           68  +  int openFlags;                /* xOpen() flags used to open this connection */
           69  +};
           70  +
           71  +/*
           72  +** Page status values
           73  +*/
           74  +#define SSDSIM_FREE      0
           75  +#define SSDSIM_WRITTEN   1
           76  +#define SSDSIM_OBSOLETE  2
           77  +
           78  +/*
           79  +** Global state of the SSD simulator
           80  +*/
           81  +struct ssdsim_state {
           82  +  int szPage;               /* Size of each page in bytes */
           83  +  int szEBlock;             /* Size of an erase block in bytes */
           84  +  sqlite3_int64 szDisk;     /* Total disk space in bytes */
           85  +  int nPage;                /* Number of slots allocated in apPage[] */
           86  +  int nEBlock;              /* Nubmer of erase blocks */
           87  +  int nDealloc;             /* Number of reusable logical page numbers */
           88  +  int mxAlloc;              /* Maximum allocated logical page number */
           89  +  unsigned char **apPage;   /* Memory to hold physical pages */
           90  +  int *aDealloc;            /* Array of reuseable logical page numbers */
           91  +  int *pageMap;             /* Mapping from logical to physical pages */
           92  +  unsigned char *eStat;     /* Status of each page */
           93  +  unsigned int *nErase;     /* Number of erasures for each erase block */
           94  +  ssdsim_inode *pInode;     /* List of all inodes */
           95  +  int traceFlag;            /* True to trace operation */
           96  +  int nHostWrite;           /* Number of pages written by the application */
           97  +  int nNANDWrite;           /* Number of pages written to NAND-flash */
           98  +  sqlite3_vfs *pBase;       /* True underlying VFS */
           99  +};
          100  +static ssdsim_state g;
          101  +
          102  +/*
          103  +** Method declarations for ssdsim_file.
          104  +*/
          105  +static int ssdsimClose(sqlite3_file*);
          106  +static int ssdsimRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
          107  +static int ssdsimWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
          108  +static int ssdsimTruncate(sqlite3_file*, sqlite3_int64 size);
          109  +static int ssdsimSync(sqlite3_file*, int flags);
          110  +static int ssdsimFileSize(sqlite3_file*, sqlite3_int64 *pSize);
          111  +static int ssdsimLock(sqlite3_file*, int);
          112  +static int ssdsimUnlock(sqlite3_file*, int);
          113  +static int ssdsimCheckReservedLock(sqlite3_file*, int *);
          114  +static int ssdsimFileControl(sqlite3_file*, int op, void *pArg);
          115  +static int ssdsimSectorSize(sqlite3_file*);
          116  +static int ssdsimDeviceCharacteristics(sqlite3_file*);
          117  +static int ssdsimShmLock(sqlite3_file*,int,int,int);
          118  +static int ssdsimShmMap(sqlite3_file*,int,int,int, void volatile **);
          119  +static void ssdsimShmBarrier(sqlite3_file*);
          120  +static int ssdsimShmUnmap(sqlite3_file*,int);
          121  +
          122  +/*
          123  +** Method declarations for ssdsim_vfs.
          124  +*/
          125  +static int ssdsimOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
          126  +static int ssdsimDelete(sqlite3_vfs*, const char *zName, int syncDir);
          127  +static int ssdsimAccess(sqlite3_vfs*, const char *zName, int flags, int *);
          128  +static int ssdsimFullPathname(sqlite3_vfs*, const char *zName, int, char *);
          129  +static void *ssdsimDlOpen(sqlite3_vfs*, const char *zFilename);
          130  +static void ssdsimDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
          131  +static void (*ssdsimDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
          132  +static void ssdsimDlClose(sqlite3_vfs*, void*);
          133  +static int ssdsimRandomness(sqlite3_vfs*, int nByte, char *zOut);
          134  +static int ssdsimSleep(sqlite3_vfs*, int microseconds);
          135  +static int ssdsimCurrentTime(sqlite3_vfs*, double*);
          136  +static int ssdsimGetLastError(sqlite3_vfs*, int, char*);
          137  +static int ssdsimCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
          138  +static int ssdsimSetSystemCall(sqlite3_vfs*,const char*, sqlite3_syscall_ptr);
          139  +static sqlite3_syscall_ptr ssdsimGetSystemCall(sqlite3_vfs*, const char *);
          140  +static const char *ssdsimNextSystemCall(sqlite3_vfs*, const char *zName);
          141  +
          142  +/*
          143  +** Trace operation.
          144  +*/
          145  +static void ssdsimTrace(const char *zFormat, ...){
          146  +  if( g.traceFlag ){
          147  +    va_list ap;
          148  +    char *zMsg;
          149  +    va_start(ap, zFormat);
          150  +    vprintf(zFormat, ap);
          151  +    va_end(ap);
          152  +  }
          153  +}
          154  +
          155  +/*
          156  +** Clear all memory associated with the ssd simulator
          157  +*/
          158  +static void ssdsimShutdown(void){
          159  +  int i;
          160  +  for(i=0; i<g.nPage; i++) sqlite3_free(g.apPage[i]);
          161  +  sqlite3_free(g.apPage);
          162  +  g.apPage = 0;
          163  +  sqlite3_free(g.aDealloc);
          164  +  g.aDealloc = 0;
          165  +  g.nDealloc = 0;
          166  +  g.mxAlloc = 0;
          167  +  g.nPage = 0;
          168  +}
          169  +
          170  +/*
          171  +** Initialize the ssdsim system.
          172  +*/
          173  +static void ssdsimInit(void){
          174  +  int nPage;
          175  +  if( g.nPage ) return;
          176  +  if( g.szPage==0 ) g.szPage = 4096;
          177  +  if( g.szEBlock==0 ) g.szEBlock = 262144;
          178  +  if( g.szDisk==0 ) g.szDisk = 67108864;
          179  +  g.nPage = g.szDisk/g.szPage;
          180  +  g.nEBlock = g.szDisk/g.szEBlock;
          181  +  sqlite3_free(g.apPage);
          182  +  g.apPage = sqlite3_malloc( sizeof(g.apPage[0])*g.nPage );
          183  +  if( g.apPage==0 ){ ssdsimShutdown(); return; }
          184  +  memset(g.apPage, 0, sizeof(g.apPage[0])*nPage);
          185  +  g.aDealloc = sqlite3_malloc( sizeof(g.aDealloc[0])*g.nPage );
          186  +  if( g.aDealloc==0 ){ ssdsimShutdown(); return; }
          187  +  g.nDealloc = 0;
          188  +  g.mxAlloc = 0;
          189  +  g.nHostWrite = 0;
          190  +  g.nNANDWrite = 0;
          191  +}
          192  +
          193  +/*
          194  +** Allocate a new, unused logical page number
          195  +*/
          196  +static int ssdsimCoreLpnAlloc(void){
          197  +  if( g.nDealloc ){
          198  +    return g.aDealloc[--g.nDealloc];
          199  +  }else if( g.mxAlloc>=g.nPage ){
          200  +    return -1;
          201  +  }else{
          202  +    return g.mxAlloc++;
          203  +  }
          204  +}
          205  +
          206  +/*
          207  +** Indicate that the content of a logical page will never again be
          208  +** read.
          209  +*/
          210  +static void ssdsimCoreTrim(int lpn){
          211  +}
          212  +
          213  +/*
          214  +** Deallocate a logical page number, indicating that it is no longer
          215  +** in use.
          216  +*/
          217  +static int ssdsimCoreLpnDealloc(int lpn){
          218  +  g.aDealloc[g.nDealloc++] = lpn;
          219  +}
          220  +
          221  +/*
          222  +** Translate a logical page number into a physical page number.
          223  +*/
          224  +static int ssdsimCoreLpnToPpn(int lpn, int writeFlag){
          225  +  int ppn = lpn;
          226  +  if( g.apPage[ppn]==0 ){
          227  +    if( writeFlag ){
          228  +      g.apPage[ppn] = sqlite3_malloc( g.szPage );
          229  +    }
          230  +    if( g.apPage[ppn]==0 ) ppn = -1;
          231  +  }
          232  +  return ppn;
          233  +}
          234  +
          235  +/*
          236  +** Indicate that a transaction boundary has occurred
          237  +*/
          238  +static int ssdsimCoreSync(void){
          239  +}
          240  +
          241  +
          242  +/*
          243  +** Truncate an inode
          244  +*/
          245  +static void ssdsimTruncateInode(ssdsim_inode *pInode, sqlite3_int64 size){
          246  +  if( pInode->len > size ){
          247  +    int nOld = pInode->len/g.szPage;
          248  +    int nNew = size/g.szPage;
          249  +    int i;
          250  +    for(i=nOld; i>nNew; i--){
          251  +      ssdsimCoreLpnDealloc(pInode->aiPage[i]);
          252  +    }
          253  +    pInode->len = size;
          254  +  }
          255  +}
          256  +
          257  +/*
          258  +** Delete an inode
          259  +*/
          260  +static void ssdsimDeleteInode(ssdsim_inode *pInode){
          261  +  if( pInode->pFiles ){
          262  +    pInode->inodeFlags |= SSDSIM_DELETEONCLOSE;
          263  +    return;
          264  +  }
          265  +  ssdsimTruncateInode(pInode, 0);
          266  +  sqlite3_free(pInode->apShm);
          267  +  sqlite3_free(pInode->aiPage);
          268  +  if( g.pInode==pInode ){
          269  +    g.pInode = pInode->pNext;
          270  +  }else{
          271  +    ssdsim_inode *pX;
          272  +    for(pX=g.pInode; pX && pX->pNext!=pInode; pX=pX->pNext){}
          273  +    if( pX ) pX->pNext = pInode->pNext;
          274  +  }
          275  +  sqlite3_free(pInode);
          276  +}
          277  +
          278  +
          279  +/*
          280  +** Close an ssdsim-file.
          281  +*/
          282  +static int ssdsimClose(sqlite3_file *pFile){
          283  +  ssdsim_file *p = (ssdsim_file *)pFile;
          284  +  int rc;
          285  +  ssdsim_inode *pInode = p->pInode;
          286  +  if( p==pInode->pFiles ){
          287  +    pInode->pFiles = p->pNext;
          288  +    if( (pInode->inodeFlags & SSDSIM_DELETEONCLOSE)!=0 ){
          289  +      ssdsimDeleteInode(pInode);
          290  +    }
          291  +  }else{
          292  +    ssdsim_file *pX;
          293  +    for(pX = pInode->pFiles; pX && pX->pNext!=p; pX=pX->pNext){}
          294  +    if( pX ) pX->pNext = p->pNext;
          295  +  }
          296  +  memset(p, 0, sizeof(*p));
          297  +  return SQLITE_OK;
          298  +}
          299  +
          300  +/*
          301  +** Read data from an ssdsim-file.
          302  +*/
          303  +static int ssdsimRead(
          304  +  sqlite3_file *pFile, 
          305  +  void *zBuf, 
          306  +  int iAmt, 
          307  +  sqlite_int64 iOfst
          308  +){
          309  +  ssdsim_file *p = (ssdsim_file *)pFile;
          310  +  ssdsim_inode *pInode = p->pInode;
          311  +  int rc = SQLITE_OK;
          312  +  int lpn, ppn, n;
          313  +  unsigned char *pOut = (unsigned char*)zBuf;
          314  +  unsigned char *pContent;
          315  +  while( iAmt>0 ){
          316  +    if( iAmt+iOfst>pInode->len ){
          317  +      rc = SQLITE_IOERR_SHORT_READ;
          318  +      iAmt = pInode->len - iOfst;
          319  +      if( iAmt<=0 ) break;
          320  +    }
          321  +    lpn = pInode->aiPage[iOfst/g.szPage];
          322  +    ppn = ssdsimCoreLpnToPpn(lpn, 0);
          323  +    n = iAmt;
          324  +    if( (iOfst+n-1)*g.szPage > lpn ){
          325  +      n = (lpn+1)*g.szPage - iOfst;
          326  +    }
          327  +    if( ppn>=0 && ppn<g.nPage && (pContent = g.apPage[ppn])!=0 ){
          328  +      memcpy(pOut, &pContent[iOfst%g.szPage], n);
          329  +    }else{
          330  +      memset(pOut, 0, n);
          331  +    }
          332  +    iOfst += n;
          333  +    iAmt -= n;
          334  +    pOut += n;
          335  +  }
          336  +  return rc;
          337  +}
          338  +
          339  +/*
          340  +** Write data to an ssdsim-file.
          341  +*/
          342  +static int ssdsimWrite(
          343  +  sqlite3_file *pFile, 
          344  +  const void *zBuf, 
          345  +  int iAmt, 
          346  +  sqlite_int64 iOfst
          347  +){
          348  +  ssdsim_file *p = (ssdsim_file *)pFile;
          349  +  ssdsim_inode *pInode = p->pInode;
          350  +  int rc = SQLITE_OK;
          351  +  int pn, lpn, ppn;
          352  +  sqlite3_int64 lenNew;
          353  +  const unsigned char *pIn = (const unsigned char*)zBuf;
          354  +  unsigned char *pDest;
          355  +
          356  +  lenNew = iOfst+iAmt;
          357  +  if( lenNew <= pInode->len ){
          358  +    lenNew = pInode->len;
          359  +  }else{
          360  +    int nOld, nNew;
          361  +    int *aiPage;
          362  +    nOld = pInode->len/g.szPage;
          363  +    nNew = (iOfst+iAmt)/g.szPage;
          364  +    if( nOld<nNew ){
          365  +      aiPage = sqlite3_realloc(pInode->aiPage, nNew*sizeof(int));
          366  +      if( aiPage==0 ) return SQLITE_NOMEM;
          367  +      memset(aiPage+nOld, 0xff, sizeof(int)*(nNew - nOld));
          368  +      pInode->aiPage = aiPage;
          369  +    }
          370  +  }
          371  +  while( iAmt>0 ){
          372  +    int n;
          373  +    lpn = pInode->aiPage[iOfst/g.szPage];
          374  +    if( lpn<0 ){
          375  +      lpn = ssdsimCoreLpnAlloc();
          376  +      if( lpn<0 ) return SQLITE_FULL;
          377  +      pInode->aiPage[iOfst/g.szPage];
          378  +    }
          379  +    ppn = ssdsimCoreLpnToPpn(lpn, 1);
          380  +    if( ppn<0 ) return SQLITE_NOMEM;
          381  +    n = iAmt;
          382  +    if( (iOfst+n-1)*g.szPage > lpn ){
          383  +      n = (lpn+1)*g.szPage - iOfst;
          384  +    }
          385  +    pDest = g.apPage[ppn];
          386  +    memcpy(pDest, pIn, n);
          387  +    iOfst += n;
          388  +    iAmt -= n;
          389  +    pIn += n;
          390  +  }
          391  +  pInode->len = lenNew;
          392  +  return rc;
          393  +}
          394  +
          395  +/*
          396  +** Truncate an ssdsim-file.
          397  +*/
          398  +static int ssdsimTruncate(sqlite3_file *pFile, sqlite_int64 size){
          399  +  ssdsim_file *p = (ssdsim_file *)pFile;
          400  +  ssdsim_inode *pInode = p->pInode;
          401  +  ssdsimTruncateInode(pInode, size);
          402  +  return SQLITE_OK;
          403  +}
          404  +
          405  +/*
          406  +** Sync an ssdsim-file.
          407  +*/
          408  +static int ssdsimSync(sqlite3_file *pFile, int flags){
          409  +  ssdsim_file *p = (ssdsim_file *)pFile;
          410  +  ssdsim_inode *pInode = p->pInode;
          411  +  ssdsimCoreSync();
          412  +  return SQLITE_OK;
          413  +}
          414  +
          415  +/*
          416  +** Return the current file-size of an ssdsim-file.
          417  +*/
          418  +static int ssdsimFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
          419  +  ssdsim_file *p = (ssdsim_file *)pFile;
          420  +  ssdsim_inode *pInode = p->pInode;
          421  +  *pSize = pInode->len;
          422  +  return SQLITE_OK;
          423  +}
          424  +
          425  +/*
          426  +** Return the name of a lock.
          427  +*/
          428  +static const char *lockName(int eLock){
          429  +  const char *azLockNames[] = {
          430  +     "NONE", "SHARED", "RESERVED", "PENDING", "EXCLUSIVE"
          431  +  };
          432  +  if( eLock<0 || eLock>=sizeof(azLockNames)/sizeof(azLockNames[0]) ){
          433  +    return "???";
          434  +  }else{
          435  +    return azLockNames[eLock];
          436  +  }
          437  +}
          438  +
          439  +/*
          440  +** Lock an ssdsim-file.
          441  +*/
          442  +static int ssdsimLock(sqlite3_file *pFile, int eLock){
          443  +  ssdsim_file *p = (ssdsim_file *)pFile;
          444  +  ssdsim_inode *pInode = p->pInode;
          445  +  ssdsim_file *pF;
          446  +  int rc = SQLITE_OK;
          447  +  if( eLock==SQLITE_LOCK_SHARED ){
          448  +    for(pF=pInode->pFiles; pF; pF=pF->pNext){
          449  +      if( pF!=p && pF->eLock>=SQLITE_LOCK_PENDING ) return SQLITE_BUSY;
          450  +    }
          451  +  }else if( eLock>=SQLITE_LOCK_RESERVED ){
          452  +    for(pF=pInode->pFiles; pF; pF=pF->pNext){
          453  +      if( pF!=p && pF->eLock>=SQLITE_LOCK_RESERVED ) return SQLITE_BUSY;
          454  +    }
          455  +  }else if( eLock==SQLITE_LOCK_EXCLUSIVE ){
          456  +    for(pF=pInode->pFiles; pF; pF=pF->pNext){
          457  +      if( pF!=p && pF->eLock>=SQLITE_LOCK_SHARED ){
          458  +        eLock = SQLITE_LOCK_PENDING;
          459  +        rc = SQLITE_BUSY;
          460  +      }
          461  +    }
          462  +  }
          463  +  p->eLock = eLock;
          464  +  return rc;
          465  +}
          466  +
          467  +/*
          468  +** Unlock an ssdsim-file.
          469  +*/
          470  +static int ssdsimUnlock(sqlite3_file *pFile, int eLock){
          471  +  ssdsim_file *p = (ssdsim_file *)pFile;
          472  +  if( p->eLock>eLock ) p->eLock = eLock;
          473  +  return SQLITE_OK;
          474  +}
          475  +
          476  +/*
          477  +** Check if another file-handle holds a RESERVED lock on an ssdsim-file.
          478  +*/
          479  +static int ssdsimCheckReservedLock(sqlite3_file *pFile, int *pResOut){
          480  +  ssdsim_file *p = (ssdsim_file *)pFile;
          481  +  ssdsim_inode *pInode = p->pInode;
          482  +  ssdsim_file *pF;
          483  +  int rc = 0;
          484  +  for(pF=pInode->pFiles; pF; pF=pF->pNext){
          485  +    if( pF!=p && pF->eLock>=SQLITE_LOCK_RESERVED ){
          486  +      rc = 1;
          487  +      break;
          488  +    }
          489  +  }
          490  +  *pResOut = rc;
          491  +  return SQLITE_OK;
          492  +}
          493  +
          494  +/*
          495  +** File control method. For custom operations on an ssdsim-file.
          496  +*/
          497  +static int ssdsimFileControl(sqlite3_file *pFile, int op, void *pArg){
          498  +  ssdsim_file *p = (ssdsim_file *)pFile;
          499  +  ssdsim_inode *pInode = p->pInode;
          500  +  switch( op ){
          501  +    case SQLITE_FCNTL_LOCKSTATE: {
          502  +      *(int*)pArg = p->eLock;
          503  +      return SQLITE_OK;
          504  +    }
          505  +    case SQLITE_FCNTL_VFSNAME: {
          506  +      *(char**)pArg = sqlite3_mprintf("ssdsim");
          507  +      return SQLITE_OK;
          508  +    }
          509  +    case SQLITE_FCNTL_PRAGMA: {
          510  +#if 0
          511  +      const char *const* a = (const char*const*)pArg;
          512  +      sqlite3_snprintf(sizeof(zBuf), zBuf, "PRAGMA,[%s,%s]",a[1],a[2]);
          513  +      zOp = zBuf;
          514  +#endif
          515  +      break;
          516  +    }
          517  +    default: {
          518  +      break;
          519  +    }
          520  +  }
          521  +  return SQLITE_NOTFOUND;
          522  +}
          523  +
          524  +/*
          525  +** Return the sector-size in bytes for an ssdsim-file.
          526  +*/
          527  +static int ssdsimSectorSize(sqlite3_file *pFile){
          528  +  return g.szPage;
          529  +}
          530  +
          531  +/*
          532  +** Return the device characteristic flags supported by an ssdsim-file.
          533  +*/
          534  +static int ssdsimDeviceCharacteristics(sqlite3_file *pFile){
          535  +  return 
          536  +     SQLITE_IOCAP_ATOMIC |
          537  +     SQLITE_IOCAP_POWERSAFE_OVERWRITE |
          538  +     SQLITE_IOCAP_SAFE_APPEND |
          539  +     SQLITE_IOCAP_SEQUENTIAL |
          540  +     SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
          541  +     0;
          542  +}
          543  +
          544  +/*
          545  +** Shared-memory operations.
          546  +*/
          547  +static int ssdsimShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
          548  +  ssdsim_file *p = (ssdsim_file *)pFile;
          549  +  ssdsim_inode *pInode = p->pInode;
          550  +  ssdsim_file *pF;
          551  +  unsigned int lockMask = 0;
          552  +
          553  +  /* Constraints on the SQLite core: */
          554  +  assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK );
          555  +  assert( n>=1 );
          556  +  assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
          557  +       || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
          558  +       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
          559  +       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
          560  +  assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
          561  +
          562  +  /* Mask of bits involved in this lock */
          563  +  lockMask = (1<<(ofst+n)) - (1<<ofst);
          564  +
          565  +  /* The unlock case */
          566  +  if( flags & SQLITE_SHM_UNLOCK ){
          567  +    p->shmWriteLock &= ~lockMask;
          568  +    p->shmReadLock &= ~lockMask;
          569  +    return SQLITE_OK;
          570  +  }
          571  +
          572  +  /* The shared-lock case */
          573  +  if( flags & SQLITE_SHM_SHARED ){
          574  +    /* Disallow if any sibling (including ourself) holds an exclusive lock */
          575  +    for(pF=pInode->pFiles; pF; pF=pF->pNext){
          576  +      if( pF->shmWriteLock & lockMask ){
          577  +        return SQLITE_BUSY;
          578  +      }
          579  +    }
          580  +    p->shmReadLock |= lockMask;
          581  +    return SQLITE_OK;
          582  +  }
          583  +
          584  +  /* The rest of this procedure is the exclusive lock case */
          585  +  assert( flags & SQLITE_SHM_EXCLUSIVE );
          586  +
          587  +  /* Disallow an exclusive if any kind of lock is held by any other */
          588  +  for(pF=pInode->pFiles; pF; pF=pF->pNext){
          589  +    if( pF==p ) continue;
          590  +    if( (pF->shmWriteLock & lockMask)!=0 ){
          591  +      return SQLITE_BUSY;
          592  +    }
          593  +    if( (pF->shmReadLock & lockMask)!=0 ){
          594  +      return SQLITE_BUSY;
          595  +    }
          596  +  }
          597  +  p->shmWriteLock |= lockMask;
          598  +  return SQLITE_OK;
          599  +}
          600  +static int ssdsimShmMap(
          601  +  sqlite3_file *pFile, 
          602  +  int iRegion, 
          603  +  int szRegion, 
          604  +  int isWrite, 
          605  +  void volatile **pp
          606  +){
          607  +  ssdsim_file *p = (ssdsim_file *)pFile;
          608  +  ssdsim_inode *pInode = p->pInode;
          609  +  char **apShm;
          610  +  int i;
          611  +  if( p->shmOpen==0 ){
          612  +    p->shmOpen = 1;
          613  +    p->shmReadLock = 0;
          614  +    p->shmWriteLock = 0;
          615  +  }
          616  +  if( pInode->nShmRegion<=iRegion ){
          617  +    if( isWrite==0 ){
          618  +      *pp = 0;
          619  +      return SQLITE_OK;
          620  +    }
          621  +    apShm = sqlite3_realloc(pInode->apShm, 
          622  +                            (iRegion+1)*sizeof(pInode->apShm[0]));
          623  +    if( apShm==0 ) return SQLITE_NOMEM;
          624  +    pInode->apShm = apShm;
          625  +    for(i=pInode->nShmRegion; i<=iRegion; i++){
          626  +      apShm[i] = sqlite3_malloc(szRegion);
          627  +      if( apShm[i]==0 ) return SQLITE_NOMEM;
          628  +      memset(apShm[i], 0, szRegion);
          629  +      pInode->nShmRegion = i+1;
          630  +    }
          631  +    pInode->szShmRegion = szRegion;
          632  +  }
          633  +  *pp = pInode->apShm[iRegion];
          634  +  return SQLITE_OK;
          635  +}
          636  +static void ssdsimShmBarrier(sqlite3_file *pFile){
          637  +  /* noop */
          638  +}
          639  +static int ssdsimShmUnmap(sqlite3_file *pFile, int delFlag){
          640  +  ssdsim_file *p = (ssdsim_file *)pFile;
          641  +  ssdsim_inode *pInode = p->pInode;
          642  +  if( p->shmOpen ){
          643  +    ssdsim_file *pF;
          644  +    unsigned char shmOpen = 0;
          645  +    p->shmOpen = 0;
          646  +    for(pF=pInode->pFiles; pF; pF=pF->pNext) shmOpen |= pF->shmOpen;
          647  +    if( !shmOpen ){
          648  +      int i;
          649  +      for(i=0; i<pInode->nShmRegion; i++) sqlite3_free(pInode->apShm[i]);
          650  +      sqlite3_free(pInode->apShm);
          651  +      pInode->apShm = 0;
          652  +      pInode->nShmRegion = 0;
          653  +    }
          654  +  }
          655  +  return SQLITE_OK;
          656  +}
          657  +
          658  +static const sqlite3_io_methods ssdsim_io_methods = {
          659  +  /* iVersion               */ 2,
          660  +  /* xClose                 */ ssdsimClose,
          661  +  /* xRead                  */ ssdsimRead,
          662  +  /* xWrite                 */ ssdsimWrite,
          663  +  /* xTruncate              */ ssdsimTruncate,
          664  +  /* xSync                  */ ssdsimSync,
          665  +  /* xFileSize              */ ssdsimFileSize,
          666  +  /* xLock                  */ ssdsimLock,
          667  +  /* xUnlock                */ ssdsimUnlock,
          668  +  /* xCheckReservedLock     */ ssdsimCheckReservedLock,
          669  +  /* xFileControl           */ ssdsimFileControl,
          670  +  /* xSectorSize            */ ssdsimSectorSize,
          671  +  /* xDeviceCharacteristics */ ssdsimDeviceCharacteristics,
          672  +  /* xShmMap                */ ssdsimShmMap,
          673  +  /* xShmLock               */ ssdsimShmLock,
          674  +  /* xShmBarrier            */ ssdsimShmBarrier,
          675  +  /* xShmUnmap              */ ssdsimShmUnmap
          676  +};
          677  +
          678  +/*
          679  +** Find an inode given its name.
          680  +*/
          681  +static ssdsim_inode *ssdsimFindInode(const char *zName){
          682  +  ssdsim_inode *pInode;
          683  +  for(pInode=g.pInode; pInode; pInode=pInode->pNext){
          684  +    if( strcmp(pInode->zPath, zName)==0 ) break;
          685  +  }
          686  +  return pInode;
          687  +}
          688  +
          689  +/*
          690  +** Open an ssdsim file handle.
          691  +*/
          692  +static int ssdsimOpen(
          693  +  sqlite3_vfs *pVfs,
          694  +  const char *zName,
          695  +  sqlite3_file *pFile,
          696  +  int flags,
          697  +  int *pOutFlags
          698  +){
          699  +  int rc;
          700  +  ssdsim_file *p = (ssdsim_file *)pFile;
          701  +  ssdsim_inode *pInode = ssdsimFindInode(zName);
          702  +  if( pInode==0 ){
          703  +    int n = (int)strlen(zName);
          704  +    pInode = sqlite3_malloc( sizeof(*pInode) + n + 1 );
          705  +    if( pInode==0 ) return SQLITE_NOMEM;
          706  +    memset(pInode, 0, sizeof(*pInode));
          707  +    pInode->pNext = g.pInode;
          708  +    g.pInode = pInode;
          709  +    pInode->zPath = (char*)&pInode[1];
          710  +    strcpy(pInode->zPath, zName);
          711  +    pInode->len = 0;
          712  +    pInode->aiPage = 0;
          713  +    pInode->pFiles = 0;
          714  +    pInode->inodeFlags = 0;
          715  +    pInode->nShmRegion = 0;
          716  +    pInode->szShmRegion = 0;
          717  +    pInode->apShm = 0;
          718  +    if( flags & SQLITE_OPEN_DELETEONCLOSE ){
          719  +      pInode->inodeFlags |= SSDSIM_DELETEONCLOSE;
          720  +    }
          721  +  }
          722  +  p->pInode = pInode;
          723  +  p->pNext = pInode->pFiles;
          724  +  pInode->pFiles = p;
          725  +  p->eLock = 0;
          726  +  p->shmOpen = 0;
          727  +  p->shmReadLock = 0;
          728  +  p->shmWriteLock = 0;
          729  +  p->openFlags = flags;
          730  +  p->base.pMethods = &ssdsim_io_methods;
          731  +  return SQLITE_OK;
          732  +}
          733  +
          734  +/*
          735  +** Delete the file located at zPath. If the dirSync argument is true,
          736  +** ensure the file-system modifications are synced to disk before
          737  +** returning.
          738  +*/
          739  +static int ssdsimDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
          740  +  ssdsim_inode *pInode = ssdsimFindInode(zPath);
          741  +  if( pInode==0 ) return SQLITE_NOTFOUND;
          742  +  ssdsimDeleteInode(pInode);
          743  +  return SQLITE_OK;
          744  +}
          745  +
          746  +/*
          747  +** Test for access permissions. Return true if the requested permission
          748  +** is available, or false otherwise.
          749  +*/
          750  +static int ssdsimAccess(
          751  +  sqlite3_vfs *pVfs, 
          752  +  const char *zPath, 
          753  +  int flags, 
          754  +  int *pResOut
          755  +){
          756  +  ssdsim_inode *pInode = ssdsimFindInode(zPath);
          757  +  *pResOut = pInode!=0;
          758  +  return SQLITE_OK;
          759  +}
          760  +
          761  +/*
          762  +** Populate buffer zOut with the full canonical pathname corresponding
          763  +** to the pathname in zPath. zOut is guaranteed to point to a buffer
          764  +** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
          765  +*/
          766  +static int ssdsimFullPathname(
          767  +  sqlite3_vfs *pVfs, 
          768  +  const char *zPath, 
          769  +  int nOut, 
          770  +  char *zOut
          771  +){
          772  +  while( zPath[0]=='/' ) zPath++;
          773  +  sqlite3_snprintf(nOut, zOut, "/%s", zPath);
          774  +  return SQLITE_OK;
          775  +}
          776  +
          777  +/*
          778  +** Open the dynamic library located at zPath and return a handle.
          779  +*/
          780  +static void *ssdsimDlOpen(sqlite3_vfs *pVfs, const char *zPath){
          781  +  return 0;
          782  +}
          783  +
          784  +/*
          785  +** Populate the buffer zErrMsg (size nByte bytes) with a human readable
          786  +** utf-8 string describing the most recent error encountered associated 
          787  +** with dynamic libraries.
          788  +*/
          789  +static void ssdsimDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
          790  +  sqlite3_snprintf(nByte, zErrMsg, "not supported by this VFS");
          791  +}
          792  +
          793  +/*
          794  +** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
          795  +*/
          796  +static void (*ssdsimDlSym(sqlite3_vfs *pVfs,void *p,const char *zSym))(void){
          797  +  return 0;
          798  +}
          799  +
          800  +/*
          801  +** Close the dynamic library handle pHandle.
          802  +*/
          803  +static void ssdsimDlClose(sqlite3_vfs *pVfs, void *pHandle){
          804  +}
          805  +
          806  +/*
          807  +** Populate the buffer pointed to by zBufOut with nByte bytes of 
          808  +** random data.
          809  +*/
          810  +static int ssdsimRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
          811  +  return g.pBase->xRandomness(g.pBase, nByte, zBufOut);
          812  +}
          813  +
          814  +/*
          815  +** Sleep for nMicro microseconds. Return the number of microseconds 
          816  +** actually slept.
          817  +*/
          818  +static int ssdsimSleep(sqlite3_vfs *pVfs, int nMicro){
          819  +  return g.pBase->xSleep(g.pBase, nMicro);
          820  +}
          821  +
          822  +/*
          823  +** Return the current time as a Julian Day number in *pTimeOut.
          824  +*/
          825  +static int ssdsimCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
          826  +  return g.pBase->xCurrentTime(g.pBase, pTimeOut);
          827  +}
          828  +static int ssdsimCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
          829  +  return g.pBase->xCurrentTimeInt64(g.pBase, pTimeOut);
          830  +}
          831  +
          832  +/*
          833  +** Return th3 emost recent error code and message
          834  +*/
          835  +static int ssdsimGetLastError(sqlite3_vfs *pVfs, int iErr, char *zErr){
          836  +  return SQLITE_OK;
          837  +}
          838  +
          839  +/*
          840  +** Override system calls.
          841  +*/
          842  +static int ssdsimSetSystemCall(
          843  +  sqlite3_vfs *pVfs,
          844  +  const char *zName,
          845  +  sqlite3_syscall_ptr pFunc
          846  +){
          847  +  return SQLITE_NOTFOUND;
          848  +}
          849  +static sqlite3_syscall_ptr ssdsimGetSystemCall(
          850  +  sqlite3_vfs *pVfs,
          851  +  const char *zName
          852  +){
          853  +  return 0;
          854  +}
          855  +static const char *ssdsimNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
          856  +  return 0;
          857  +}
          858  +
          859  +static sqlite3_vfs ssdsim_vfs = {
          860  +  /* iVersion          */ 3,
          861  +  /* szOsFile          */ sizeof(ssdsim_file),
          862  +  /* mxPathname        */ 1024,
          863  +  /* pNext             */ 0,
          864  +  /* zName             */ "ssdsim",
          865  +  /* pAppData          */ 0,
          866  +  /* xOpen             */ ssdsimOpen,
          867  +  /* xDelete           */ ssdsimDelete,
          868  +  /* xAccess           */ ssdsimAccess,
          869  +  /* xFullPathname     */ ssdsimFullPathname,
          870  +  /* xDlOpen           */ ssdsimDlOpen,
          871  +  /* xDlError          */ ssdsimDlError,
          872  +  /* xDlSym            */ ssdsimDlSym,
          873  +  /* xDlClose          */ ssdsimDlClose,
          874  +  /* xRandomness       */ ssdsimRandomness,
          875  +  /* xSleep            */ ssdsimSleep,
          876  +  /* xCurrentTime      */ ssdsimCurrentTime,
          877  +  /* xGetLastError     */ ssdsimGetLastError,
          878  +  /* xCurrentTimeInt64 */ ssdsimCurrentTimeInt64,
          879  +  /* xSetSystemCall    */ ssdsimSetSystemCall,
          880  +  /* xGetSystemCall    */ ssdsimGetSystemCall,
          881  +  /* xNextSystemCall   */ ssdsimNextSystemCall
          882  +};
          883  +
          884  +/*
          885  +** Clients invoke this routine to register the SSD simulator
          886  +*/
          887  +int ssdsim_register(
          888  +   const char *zBaseName,          /* Name of the underlying VFS */
          889  +   const char *zParams,            /* Configuration parameter */
          890  +   int makeDefault                 /* True to make the new VFS the default */
          891  +){
          892  +  sqlite3_vfs *pNew;
          893  +  sqlite3_vfs *pRoot;
          894  +
          895  +  if( g.pBase ) return SQLITE_ERROR;
          896  +  g.pBase = sqlite3_vfs_find(zBaseName);
          897  +  if( g.pBase==0 ) return SQLITE_NOTFOUND;
          898  +  return sqlite3_vfs_register(&ssdsim_vfs, makeDefault);
          899  +}
          900  +
          901  +/*
          902  +** Clients invoke this routine to get SSD simulator write-amplification
          903  +** statistics.
          904  +*/
          905  +void ssdsim_report(FILE *pOut, int reportNum){
          906  +  fprintf(pOut, "host page writes...... %9d\n", g.nHostWrite);
          907  +  fprintf(pOut, "NAND page writes...... %9d\n", g.nNANDWrite);
          908  +  if( g.nHostWrite>0 ){
          909  +    fprintf(pOut, "write amplification... %11.2f\n",
          910  +            (double)g.nNANDWrite/(double)g.nHostWrite);
          911  +  }
          912  +}