/ Changes On Branch ssdsim
Login

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

Changes In Branch ssdsim Excluding Merge-Ins

This is equivalent to a diff from 317c80cb to ae2f1627

2012-10-25
23:47
Further work on getting ssdsim to run. This is an incremental checkin to save my place while jumping off to work on other things. (Leaf check-in: ae2f1627 user: drh tags: ssdsim)
15:43
Fix typo in usage text for the command-line shell. Also, in the same usage text, make the file name argument to -init uppercase for consistency. (check-in: a6d906cf user: mistachkin tags: trunk)
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)
15:23
Improvements to the command-line argument parsing in the command-line shell. Command-line options can now occur either before or after the database name and first command and are still accepted and processed. Command-line options are processed even if no database name is given (and :memory: is assumed). (check-in: 317c80cb user: drh tags: trunk)
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 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]);
................................................................................
  2977   2984         vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1);
  2978   2985   #endif
  2979   2986   #ifdef SQLITE_ENABLE_MULTIPLEX
  2980   2987       }else if( strcmp(z,"-multiplex")==0 ){
  2981   2988         extern int sqlite3_multiple_initialize(const char*,int);
  2982   2989         sqlite3_multiplex_initialize(0, 1);
  2983   2990   #endif
         2991  +#ifdef SQLITE_ENABLE_SSDSIM
         2992  +    }else if( strcmp(z, "-ssdsim")==0 ){
         2993  +      extern int ssdsim_register(const char*,const char*,int);
         2994  +      ssdsim_register(0, cmdline_option_value(argc,argv,++i), 1);
         2995  +#endif
  2984   2996       }else if( strcmp(z,"-vfs")==0 ){
  2985   2997         sqlite3_vfs *pVfs = sqlite3_vfs_find(cmdline_option_value(argc,argv,++i));
  2986   2998         if( pVfs ){
  2987   2999           sqlite3_vfs_register(pVfs, 1);
  2988   3000         }else{
  2989   3001           fprintf(stderr, "no such VFS: \"%s\"\n", argv[i]);
  2990   3002           exit(1);
................................................................................
  3071   3083   #ifdef SQLITE_ENABLE_VFSTRACE
  3072   3084       }else if( strcmp(z,"-vfstrace")==0 ){
  3073   3085         i++;
  3074   3086   #endif
  3075   3087   #ifdef SQLITE_ENABLE_MULTIPLEX
  3076   3088       }else if( strcmp(z,"-multiplex")==0 ){
  3077   3089         i++;
         3090  +#endif
         3091  +#ifdef SQLITE_ENABLE_SSDSIM
         3092  +    }else if( strcmp(z,"-ssdsim")==0 ){
         3093  +      i++;
  3078   3094   #endif
  3079   3095       }else if( strcmp(z,"-help")==0 ){
  3080   3096         usage(1);
  3081   3097       }else if( strcmp(z,"-cmd")==0 ){
  3082   3098         if( i==argc-1 ) break;
  3083   3099         z = cmdline_option_value(argc,argv,++i);
  3084   3100         if( z[0]=='.' ){

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.  Return non-zero if there is an error.
          172  +*/
          173  +static int ssdsimInit(void){
          174  +  int nPage;
          175  +  if( g.nPage ) return 0;
          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 1; }
          184  +  memset(g.apPage, 0, sizeof(g.apPage[0])*g.nPage);
          185  +  g.aDealloc = sqlite3_malloc( sizeof(g.aDealloc[0])*g.nPage );
          186  +  if( g.aDealloc==0 ){ ssdsimShutdown(); return 1; }
          187  +  g.nDealloc = 0;
          188  +  g.mxAlloc = 0;
          189  +  g.nHostWrite = 0;
          190  +  g.nNANDWrite = 0;
          191  +  return 0;
          192  +}
          193  +
          194  +/*
          195  +** Allocate a new, unused logical page number
          196  +*/
          197  +static int ssdsimCoreLpnAlloc(void){
          198  +  if( g.nDealloc ){
          199  +    return g.aDealloc[--g.nDealloc];
          200  +  }else if( g.mxAlloc>=g.nPage ){
          201  +    return -1;
          202  +  }else{
          203  +    return g.mxAlloc++;
          204  +  }
          205  +}
          206  +
          207  +/*
          208  +** Indicate that the content of a logical page will never again be
          209  +** read.
          210  +*/
          211  +static void ssdsimCoreTrim(int lpn){
          212  +}
          213  +
          214  +/*
          215  +** Deallocate a logical page number, indicating that it is no longer
          216  +** in use.
          217  +*/
          218  +static int ssdsimCoreLpnDealloc(int lpn){
          219  +  g.aDealloc[g.nDealloc++] = lpn;
          220  +}
          221  +
          222  +/*
          223  +** Translate a logical page number into a physical page number.
          224  +*/
          225  +static int ssdsimCoreLpnToPpn(int lpn, int writeFlag){
          226  +  int ppn = lpn;
          227  +  if( g.apPage[ppn]==0 ){
          228  +    if( writeFlag ){
          229  +      g.apPage[ppn] = sqlite3_malloc( g.szPage );
          230  +    }
          231  +    if( g.apPage[ppn]==0 ) ppn = -1;
          232  +  }
          233  +  return ppn;
          234  +}
          235  +
          236  +/*
          237  +** Indicate that a transaction boundary has occurred
          238  +*/
          239  +static int ssdsimCoreSync(void){
          240  +}
          241  +
          242  +
          243  +/*
          244  +** Truncate an inode
          245  +*/
          246  +static void ssdsimTruncateInode(ssdsim_inode *pInode, sqlite3_int64 size){
          247  +  if( pInode->len > size ){
          248  +    int nOld = pInode->len/g.szPage;
          249  +    int nNew = size/g.szPage;
          250  +    int i;
          251  +    for(i=nOld; i>nNew; i--){
          252  +      ssdsimCoreLpnDealloc(pInode->aiPage[i]);
          253  +    }
          254  +    pInode->len = size;
          255  +  }
          256  +}
          257  +
          258  +/*
          259  +** Delete an inode
          260  +*/
          261  +static void ssdsimDeleteInode(ssdsim_inode *pInode){
          262  +  if( pInode->pFiles ){
          263  +    pInode->inodeFlags |= SSDSIM_DELETEONCLOSE;
          264  +    return;
          265  +  }
          266  +  ssdsimTruncateInode(pInode, 0);
          267  +  sqlite3_free(pInode->apShm);
          268  +  sqlite3_free(pInode->aiPage);
          269  +  if( g.pInode==pInode ){
          270  +    g.pInode = pInode->pNext;
          271  +  }else{
          272  +    ssdsim_inode *pX;
          273  +    for(pX=g.pInode; pX && pX->pNext!=pInode; pX=pX->pNext){}
          274  +    if( pX ) pX->pNext = pInode->pNext;
          275  +  }
          276  +  sqlite3_free(pInode);
          277  +}
          278  +
          279  +
          280  +/*
          281  +** Close an ssdsim-file.
          282  +*/
          283  +static int ssdsimClose(sqlite3_file *pFile){
          284  +  ssdsim_file *p = (ssdsim_file *)pFile;
          285  +  int rc;
          286  +  ssdsim_inode *pInode = p->pInode;
          287  +  if( p==pInode->pFiles ){
          288  +    pInode->pFiles = p->pNext;
          289  +    if( (pInode->inodeFlags & SSDSIM_DELETEONCLOSE)!=0 ){
          290  +      ssdsimDeleteInode(pInode);
          291  +    }
          292  +  }else{
          293  +    ssdsim_file *pX;
          294  +    for(pX = pInode->pFiles; pX && pX->pNext!=p; pX=pX->pNext){}
          295  +    if( pX ) pX->pNext = p->pNext;
          296  +  }
          297  +  memset(p, 0, sizeof(*p));
          298  +  return SQLITE_OK;
          299  +}
          300  +
          301  +/*
          302  +** Read data from an ssdsim-file.
          303  +*/
          304  +static int ssdsimRead(
          305  +  sqlite3_file *pFile, 
          306  +  void *zBuf, 
          307  +  int iAmt, 
          308  +  sqlite_int64 iOfst
          309  +){
          310  +  ssdsim_file *p = (ssdsim_file *)pFile;
          311  +  ssdsim_inode *pInode = p->pInode;
          312  +  int rc = SQLITE_OK;
          313  +  int lpn, ppn, n, mx;
          314  +  unsigned char *pOut = (unsigned char*)zBuf;
          315  +  unsigned char *pContent;
          316  +  while( iAmt>0 ){
          317  +    if( iAmt+iOfst>pInode->len ){
          318  +      rc = SQLITE_IOERR_SHORT_READ;
          319  +      iAmt = pInode->len - iOfst;
          320  +      if( iAmt<=0 ) break;
          321  +    }
          322  +    lpn = pInode->aiPage[iOfst/g.szPage];
          323  +    ppn = ssdsimCoreLpnToPpn(lpn, 0);
          324  +    n = iAmt;
          325  +    mx = g.szPage - iOfst%g.szPage;
          326  +    if( n>mx ) n = mx;
          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-1)/g.szPage;
          363  +    nNew = (iOfst+iAmt+g.szPage-1)/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, mx;
          373  +    lpn = pInode->aiPage[iOfst/g.szPage];
          374  +    if( lpn<0 ){
          375  +      lpn = ssdsimCoreLpnAlloc();
          376  +      if( lpn<0 ) return SQLITE_FULL;
          377  +    }
          378  +    ppn = ssdsimCoreLpnToPpn(lpn, 1);
          379  +    if( ppn<0 ) return SQLITE_NOMEM;
          380  +    n = iAmt;
          381  +    mx = g.szPage - iOfst%g.szPage;
          382  +    if( n>mx ) n = mx;
          383  +    pDest = g.apPage[ppn];
          384  +    memcpy(pDest, pIn, n);
          385  +    iOfst += n;
          386  +    iAmt -= n;
          387  +    pIn += n;
          388  +  }
          389  +  pInode->len = lenNew;
          390  +  return rc;
          391  +}
          392  +
          393  +/*
          394  +** Truncate an ssdsim-file.
          395  +*/
          396  +static int ssdsimTruncate(sqlite3_file *pFile, sqlite_int64 size){
          397  +  ssdsim_file *p = (ssdsim_file *)pFile;
          398  +  ssdsim_inode *pInode = p->pInode;
          399  +  ssdsimTruncateInode(pInode, size);
          400  +  return SQLITE_OK;
          401  +}
          402  +
          403  +/*
          404  +** Sync an ssdsim-file.
          405  +*/
          406  +static int ssdsimSync(sqlite3_file *pFile, int flags){
          407  +  ssdsim_file *p = (ssdsim_file *)pFile;
          408  +  ssdsim_inode *pInode = p->pInode;
          409  +  ssdsimCoreSync();
          410  +  return SQLITE_OK;
          411  +}
          412  +
          413  +/*
          414  +** Return the current file-size of an ssdsim-file.
          415  +*/
          416  +static int ssdsimFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
          417  +  ssdsim_file *p = (ssdsim_file *)pFile;
          418  +  ssdsim_inode *pInode = p->pInode;
          419  +  *pSize = pInode->len;
          420  +  return SQLITE_OK;
          421  +}
          422  +
          423  +/*
          424  +** Return the name of a lock.
          425  +*/
          426  +static const char *lockName(int eLock){
          427  +  const char *azLockNames[] = {
          428  +     "NONE", "SHARED", "RESERVED", "PENDING", "EXCLUSIVE"
          429  +  };
          430  +  if( eLock<0 || eLock>=sizeof(azLockNames)/sizeof(azLockNames[0]) ){
          431  +    return "???";
          432  +  }else{
          433  +    return azLockNames[eLock];
          434  +  }
          435  +}
          436  +
          437  +/*
          438  +** Lock an ssdsim-file.
          439  +*/
          440  +static int ssdsimLock(sqlite3_file *pFile, int eLock){
          441  +  ssdsim_file *p = (ssdsim_file *)pFile;
          442  +  ssdsim_inode *pInode = p->pInode;
          443  +  ssdsim_file *pF;
          444  +  int rc = SQLITE_OK;
          445  +  if( eLock==SQLITE_LOCK_SHARED ){
          446  +    for(pF=pInode->pFiles; pF; pF=pF->pNext){
          447  +      if( pF!=p && pF->eLock>=SQLITE_LOCK_PENDING ) return SQLITE_BUSY;
          448  +    }
          449  +  }else if( eLock>=SQLITE_LOCK_RESERVED ){
          450  +    for(pF=pInode->pFiles; pF; pF=pF->pNext){
          451  +      if( pF!=p && pF->eLock>=SQLITE_LOCK_RESERVED ) return SQLITE_BUSY;
          452  +    }
          453  +  }else if( eLock==SQLITE_LOCK_EXCLUSIVE ){
          454  +    for(pF=pInode->pFiles; pF; pF=pF->pNext){
          455  +      if( pF!=p && pF->eLock>=SQLITE_LOCK_SHARED ){
          456  +        eLock = SQLITE_LOCK_PENDING;
          457  +        rc = SQLITE_BUSY;
          458  +      }
          459  +    }
          460  +  }
          461  +  p->eLock = eLock;
          462  +  return rc;
          463  +}
          464  +
          465  +/*
          466  +** Unlock an ssdsim-file.
          467  +*/
          468  +static int ssdsimUnlock(sqlite3_file *pFile, int eLock){
          469  +  ssdsim_file *p = (ssdsim_file *)pFile;
          470  +  if( p->eLock>eLock ) p->eLock = eLock;
          471  +  return SQLITE_OK;
          472  +}
          473  +
          474  +/*
          475  +** Check if another file-handle holds a RESERVED lock on an ssdsim-file.
          476  +*/
          477  +static int ssdsimCheckReservedLock(sqlite3_file *pFile, int *pResOut){
          478  +  ssdsim_file *p = (ssdsim_file *)pFile;
          479  +  ssdsim_inode *pInode = p->pInode;
          480  +  ssdsim_file *pF;
          481  +  int rc = 0;
          482  +  for(pF=pInode->pFiles; pF; pF=pF->pNext){
          483  +    if( pF!=p && pF->eLock>=SQLITE_LOCK_RESERVED ){
          484  +      rc = 1;
          485  +      break;
          486  +    }
          487  +  }
          488  +  *pResOut = rc;
          489  +  return SQLITE_OK;
          490  +}
          491  +
          492  +/*
          493  +** File control method. For custom operations on an ssdsim-file.
          494  +*/
          495  +static int ssdsimFileControl(sqlite3_file *pFile, int op, void *pArg){
          496  +  ssdsim_file *p = (ssdsim_file *)pFile;
          497  +  ssdsim_inode *pInode = p->pInode;
          498  +  switch( op ){
          499  +    case SQLITE_FCNTL_LOCKSTATE: {
          500  +      *(int*)pArg = p->eLock;
          501  +      return SQLITE_OK;
          502  +    }
          503  +    case SQLITE_FCNTL_VFSNAME: {
          504  +      *(char**)pArg = sqlite3_mprintf("ssdsim");
          505  +      return SQLITE_OK;
          506  +    }
          507  +    case SQLITE_FCNTL_PRAGMA: {
          508  +#if 0
          509  +      const char *const* a = (const char*const*)pArg;
          510  +      sqlite3_snprintf(sizeof(zBuf), zBuf, "PRAGMA,[%s,%s]",a[1],a[2]);
          511  +      zOp = zBuf;
          512  +#endif
          513  +      break;
          514  +    }
          515  +    default: {
          516  +      break;
          517  +    }
          518  +  }
          519  +  return SQLITE_NOTFOUND;
          520  +}
          521  +
          522  +/*
          523  +** Return the sector-size in bytes for an ssdsim-file.
          524  +*/
          525  +static int ssdsimSectorSize(sqlite3_file *pFile){
          526  +  return g.szPage;
          527  +}
          528  +
          529  +/*
          530  +** Return the device characteristic flags supported by an ssdsim-file.
          531  +*/
          532  +static int ssdsimDeviceCharacteristics(sqlite3_file *pFile){
          533  +  return 
          534  +     SQLITE_IOCAP_ATOMIC |
          535  +     SQLITE_IOCAP_POWERSAFE_OVERWRITE |
          536  +     SQLITE_IOCAP_SAFE_APPEND |
          537  +     SQLITE_IOCAP_SEQUENTIAL |
          538  +     SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
          539  +     0;
          540  +}
          541  +
          542  +/*
          543  +** Shared-memory operations.
          544  +*/
          545  +static int ssdsimShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
          546  +  ssdsim_file *p = (ssdsim_file *)pFile;
          547  +  ssdsim_inode *pInode = p->pInode;
          548  +  ssdsim_file *pF;
          549  +  unsigned int lockMask = 0;
          550  +
          551  +  /* Constraints on the SQLite core: */
          552  +  assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK );
          553  +  assert( n>=1 );
          554  +  assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
          555  +       || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
          556  +       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
          557  +       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
          558  +  assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
          559  +
          560  +  /* Mask of bits involved in this lock */
          561  +  lockMask = (1<<(ofst+n)) - (1<<ofst);
          562  +
          563  +  /* The unlock case */
          564  +  if( flags & SQLITE_SHM_UNLOCK ){
          565  +    p->shmWriteLock &= ~lockMask;
          566  +    p->shmReadLock &= ~lockMask;
          567  +    return SQLITE_OK;
          568  +  }
          569  +
          570  +  /* The shared-lock case */
          571  +  if( flags & SQLITE_SHM_SHARED ){
          572  +    /* Disallow if any sibling (including ourself) holds an exclusive lock */
          573  +    for(pF=pInode->pFiles; pF; pF=pF->pNext){
          574  +      if( pF->shmWriteLock & lockMask ){
          575  +        return SQLITE_BUSY;
          576  +      }
          577  +    }
          578  +    p->shmReadLock |= lockMask;
          579  +    return SQLITE_OK;
          580  +  }
          581  +
          582  +  /* The rest of this procedure is the exclusive lock case */
          583  +  assert( flags & SQLITE_SHM_EXCLUSIVE );
          584  +
          585  +  /* Disallow an exclusive if any kind of lock is held by any other */
          586  +  for(pF=pInode->pFiles; pF; pF=pF->pNext){
          587  +    if( pF==p ) continue;
          588  +    if( (pF->shmWriteLock & lockMask)!=0 ){
          589  +      return SQLITE_BUSY;
          590  +    }
          591  +    if( (pF->shmReadLock & lockMask)!=0 ){
          592  +      return SQLITE_BUSY;
          593  +    }
          594  +  }
          595  +  p->shmWriteLock |= lockMask;
          596  +  return SQLITE_OK;
          597  +}
          598  +static int ssdsimShmMap(
          599  +  sqlite3_file *pFile, 
          600  +  int iRegion, 
          601  +  int szRegion, 
          602  +  int isWrite, 
          603  +  void volatile **pp
          604  +){
          605  +  ssdsim_file *p = (ssdsim_file *)pFile;
          606  +  ssdsim_inode *pInode = p->pInode;
          607  +  char **apShm;
          608  +  int i;
          609  +  if( p->shmOpen==0 ){
          610  +    p->shmOpen = 1;
          611  +    p->shmReadLock = 0;
          612  +    p->shmWriteLock = 0;
          613  +  }
          614  +  if( pInode->nShmRegion<=iRegion ){
          615  +    if( isWrite==0 ){
          616  +      *pp = 0;
          617  +      return SQLITE_OK;
          618  +    }
          619  +    apShm = sqlite3_realloc(pInode->apShm, 
          620  +                            (iRegion+1)*sizeof(pInode->apShm[0]));
          621  +    if( apShm==0 ) return SQLITE_NOMEM;
          622  +    pInode->apShm = apShm;
          623  +    for(i=pInode->nShmRegion; i<=iRegion; i++){
          624  +      apShm[i] = sqlite3_malloc(szRegion);
          625  +      if( apShm[i]==0 ) return SQLITE_NOMEM;
          626  +      memset(apShm[i], 0, szRegion);
          627  +      pInode->nShmRegion = i+1;
          628  +    }
          629  +    pInode->szShmRegion = szRegion;
          630  +  }
          631  +  *pp = pInode->apShm[iRegion];
          632  +  return SQLITE_OK;
          633  +}
          634  +static void ssdsimShmBarrier(sqlite3_file *pFile){
          635  +  /* noop */
          636  +}
          637  +static int ssdsimShmUnmap(sqlite3_file *pFile, int delFlag){
          638  +  ssdsim_file *p = (ssdsim_file *)pFile;
          639  +  ssdsim_inode *pInode = p->pInode;
          640  +  if( p->shmOpen ){
          641  +    ssdsim_file *pF;
          642  +    unsigned char shmOpen = 0;
          643  +    p->shmOpen = 0;
          644  +    for(pF=pInode->pFiles; pF; pF=pF->pNext) shmOpen |= pF->shmOpen;
          645  +    if( !shmOpen ){
          646  +      int i;
          647  +      for(i=0; i<pInode->nShmRegion; i++) sqlite3_free(pInode->apShm[i]);
          648  +      sqlite3_free(pInode->apShm);
          649  +      pInode->apShm = 0;
          650  +      pInode->nShmRegion = 0;
          651  +    }
          652  +  }
          653  +  return SQLITE_OK;
          654  +}
          655  +
          656  +static const sqlite3_io_methods ssdsim_io_methods = {
          657  +  /* iVersion               */ 3,
          658  +  /* xClose                 */ ssdsimClose,
          659  +  /* xRead                  */ ssdsimRead,
          660  +  /* xWrite                 */ ssdsimWrite,
          661  +  /* xTruncate              */ ssdsimTruncate,
          662  +  /* xSync                  */ ssdsimSync,
          663  +  /* xFileSize              */ ssdsimFileSize,
          664  +  /* xLock                  */ ssdsimLock,
          665  +  /* xUnlock                */ ssdsimUnlock,
          666  +  /* xCheckReservedLock     */ ssdsimCheckReservedLock,
          667  +  /* xFileControl           */ ssdsimFileControl,
          668  +  /* xSectorSize            */ ssdsimSectorSize,
          669  +  /* xDeviceCharacteristics */ ssdsimDeviceCharacteristics,
          670  +  /* xShmMap                */ ssdsimShmMap,
          671  +  /* xShmLock               */ ssdsimShmLock,
          672  +  /* xShmBarrier            */ ssdsimShmBarrier,
          673  +  /* xShmUnmap              */ ssdsimShmUnmap
          674  +};
          675  +
          676  +/*
          677  +** Find an inode given its name.
          678  +*/
          679  +static ssdsim_inode *ssdsimFindInode(const char *zName){
          680  +  ssdsim_inode *pInode;
          681  +  for(pInode=g.pInode; pInode; pInode=pInode->pNext){
          682  +    if( strcmp(pInode->zPath, zName)==0 ) break;
          683  +  }
          684  +  return pInode;
          685  +}
          686  +
          687  +/*
          688  +** Open an ssdsim file handle.
          689  +*/
          690  +static int ssdsimOpen(
          691  +  sqlite3_vfs *pVfs,
          692  +  const char *zName,
          693  +  sqlite3_file *pFile,
          694  +  int flags,
          695  +  int *pOutFlags
          696  +){
          697  +  int rc;
          698  +  ssdsim_file *p = (ssdsim_file *)pFile;
          699  +  ssdsim_inode *pInode;
          700  +  char zTempname[100];
          701  +
          702  +  if( ssdsimInit() ) return SQLITE_CANTOPEN;
          703  +  if( zName==0 ){
          704  +    sqlite3_uint64 r;
          705  +    sqlite3_randomness(sizeof(r), &r);
          706  +    sqlite3_snprintf(sizeof(zTempname), zTempname, "/tmp%llx", r);
          707  +    zName = zTempname;
          708  +  }
          709  +  pInode = ssdsimFindInode(zName);
          710  +  if( pInode==0 ){
          711  +    int n = (int)strlen(zName);
          712  +    pInode = sqlite3_malloc( sizeof(*pInode) + n + 1 );
          713  +    if( pInode==0 ) return SQLITE_NOMEM;
          714  +    memset(pInode, 0, sizeof(*pInode));
          715  +    pInode->pNext = g.pInode;
          716  +    g.pInode = pInode;
          717  +    pInode->zPath = (char*)&pInode[1];
          718  +    strcpy(pInode->zPath, zName);
          719  +    pInode->len = 0;
          720  +    pInode->aiPage = 0;
          721  +    pInode->pFiles = 0;
          722  +    pInode->inodeFlags = 0;
          723  +    pInode->nShmRegion = 0;
          724  +    pInode->szShmRegion = 0;
          725  +    pInode->apShm = 0;
          726  +    if( flags & SQLITE_OPEN_DELETEONCLOSE ){
          727  +      pInode->inodeFlags |= SSDSIM_DELETEONCLOSE;
          728  +    }
          729  +  }
          730  +  p->pInode = pInode;
          731  +  p->pNext = pInode->pFiles;
          732  +  pInode->pFiles = p;
          733  +  p->eLock = 0;
          734  +  p->shmOpen = 0;
          735  +  p->shmReadLock = 0;
          736  +  p->shmWriteLock = 0;
          737  +  p->openFlags = flags;
          738  +  p->base.pMethods = &ssdsim_io_methods;
          739  +  return SQLITE_OK;
          740  +}
          741  +
          742  +/*
          743  +** Delete the file located at zPath. If the dirSync argument is true,
          744  +** ensure the file-system modifications are synced to disk before
          745  +** returning.
          746  +*/
          747  +static int ssdsimDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
          748  +  ssdsim_inode *pInode;
          749  +
          750  +  if( ssdsimInit() ) return SQLITE_CANTOPEN;
          751  +  pInode = ssdsimFindInode(zPath);
          752  +  if( pInode ) ssdsimDeleteInode(pInode);
          753  +  return SQLITE_OK;
          754  +}
          755  +
          756  +/*
          757  +** Test for access permissions. Return true if the requested permission
          758  +** is available, or false otherwise.
          759  +*/
          760  +static int ssdsimAccess(
          761  +  sqlite3_vfs *pVfs, 
          762  +  const char *zPath, 
          763  +  int flags, 
          764  +  int *pResOut
          765  +){
          766  +  ssdsim_inode *pInode;
          767  +  if( ssdsimInit() ) return SQLITE_CANTOPEN;
          768  +  pInode = ssdsimFindInode(zPath);
          769  +  *pResOut = pInode!=0;
          770  +  return SQLITE_OK;
          771  +}
          772  +
          773  +/*
          774  +** Populate buffer zOut with the full canonical pathname corresponding
          775  +** to the pathname in zPath. zOut is guaranteed to point to a buffer
          776  +** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
          777  +*/
          778  +static int ssdsimFullPathname(
          779  +  sqlite3_vfs *pVfs, 
          780  +  const char *zPath, 
          781  +  int nOut, 
          782  +  char *zOut
          783  +){
          784  +  while( zPath[0]=='/' ) zPath++;
          785  +  sqlite3_snprintf(nOut, zOut, "/%s", zPath);
          786  +  return SQLITE_OK;
          787  +}
          788  +
          789  +/*
          790  +** Open the dynamic library located at zPath and return a handle.
          791  +*/
          792  +static void *ssdsimDlOpen(sqlite3_vfs *pVfs, const char *zPath){
          793  +  return 0;
          794  +}
          795  +
          796  +/*
          797  +** Populate the buffer zErrMsg (size nByte bytes) with a human readable
          798  +** utf-8 string describing the most recent error encountered associated 
          799  +** with dynamic libraries.
          800  +*/
          801  +static void ssdsimDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
          802  +  sqlite3_snprintf(nByte, zErrMsg, "not supported by this VFS");
          803  +}
          804  +
          805  +/*
          806  +** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
          807  +*/
          808  +static void (*ssdsimDlSym(sqlite3_vfs *pVfs,void *p,const char *zSym))(void){
          809  +  return 0;
          810  +}
          811  +
          812  +/*
          813  +** Close the dynamic library handle pHandle.
          814  +*/
          815  +static void ssdsimDlClose(sqlite3_vfs *pVfs, void *pHandle){
          816  +}
          817  +
          818  +/*
          819  +** Populate the buffer pointed to by zBufOut with nByte bytes of 
          820  +** random data.
          821  +*/
          822  +static int ssdsimRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
          823  +  return g.pBase->xRandomness(g.pBase, nByte, zBufOut);
          824  +}
          825  +
          826  +/*
          827  +** Sleep for nMicro microseconds. Return the number of microseconds 
          828  +** actually slept.
          829  +*/
          830  +static int ssdsimSleep(sqlite3_vfs *pVfs, int nMicro){
          831  +  return g.pBase->xSleep(g.pBase, nMicro);
          832  +}
          833  +
          834  +/*
          835  +** Return the current time as a Julian Day number in *pTimeOut.
          836  +*/
          837  +static int ssdsimCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
          838  +  return g.pBase->xCurrentTime(g.pBase, pTimeOut);
          839  +}
          840  +static int ssdsimCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
          841  +  return g.pBase->xCurrentTimeInt64(g.pBase, pTimeOut);
          842  +}
          843  +
          844  +/*
          845  +** Return th3 emost recent error code and message
          846  +*/
          847  +static int ssdsimGetLastError(sqlite3_vfs *pVfs, int iErr, char *zErr){
          848  +  return SQLITE_OK;
          849  +}
          850  +
          851  +/*
          852  +** Override system calls.
          853  +*/
          854  +static int ssdsimSetSystemCall(
          855  +  sqlite3_vfs *pVfs,
          856  +  const char *zName,
          857  +  sqlite3_syscall_ptr pFunc
          858  +){
          859  +  return SQLITE_NOTFOUND;
          860  +}
          861  +static sqlite3_syscall_ptr ssdsimGetSystemCall(
          862  +  sqlite3_vfs *pVfs,
          863  +  const char *zName
          864  +){
          865  +  return 0;
          866  +}
          867  +static const char *ssdsimNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
          868  +  return 0;
          869  +}
          870  +
          871  +static sqlite3_vfs ssdsim_vfs = {
          872  +  /* iVersion          */ 3,
          873  +  /* szOsFile          */ sizeof(ssdsim_file),
          874  +  /* mxPathname        */ 1024,
          875  +  /* pNext             */ 0,
          876  +  /* zName             */ "ssdsim",
          877  +  /* pAppData          */ 0,
          878  +  /* xOpen             */ ssdsimOpen,
          879  +  /* xDelete           */ ssdsimDelete,
          880  +  /* xAccess           */ ssdsimAccess,
          881  +  /* xFullPathname     */ ssdsimFullPathname,
          882  +  /* xDlOpen           */ ssdsimDlOpen,
          883  +  /* xDlError          */ ssdsimDlError,
          884  +  /* xDlSym            */ ssdsimDlSym,
          885  +  /* xDlClose          */ ssdsimDlClose,
          886  +  /* xRandomness       */ ssdsimRandomness,
          887  +  /* xSleep            */ ssdsimSleep,
          888  +  /* xCurrentTime      */ ssdsimCurrentTime,
          889  +  /* xGetLastError     */ ssdsimGetLastError,
          890  +  /* xCurrentTimeInt64 */ ssdsimCurrentTimeInt64,
          891  +  /* xSetSystemCall    */ ssdsimSetSystemCall,
          892  +  /* xGetSystemCall    */ ssdsimGetSystemCall,
          893  +  /* xNextSystemCall   */ ssdsimNextSystemCall
          894  +};
          895  +
          896  +/*
          897  +** Clients invoke this routine to register the SSD simulator
          898  +*/
          899  +int ssdsim_register(
          900  +   const char *zBaseName,          /* Name of the underlying VFS */
          901  +   const char *zParams,            /* Configuration parameter */
          902  +   int makeDefault                 /* True to make the new VFS the default */
          903  +){
          904  +  sqlite3_vfs *pNew;
          905  +  sqlite3_vfs *pRoot;
          906  +
          907  +  if( g.pBase ) return SQLITE_ERROR;
          908  +  g.pBase = sqlite3_vfs_find(zBaseName);
          909  +  if( g.pBase==0 ) return SQLITE_NOTFOUND;
          910  +  return sqlite3_vfs_register(&ssdsim_vfs, makeDefault);
          911  +}
          912  +
          913  +/*
          914  +** Clients invoke this routine to get SSD simulator write-amplification
          915  +** statistics.
          916  +*/
          917  +void ssdsim_report(FILE *pOut, int reportNum){
          918  +  fprintf(pOut, "host page writes...... %9d\n", g.nHostWrite);
          919  +  fprintf(pOut, "NAND page writes...... %9d\n", g.nNANDWrite);
          920  +  if( g.nHostWrite>0 ){
          921  +    fprintf(pOut, "write amplification... %11.2f\n",
          922  +            (double)g.nNANDWrite/(double)g.nHostWrite);
          923  +  }
          924  +}