Compare commits

...

3 Commits

Author SHA1 Message Date
drh 79e5c91672 Further work on getting ssdsim to run. This is an incremental checkin to
save my place while jumping off to work on other things.

FossilOrigin-Name: ae2f1627b1672e511be1ff132f2a3fb8f8bb7169
2012-10-25 23:47:27 +00:00
drh 08882bc737 Merge the command-line shell enhancements from trunk. Other edits toward
trying to get ssdsim to run.

FossilOrigin-Name: 848f87e22fc6bfbbb7dbc7491c8a20cdd53b7420
2012-10-25 15:32:29 +00:00
drh 57c9c0a221 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.

FossilOrigin-Name: 9e6efcf05402cd0b259c9b2ae57e349800650bd7
2012-10-25 01:50:59 +00:00
4 changed files with 948 additions and 7 deletions
+7 -6
View File
@@ -1,5 +1,5 @@
C Improvements\sto\sthe\scommand-line\sargument\sparsing\sin\sthe\s\ncommand-line\sshell.\s\sCommand-line\soptions\scan\snow\soccur\seither\sbefore\nor\safter\sthe\sdatabase\sname\sand\sfirst\scommand\sand\sare\sstill\saccepted\sand\nprocessed.\s\sCommand-line\soptions\sare\sprocessed\seven\sif\sno\sdatabase\sname\nis\sgiven\s(and\s:memory:\sis\sassumed).
D 2012-10-25T15:23:14.369
C Further\swork\son\sgetting\sssdsim\sto\srun.\s\sThis\sis\san\sincremental\scheckin\sto\nsave\smy\splace\swhile\sjumping\soff\sto\swork\son\sother\sthings.
D 2012-10-25T23:47:27.493
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 5f4f26109f9d80829122e0e09f9cda008fa065fb
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -175,7 +175,7 @@ F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
F src/resolve.c 7b986a715ac281643309c29257bb58cfae7aa810
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
F src/select.c 9b759521f805e25e97baa2dd64037157fe365817
F src/shell.c d570e6cb3889eb07fdf15808a1b6b136c78e4da5
F src/shell.c 9e9af864536409a5ceb7e0b432f3e5b0c2cb9249
F src/sqlite.h.in c7be05ad191d2634292fcc77bdb2bcfa4526eb98
F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
@@ -221,6 +221,7 @@ F src/test_rtree.c aba603c949766c4193f1068b91c787f57274e0d9
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f
F src/test_spellfix.c 76dd8d3111d2f5354c374f71fa23b752bd0b029c
F src/test_ssdsim.c bb4fefe91b9e4d4e3e09488814cc586575590ad4
F src/test_stat.c d1569c7a4839f13e80187e2c26b2ab4da2d03935
F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
F src/test_syscall.c a992d8c80ea91fbf21fb2dd570db40e77dd7e6ae
@@ -1021,7 +1022,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
F tool/win/sqlite.vsix 67d8a99aceb56384a81b3f30d6c71743146d2cc9
P aaa2d9b0db74d8452d9294de17cff786ab4ec7c8
R 4204a6c0a11b862c569be1046625095c
P 848f87e22fc6bfbbb7dbc7491c8a20cdd53b7420
R b9ddcfef141182b6a9e7751152774567
U drh
Z 98d27aa00ef0fe7caf71f35a32885811
Z 1dcb4d96fc4b1f34fb139b8208725f7a
+1 -1
View File
@@ -1 +1 @@
317c80cba3688a97ade9cde622cc3bd94cf3436a
ae2f1627b1672e511be1ff132f2a3fb8f8bb7169
+16
View File
@@ -1016,6 +1016,10 @@ static char *save_err_msg(
return zErrMsg;
}
#ifdef SQLITE_ENABLE_SSDSIM
extern void ssdsim_report(FILE*, int);
#endif
/*
** Display memory stats.
*/
@@ -2100,6 +2104,9 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}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
@@ -2980,6 +2987,11 @@ int main(int argc, char **argv){
}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));
@@ -3075,6 +3087,10 @@ int main(int argc, char **argv){
#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);
+924
View File
@@ -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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#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; i++) sqlite3_free(g.apPage[i]);
sqlite3_free(g.apPage);
g.apPage = 0;
sqlite3_free(g.aDealloc);
g.aDealloc = 0;
g.nDealloc = 0;
g.mxAlloc = 0;
g.nPage = 0;
}
/*
** Initialize the ssdsim system. Return non-zero if there is an error.
*/
static int ssdsimInit(void){
int nPage;
if( g.nPage ) return 0;
if( g.szPage==0 ) g.szPage = 4096;
if( g.szEBlock==0 ) g.szEBlock = 262144;
if( g.szDisk==0 ) g.szDisk = 67108864;
g.nPage = g.szDisk/g.szPage;
g.nEBlock = g.szDisk/g.szEBlock;
sqlite3_free(g.apPage);
g.apPage = sqlite3_malloc( sizeof(g.apPage[0])*g.nPage );
if( g.apPage==0 ){ ssdsimShutdown(); return 1; }
memset(g.apPage, 0, sizeof(g.apPage[0])*g.nPage);
g.aDealloc = sqlite3_malloc( sizeof(g.aDealloc[0])*g.nPage );
if( g.aDealloc==0 ){ ssdsimShutdown(); return 1; }
g.nDealloc = 0;
g.mxAlloc = 0;
g.nHostWrite = 0;
g.nNANDWrite = 0;
return 0;
}
/*
** Allocate a new, unused logical page number
*/
static int ssdsimCoreLpnAlloc(void){
if( g.nDealloc ){
return g.aDealloc[--g.nDealloc];
}else if( g.mxAlloc>=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 && ppn<g.nPage && (pContent = g.apPage[ppn])!=0 ){
memcpy(pOut, &pContent[iOfst%g.szPage], n);
}else{
memset(pOut, 0, n);
}
iOfst += n;
iAmt -= n;
pOut += n;
}
return rc;
}
/*
** Write data to an ssdsim-file.
*/
static int ssdsimWrite(
sqlite3_file *pFile,
const void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
ssdsim_file *p = (ssdsim_file *)pFile;
ssdsim_inode *pInode = p->pInode;
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( nOld<nNew ){
aiPage = sqlite3_realloc(pInode->aiPage, 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<<ofst);
/* The unlock case */
if( flags & SQLITE_SHM_UNLOCK ){
p->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; i<pInode->nShmRegion; 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);
}
}