|
|
|
@@ -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);
|
|
|
|
|
}
|
|
|
|
|
}
|