/* ** 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); } }