Index: src/shell.c ================================================================== --- src/shell.c +++ src/shell.c @@ -1014,10 +1014,14 @@ memcpy(zErrMsg, sqlite3_errmsg(db), nErrMsg); } return zErrMsg; } +#ifdef SQLITE_ENABLE_SSDSIM +extern void ssdsim_report(FILE*, int); +#endif + /* ** Display memory stats. */ static int display_stats( sqlite3 *db, /* Database to query */ @@ -2098,10 +2102,13 @@ strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1); } }else if( c=='q' && strncmp(azArg[0], "quit", n)==0 && nArg==1 ){ +#ifdef SQLITE_ENABLE_SSDSIM + ssdsim_report(p->out, 0); +#endif rc = 2; }else if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 && nArg==2 ){ FILE *alt = fopen(azArg[1], "rb"); @@ -2979,10 +2986,15 @@ #ifdef SQLITE_ENABLE_MULTIPLEX }else if( strcmp(z,"-multiplex")==0 ){ extern int sqlite3_multiple_initialize(const char*,int); sqlite3_multiplex_initialize(0, 1); #endif +#ifdef SQLITE_ENABLE_SSDSIM + }else if( strcmp(z, "-ssdsim")==0 ){ + extern int ssdsim_register(const char*,const char*,int); + ssdsim_register(0, cmdline_option_value(argc,argv,++i), 1); +#endif }else if( strcmp(z,"-vfs")==0 ){ sqlite3_vfs *pVfs = sqlite3_vfs_find(cmdline_option_value(argc,argv,++i)); if( pVfs ){ sqlite3_vfs_register(pVfs, 1); }else{ @@ -3073,10 +3085,14 @@ i++; #endif #ifdef SQLITE_ENABLE_MULTIPLEX }else if( strcmp(z,"-multiplex")==0 ){ i++; +#endif +#ifdef SQLITE_ENABLE_SSDSIM + }else if( strcmp(z,"-ssdsim")==0 ){ + i++; #endif }else if( strcmp(z,"-help")==0 ){ usage(1); }else if( strcmp(z,"-cmd")==0 ){ if( i==argc-1 ) break; ADDED src/test_ssdsim.c Index: src/test_ssdsim.c ================================================================== --- /dev/null +++ src/test_ssdsim.c @@ -0,0 +1,924 @@ +/* +** 2012 October 23 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains code implements a VFS shim that attempts to simulate +** a NAND-flash SSD in order to estimate the Write Amplification Factor +** (WAF) for a typical SQLite workload. +** +** This simulator is single-threaded, for simplicity. +** +** USAGE: +** +** This source file exports a single symbol which is the name of a +** function: +** +** int ssdsim_register( +** const char *zBaseVfsName, // Name of the underlying real VFS +** int makeDefault // Make the new VFS the default +** ); +*/ +#include +#include +#include +#include +#include "sqlite3.h" + +/* Forward declaration of objects */ +typedef struct ssdsim_state ssdsim_state; +typedef struct ssdsim_inode ssdsim_inode; +typedef struct ssdsim_file ssdsim_file; + +/* +** Each file on disk +*/ +struct ssdsim_inode { + ssdsim_inode *pNext; /* Next inode in a list of them all */ + char *zPath; /* Full pathname of the file */ + sqlite3_int64 len; /* Size of the file in bytes */ + int *aiPage; /* Array of logical page numbers */ + ssdsim_file *pFiles; /* List of open file descriptors */ + int inodeFlags; /* SSDSIM_* flags */ + int nShmRegion; /* Number of allocated shared memory regions */ + int szShmRegion; /* Size of each shared-memory region */ + char **apShm; /* Shared memory regions */ +}; + +#define SSDSIM_DELETEONCLOSE 0x0001 + +/* +** Each open file +*/ +struct ssdsim_file { + sqlite3_file base; /* Base class. Must be first */ + ssdsim_file *pNext; /* Next opening of the same inode */ + ssdsim_inode *pInode; /* The file */ + signed char eLock; /* Lock state for this connection */ + unsigned char shmOpen; /* True if SHM is open */ + unsigned short shmReadLock; /* Shared locks held by the shared memory */ + unsigned short shmWriteLock; /* Exclusive locks held by the shared memory */ + int openFlags; /* xOpen() flags used to open this connection */ +}; + +/* +** Page status values +*/ +#define SSDSIM_FREE 0 +#define SSDSIM_WRITTEN 1 +#define SSDSIM_OBSOLETE 2 + +/* +** Global state of the SSD simulator +*/ +struct ssdsim_state { + int szPage; /* Size of each page in bytes */ + int szEBlock; /* Size of an erase block in bytes */ + sqlite3_int64 szDisk; /* Total disk space in bytes */ + int nPage; /* Number of slots allocated in apPage[] */ + int nEBlock; /* Nubmer of erase blocks */ + int nDealloc; /* Number of reusable logical page numbers */ + int mxAlloc; /* Maximum allocated logical page number */ + unsigned char **apPage; /* Memory to hold physical pages */ + int *aDealloc; /* Array of reuseable logical page numbers */ + int *pageMap; /* Mapping from logical to physical pages */ + unsigned char *eStat; /* Status of each page */ + unsigned int *nErase; /* Number of erasures for each erase block */ + ssdsim_inode *pInode; /* List of all inodes */ + int traceFlag; /* True to trace operation */ + int nHostWrite; /* Number of pages written by the application */ + int nNANDWrite; /* Number of pages written to NAND-flash */ + sqlite3_vfs *pBase; /* True underlying VFS */ +}; +static ssdsim_state g; + +/* +** Method declarations for ssdsim_file. +*/ +static int ssdsimClose(sqlite3_file*); +static int ssdsimRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); +static int ssdsimWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64); +static int ssdsimTruncate(sqlite3_file*, sqlite3_int64 size); +static int ssdsimSync(sqlite3_file*, int flags); +static int ssdsimFileSize(sqlite3_file*, sqlite3_int64 *pSize); +static int ssdsimLock(sqlite3_file*, int); +static int ssdsimUnlock(sqlite3_file*, int); +static int ssdsimCheckReservedLock(sqlite3_file*, int *); +static int ssdsimFileControl(sqlite3_file*, int op, void *pArg); +static int ssdsimSectorSize(sqlite3_file*); +static int ssdsimDeviceCharacteristics(sqlite3_file*); +static int ssdsimShmLock(sqlite3_file*,int,int,int); +static int ssdsimShmMap(sqlite3_file*,int,int,int, void volatile **); +static void ssdsimShmBarrier(sqlite3_file*); +static int ssdsimShmUnmap(sqlite3_file*,int); + +/* +** Method declarations for ssdsim_vfs. +*/ +static int ssdsimOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); +static int ssdsimDelete(sqlite3_vfs*, const char *zName, int syncDir); +static int ssdsimAccess(sqlite3_vfs*, const char *zName, int flags, int *); +static int ssdsimFullPathname(sqlite3_vfs*, const char *zName, int, char *); +static void *ssdsimDlOpen(sqlite3_vfs*, const char *zFilename); +static void ssdsimDlError(sqlite3_vfs*, int nByte, char *zErrMsg); +static void (*ssdsimDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void); +static void ssdsimDlClose(sqlite3_vfs*, void*); +static int ssdsimRandomness(sqlite3_vfs*, int nByte, char *zOut); +static int ssdsimSleep(sqlite3_vfs*, int microseconds); +static int ssdsimCurrentTime(sqlite3_vfs*, double*); +static int ssdsimGetLastError(sqlite3_vfs*, int, char*); +static int ssdsimCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); +static int ssdsimSetSystemCall(sqlite3_vfs*,const char*, sqlite3_syscall_ptr); +static sqlite3_syscall_ptr ssdsimGetSystemCall(sqlite3_vfs*, const char *); +static const char *ssdsimNextSystemCall(sqlite3_vfs*, const char *zName); + +/* +** Trace operation. +*/ +static void ssdsimTrace(const char *zFormat, ...){ + if( g.traceFlag ){ + va_list ap; + char *zMsg; + va_start(ap, zFormat); + vprintf(zFormat, ap); + va_end(ap); + } +} + +/* +** Clear all memory associated with the ssd simulator +*/ +static void ssdsimShutdown(void){ + int i; + for(i=0; i=g.nPage ){ + return -1; + }else{ + return g.mxAlloc++; + } +} + +/* +** Indicate that the content of a logical page will never again be +** read. +*/ +static void ssdsimCoreTrim(int lpn){ +} + +/* +** Deallocate a logical page number, indicating that it is no longer +** in use. +*/ +static int ssdsimCoreLpnDealloc(int lpn){ + g.aDealloc[g.nDealloc++] = lpn; +} + +/* +** Translate a logical page number into a physical page number. +*/ +static int ssdsimCoreLpnToPpn(int lpn, int writeFlag){ + int ppn = lpn; + if( g.apPage[ppn]==0 ){ + if( writeFlag ){ + g.apPage[ppn] = sqlite3_malloc( g.szPage ); + } + if( g.apPage[ppn]==0 ) ppn = -1; + } + return ppn; +} + +/* +** Indicate that a transaction boundary has occurred +*/ +static int ssdsimCoreSync(void){ +} + + +/* +** Truncate an inode +*/ +static void ssdsimTruncateInode(ssdsim_inode *pInode, sqlite3_int64 size){ + if( pInode->len > size ){ + int nOld = pInode->len/g.szPage; + int nNew = size/g.szPage; + int i; + for(i=nOld; i>nNew; i--){ + ssdsimCoreLpnDealloc(pInode->aiPage[i]); + } + pInode->len = size; + } +} + +/* +** Delete an inode +*/ +static void ssdsimDeleteInode(ssdsim_inode *pInode){ + if( pInode->pFiles ){ + pInode->inodeFlags |= SSDSIM_DELETEONCLOSE; + return; + } + ssdsimTruncateInode(pInode, 0); + sqlite3_free(pInode->apShm); + sqlite3_free(pInode->aiPage); + if( g.pInode==pInode ){ + g.pInode = pInode->pNext; + }else{ + ssdsim_inode *pX; + for(pX=g.pInode; pX && pX->pNext!=pInode; pX=pX->pNext){} + if( pX ) pX->pNext = pInode->pNext; + } + sqlite3_free(pInode); +} + + +/* +** Close an ssdsim-file. +*/ +static int ssdsimClose(sqlite3_file *pFile){ + ssdsim_file *p = (ssdsim_file *)pFile; + int rc; + ssdsim_inode *pInode = p->pInode; + if( p==pInode->pFiles ){ + pInode->pFiles = p->pNext; + if( (pInode->inodeFlags & SSDSIM_DELETEONCLOSE)!=0 ){ + ssdsimDeleteInode(pInode); + } + }else{ + ssdsim_file *pX; + for(pX = pInode->pFiles; pX && pX->pNext!=p; pX=pX->pNext){} + if( pX ) pX->pNext = p->pNext; + } + memset(p, 0, sizeof(*p)); + return SQLITE_OK; +} + +/* +** Read data from an ssdsim-file. +*/ +static int ssdsimRead( + sqlite3_file *pFile, + void *zBuf, + int iAmt, + sqlite_int64 iOfst +){ + ssdsim_file *p = (ssdsim_file *)pFile; + ssdsim_inode *pInode = p->pInode; + int rc = SQLITE_OK; + int lpn, ppn, n, mx; + unsigned char *pOut = (unsigned char*)zBuf; + unsigned char *pContent; + while( iAmt>0 ){ + if( iAmt+iOfst>pInode->len ){ + rc = SQLITE_IOERR_SHORT_READ; + iAmt = pInode->len - iOfst; + if( iAmt<=0 ) break; + } + lpn = pInode->aiPage[iOfst/g.szPage]; + ppn = ssdsimCoreLpnToPpn(lpn, 0); + n = iAmt; + mx = g.szPage - iOfst%g.szPage; + if( n>mx ) n = mx; + if( ppn>=0 && ppnpInode; + int rc = SQLITE_OK; + int pn, lpn, ppn; + sqlite3_int64 lenNew; + const unsigned char *pIn = (const unsigned char*)zBuf; + unsigned char *pDest; + + lenNew = iOfst+iAmt; + if( lenNew <= pInode->len ){ + lenNew = pInode->len; + }else{ + int nOld, nNew; + int *aiPage; + nOld = (pInode->len+g.szPage-1)/g.szPage; + nNew = (iOfst+iAmt+g.szPage-1)/g.szPage; + if( nOldaiPage, nNew*sizeof(int)); + if( aiPage==0 ) return SQLITE_NOMEM; + memset(aiPage+nOld, 0xff, sizeof(int)*(nNew - nOld)); + pInode->aiPage = aiPage; + } + } + while( iAmt>0 ){ + int n, mx; + lpn = pInode->aiPage[iOfst/g.szPage]; + if( lpn<0 ){ + lpn = ssdsimCoreLpnAlloc(); + if( lpn<0 ) return SQLITE_FULL; + } + ppn = ssdsimCoreLpnToPpn(lpn, 1); + if( ppn<0 ) return SQLITE_NOMEM; + n = iAmt; + mx = g.szPage - iOfst%g.szPage; + if( n>mx ) n = mx; + pDest = g.apPage[ppn]; + memcpy(pDest, pIn, n); + iOfst += n; + iAmt -= n; + pIn += n; + } + pInode->len = lenNew; + return rc; +} + +/* +** Truncate an ssdsim-file. +*/ +static int ssdsimTruncate(sqlite3_file *pFile, sqlite_int64 size){ + ssdsim_file *p = (ssdsim_file *)pFile; + ssdsim_inode *pInode = p->pInode; + ssdsimTruncateInode(pInode, size); + return SQLITE_OK; +} + +/* +** Sync an ssdsim-file. +*/ +static int ssdsimSync(sqlite3_file *pFile, int flags){ + ssdsim_file *p = (ssdsim_file *)pFile; + ssdsim_inode *pInode = p->pInode; + ssdsimCoreSync(); + return SQLITE_OK; +} + +/* +** Return the current file-size of an ssdsim-file. +*/ +static int ssdsimFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ + ssdsim_file *p = (ssdsim_file *)pFile; + ssdsim_inode *pInode = p->pInode; + *pSize = pInode->len; + return SQLITE_OK; +} + +/* +** Return the name of a lock. +*/ +static const char *lockName(int eLock){ + const char *azLockNames[] = { + "NONE", "SHARED", "RESERVED", "PENDING", "EXCLUSIVE" + }; + if( eLock<0 || eLock>=sizeof(azLockNames)/sizeof(azLockNames[0]) ){ + return "???"; + }else{ + return azLockNames[eLock]; + } +} + +/* +** Lock an ssdsim-file. +*/ +static int ssdsimLock(sqlite3_file *pFile, int eLock){ + ssdsim_file *p = (ssdsim_file *)pFile; + ssdsim_inode *pInode = p->pInode; + ssdsim_file *pF; + int rc = SQLITE_OK; + if( eLock==SQLITE_LOCK_SHARED ){ + for(pF=pInode->pFiles; pF; pF=pF->pNext){ + if( pF!=p && pF->eLock>=SQLITE_LOCK_PENDING ) return SQLITE_BUSY; + } + }else if( eLock>=SQLITE_LOCK_RESERVED ){ + for(pF=pInode->pFiles; pF; pF=pF->pNext){ + if( pF!=p && pF->eLock>=SQLITE_LOCK_RESERVED ) return SQLITE_BUSY; + } + }else if( eLock==SQLITE_LOCK_EXCLUSIVE ){ + for(pF=pInode->pFiles; pF; pF=pF->pNext){ + if( pF!=p && pF->eLock>=SQLITE_LOCK_SHARED ){ + eLock = SQLITE_LOCK_PENDING; + rc = SQLITE_BUSY; + } + } + } + p->eLock = eLock; + return rc; +} + +/* +** Unlock an ssdsim-file. +*/ +static int ssdsimUnlock(sqlite3_file *pFile, int eLock){ + ssdsim_file *p = (ssdsim_file *)pFile; + if( p->eLock>eLock ) p->eLock = eLock; + return SQLITE_OK; +} + +/* +** Check if another file-handle holds a RESERVED lock on an ssdsim-file. +*/ +static int ssdsimCheckReservedLock(sqlite3_file *pFile, int *pResOut){ + ssdsim_file *p = (ssdsim_file *)pFile; + ssdsim_inode *pInode = p->pInode; + ssdsim_file *pF; + int rc = 0; + for(pF=pInode->pFiles; pF; pF=pF->pNext){ + if( pF!=p && pF->eLock>=SQLITE_LOCK_RESERVED ){ + rc = 1; + break; + } + } + *pResOut = rc; + return SQLITE_OK; +} + +/* +** File control method. For custom operations on an ssdsim-file. +*/ +static int ssdsimFileControl(sqlite3_file *pFile, int op, void *pArg){ + ssdsim_file *p = (ssdsim_file *)pFile; + ssdsim_inode *pInode = p->pInode; + switch( op ){ + case SQLITE_FCNTL_LOCKSTATE: { + *(int*)pArg = p->eLock; + return SQLITE_OK; + } + case SQLITE_FCNTL_VFSNAME: { + *(char**)pArg = sqlite3_mprintf("ssdsim"); + return SQLITE_OK; + } + case SQLITE_FCNTL_PRAGMA: { +#if 0 + const char *const* a = (const char*const*)pArg; + sqlite3_snprintf(sizeof(zBuf), zBuf, "PRAGMA,[%s,%s]",a[1],a[2]); + zOp = zBuf; +#endif + break; + } + default: { + break; + } + } + return SQLITE_NOTFOUND; +} + +/* +** Return the sector-size in bytes for an ssdsim-file. +*/ +static int ssdsimSectorSize(sqlite3_file *pFile){ + return g.szPage; +} + +/* +** Return the device characteristic flags supported by an ssdsim-file. +*/ +static int ssdsimDeviceCharacteristics(sqlite3_file *pFile){ + return + SQLITE_IOCAP_ATOMIC | + SQLITE_IOCAP_POWERSAFE_OVERWRITE | + SQLITE_IOCAP_SAFE_APPEND | + SQLITE_IOCAP_SEQUENTIAL | + SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | + 0; +} + +/* +** Shared-memory operations. +*/ +static int ssdsimShmLock(sqlite3_file *pFile, int ofst, int n, int flags){ + ssdsim_file *p = (ssdsim_file *)pFile; + ssdsim_inode *pInode = p->pInode; + ssdsim_file *pF; + unsigned int lockMask = 0; + + /* Constraints on the SQLite core: */ + assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); + assert( n>=1 ); + assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) + || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); + assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); + + /* Mask of bits involved in this lock */ + lockMask = (1<<(ofst+n)) - (1<shmWriteLock &= ~lockMask; + p->shmReadLock &= ~lockMask; + return SQLITE_OK; + } + + /* The shared-lock case */ + if( flags & SQLITE_SHM_SHARED ){ + /* Disallow if any sibling (including ourself) holds an exclusive lock */ + for(pF=pInode->pFiles; pF; pF=pF->pNext){ + if( pF->shmWriteLock & lockMask ){ + return SQLITE_BUSY; + } + } + p->shmReadLock |= lockMask; + return SQLITE_OK; + } + + /* The rest of this procedure is the exclusive lock case */ + assert( flags & SQLITE_SHM_EXCLUSIVE ); + + /* Disallow an exclusive if any kind of lock is held by any other */ + for(pF=pInode->pFiles; pF; pF=pF->pNext){ + if( pF==p ) continue; + if( (pF->shmWriteLock & lockMask)!=0 ){ + return SQLITE_BUSY; + } + if( (pF->shmReadLock & lockMask)!=0 ){ + return SQLITE_BUSY; + } + } + p->shmWriteLock |= lockMask; + return SQLITE_OK; +} +static int ssdsimShmMap( + sqlite3_file *pFile, + int iRegion, + int szRegion, + int isWrite, + void volatile **pp +){ + ssdsim_file *p = (ssdsim_file *)pFile; + ssdsim_inode *pInode = p->pInode; + char **apShm; + int i; + if( p->shmOpen==0 ){ + p->shmOpen = 1; + p->shmReadLock = 0; + p->shmWriteLock = 0; + } + if( pInode->nShmRegion<=iRegion ){ + if( isWrite==0 ){ + *pp = 0; + return SQLITE_OK; + } + apShm = sqlite3_realloc(pInode->apShm, + (iRegion+1)*sizeof(pInode->apShm[0])); + if( apShm==0 ) return SQLITE_NOMEM; + pInode->apShm = apShm; + for(i=pInode->nShmRegion; i<=iRegion; i++){ + apShm[i] = sqlite3_malloc(szRegion); + if( apShm[i]==0 ) return SQLITE_NOMEM; + memset(apShm[i], 0, szRegion); + pInode->nShmRegion = i+1; + } + pInode->szShmRegion = szRegion; + } + *pp = pInode->apShm[iRegion]; + return SQLITE_OK; +} +static void ssdsimShmBarrier(sqlite3_file *pFile){ + /* noop */ +} +static int ssdsimShmUnmap(sqlite3_file *pFile, int delFlag){ + ssdsim_file *p = (ssdsim_file *)pFile; + ssdsim_inode *pInode = p->pInode; + if( p->shmOpen ){ + ssdsim_file *pF; + unsigned char shmOpen = 0; + p->shmOpen = 0; + for(pF=pInode->pFiles; pF; pF=pF->pNext) shmOpen |= pF->shmOpen; + if( !shmOpen ){ + int i; + for(i=0; inShmRegion; i++) sqlite3_free(pInode->apShm[i]); + sqlite3_free(pInode->apShm); + pInode->apShm = 0; + pInode->nShmRegion = 0; + } + } + return SQLITE_OK; +} + +static const sqlite3_io_methods ssdsim_io_methods = { + /* iVersion */ 3, + /* xClose */ ssdsimClose, + /* xRead */ ssdsimRead, + /* xWrite */ ssdsimWrite, + /* xTruncate */ ssdsimTruncate, + /* xSync */ ssdsimSync, + /* xFileSize */ ssdsimFileSize, + /* xLock */ ssdsimLock, + /* xUnlock */ ssdsimUnlock, + /* xCheckReservedLock */ ssdsimCheckReservedLock, + /* xFileControl */ ssdsimFileControl, + /* xSectorSize */ ssdsimSectorSize, + /* xDeviceCharacteristics */ ssdsimDeviceCharacteristics, + /* xShmMap */ ssdsimShmMap, + /* xShmLock */ ssdsimShmLock, + /* xShmBarrier */ ssdsimShmBarrier, + /* xShmUnmap */ ssdsimShmUnmap +}; + +/* +** Find an inode given its name. +*/ +static ssdsim_inode *ssdsimFindInode(const char *zName){ + ssdsim_inode *pInode; + for(pInode=g.pInode; pInode; pInode=pInode->pNext){ + if( strcmp(pInode->zPath, zName)==0 ) break; + } + return pInode; +} + +/* +** Open an ssdsim file handle. +*/ +static int ssdsimOpen( + sqlite3_vfs *pVfs, + const char *zName, + sqlite3_file *pFile, + int flags, + int *pOutFlags +){ + int rc; + ssdsim_file *p = (ssdsim_file *)pFile; + ssdsim_inode *pInode; + char zTempname[100]; + + if( ssdsimInit() ) return SQLITE_CANTOPEN; + if( zName==0 ){ + sqlite3_uint64 r; + sqlite3_randomness(sizeof(r), &r); + sqlite3_snprintf(sizeof(zTempname), zTempname, "/tmp%llx", r); + zName = zTempname; + } + pInode = ssdsimFindInode(zName); + if( pInode==0 ){ + int n = (int)strlen(zName); + pInode = sqlite3_malloc( sizeof(*pInode) + n + 1 ); + if( pInode==0 ) return SQLITE_NOMEM; + memset(pInode, 0, sizeof(*pInode)); + pInode->pNext = g.pInode; + g.pInode = pInode; + pInode->zPath = (char*)&pInode[1]; + strcpy(pInode->zPath, zName); + pInode->len = 0; + pInode->aiPage = 0; + pInode->pFiles = 0; + pInode->inodeFlags = 0; + pInode->nShmRegion = 0; + pInode->szShmRegion = 0; + pInode->apShm = 0; + if( flags & SQLITE_OPEN_DELETEONCLOSE ){ + pInode->inodeFlags |= SSDSIM_DELETEONCLOSE; + } + } + p->pInode = pInode; + p->pNext = pInode->pFiles; + pInode->pFiles = p; + p->eLock = 0; + p->shmOpen = 0; + p->shmReadLock = 0; + p->shmWriteLock = 0; + p->openFlags = flags; + p->base.pMethods = &ssdsim_io_methods; + return SQLITE_OK; +} + +/* +** Delete the file located at zPath. If the dirSync argument is true, +** ensure the file-system modifications are synced to disk before +** returning. +*/ +static int ssdsimDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ + ssdsim_inode *pInode; + + if( ssdsimInit() ) return SQLITE_CANTOPEN; + pInode = ssdsimFindInode(zPath); + if( pInode ) ssdsimDeleteInode(pInode); + return SQLITE_OK; +} + +/* +** Test for access permissions. Return true if the requested permission +** is available, or false otherwise. +*/ +static int ssdsimAccess( + sqlite3_vfs *pVfs, + const char *zPath, + int flags, + int *pResOut +){ + ssdsim_inode *pInode; + if( ssdsimInit() ) return SQLITE_CANTOPEN; + pInode = ssdsimFindInode(zPath); + *pResOut = pInode!=0; + return SQLITE_OK; +} + +/* +** Populate buffer zOut with the full canonical pathname corresponding +** to the pathname in zPath. zOut is guaranteed to point to a buffer +** of at least (DEVSYM_MAX_PATHNAME+1) bytes. +*/ +static int ssdsimFullPathname( + sqlite3_vfs *pVfs, + const char *zPath, + int nOut, + char *zOut +){ + while( zPath[0]=='/' ) zPath++; + sqlite3_snprintf(nOut, zOut, "/%s", zPath); + return SQLITE_OK; +} + +/* +** Open the dynamic library located at zPath and return a handle. +*/ +static void *ssdsimDlOpen(sqlite3_vfs *pVfs, const char *zPath){ + return 0; +} + +/* +** Populate the buffer zErrMsg (size nByte bytes) with a human readable +** utf-8 string describing the most recent error encountered associated +** with dynamic libraries. +*/ +static void ssdsimDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ + sqlite3_snprintf(nByte, zErrMsg, "not supported by this VFS"); +} + +/* +** Return a pointer to the symbol zSymbol in the dynamic library pHandle. +*/ +static void (*ssdsimDlSym(sqlite3_vfs *pVfs,void *p,const char *zSym))(void){ + return 0; +} + +/* +** Close the dynamic library handle pHandle. +*/ +static void ssdsimDlClose(sqlite3_vfs *pVfs, void *pHandle){ +} + +/* +** Populate the buffer pointed to by zBufOut with nByte bytes of +** random data. +*/ +static int ssdsimRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ + return g.pBase->xRandomness(g.pBase, nByte, zBufOut); +} + +/* +** Sleep for nMicro microseconds. Return the number of microseconds +** actually slept. +*/ +static int ssdsimSleep(sqlite3_vfs *pVfs, int nMicro){ + return g.pBase->xSleep(g.pBase, nMicro); +} + +/* +** Return the current time as a Julian Day number in *pTimeOut. +*/ +static int ssdsimCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ + return g.pBase->xCurrentTime(g.pBase, pTimeOut); +} +static int ssdsimCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ + return g.pBase->xCurrentTimeInt64(g.pBase, pTimeOut); +} + +/* +** Return th3 emost recent error code and message +*/ +static int ssdsimGetLastError(sqlite3_vfs *pVfs, int iErr, char *zErr){ + return SQLITE_OK; +} + +/* +** Override system calls. +*/ +static int ssdsimSetSystemCall( + sqlite3_vfs *pVfs, + const char *zName, + sqlite3_syscall_ptr pFunc +){ + return SQLITE_NOTFOUND; +} +static sqlite3_syscall_ptr ssdsimGetSystemCall( + sqlite3_vfs *pVfs, + const char *zName +){ + return 0; +} +static const char *ssdsimNextSystemCall(sqlite3_vfs *pVfs, const char *zName){ + return 0; +} + +static sqlite3_vfs ssdsim_vfs = { + /* iVersion */ 3, + /* szOsFile */ sizeof(ssdsim_file), + /* mxPathname */ 1024, + /* pNext */ 0, + /* zName */ "ssdsim", + /* pAppData */ 0, + /* xOpen */ ssdsimOpen, + /* xDelete */ ssdsimDelete, + /* xAccess */ ssdsimAccess, + /* xFullPathname */ ssdsimFullPathname, + /* xDlOpen */ ssdsimDlOpen, + /* xDlError */ ssdsimDlError, + /* xDlSym */ ssdsimDlSym, + /* xDlClose */ ssdsimDlClose, + /* xRandomness */ ssdsimRandomness, + /* xSleep */ ssdsimSleep, + /* xCurrentTime */ ssdsimCurrentTime, + /* xGetLastError */ ssdsimGetLastError, + /* xCurrentTimeInt64 */ ssdsimCurrentTimeInt64, + /* xSetSystemCall */ ssdsimSetSystemCall, + /* xGetSystemCall */ ssdsimGetSystemCall, + /* xNextSystemCall */ ssdsimNextSystemCall +}; + +/* +** Clients invoke this routine to register the SSD simulator +*/ +int ssdsim_register( + const char *zBaseName, /* Name of the underlying VFS */ + const char *zParams, /* Configuration parameter */ + int makeDefault /* True to make the new VFS the default */ +){ + sqlite3_vfs *pNew; + sqlite3_vfs *pRoot; + + if( g.pBase ) return SQLITE_ERROR; + g.pBase = sqlite3_vfs_find(zBaseName); + if( g.pBase==0 ) return SQLITE_NOTFOUND; + return sqlite3_vfs_register(&ssdsim_vfs, makeDefault); +} + +/* +** Clients invoke this routine to get SSD simulator write-amplification +** statistics. +*/ +void ssdsim_report(FILE *pOut, int reportNum){ + fprintf(pOut, "host page writes...... %9d\n", g.nHostWrite); + fprintf(pOut, "NAND page writes...... %9d\n", g.nNANDWrite); + if( g.nHostWrite>0 ){ + fprintf(pOut, "write amplification... %11.2f\n", + (double)g.nNANDWrite/(double)g.nHostWrite); + } +}