/*
** 2007 August 15
**
** 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 used to implement test interfaces to the
** memory allocation subsystem.
*/
#include "sqliteInt.h"
#include "tcl.h"
#include
#include
#include
/*
** This structure is used to encapsulate the global state variables used
** by malloc() fault simulation.
*/
static struct MemFault {
int iCountdown; /* Number of pending successes before a failure */
int nRepeat; /* Number of times to repeat the failure */
int nBenign; /* Number of benign failures seen since last config */
int nFail; /* Number of failures seen since last config */
u8 enable; /* True if enabled */
int isInstalled; /* True if the fault simulation layer is installed */
int isBenignMode; /* True if malloc failures are considered benign */
sqlite3_mem_methods m; /* 'Real' malloc implementation */
} memfault;
/*
** This routine exists as a place to set a breakpoint that will
** fire on any simulated malloc() failure.
*/
static void sqlite3Fault(void){
static int cnt = 0;
cnt++;
}
/*
** Check to see if a fault should be simulated. Return true to simulate
** the fault. Return false if the fault should not be simulated.
*/
static int faultsimStep(void){
if( likely(!memfault.enable) ){
return 0;
}
if( memfault.iCountdown>0 ){
memfault.iCountdown--;
return 0;
}
sqlite3Fault();
memfault.nFail++;
if( memfault.isBenignMode>0 ){
memfault.nBenign++;
}
memfault.nRepeat--;
if( memfault.nRepeat=0;
/* Sometimes, when running multi-threaded tests, the isBenignMode
** variable is not properly incremented/decremented so that it is
** 0 when not inside a benign malloc block. This doesn't affect
** the multi-threaded tests, as they do not use this system. But
** it does affect OOM tests run later in the same process. So
** zero the variable here, just to be sure.
*/
memfault.isBenignMode = 0;
}
/*
** Return the number of faults (both hard and benign faults) that have
** occurred since the injector was last configured.
*/
static int faultsimFailures(void){
return memfault.nFail;
}
/*
** Return the number of benign faults that have occurred since the
** injector was last configured.
*/
static int faultsimBenignFailures(void){
return memfault.nBenign;
}
/*
** Return the number of successes that will occur before the next failure.
** If no failures are scheduled, return -1.
*/
static int faultsimPending(void){
if( memfault.enable ){
return memfault.iCountdown;
}else{
return -1;
}
}
static void faultsimBeginBenign(void){
memfault.isBenignMode++;
}
static void faultsimEndBenign(void){
memfault.isBenignMode--;
}
/*
** Add or remove the fault-simulation layer using sqlite3_config(). If
** the argument is non-zero, the
*/
static int faultsimInstall(int install){
static struct sqlite3_mem_methods m = {
faultsimMalloc, /* xMalloc */
faultsimFree, /* xFree */
faultsimRealloc, /* xRealloc */
faultsimSize, /* xSize */
faultsimRoundup, /* xRoundup */
faultsimInit, /* xInit */
faultsimShutdown, /* xShutdown */
0 /* pAppData */
};
int rc;
install = (install ? 1 : 0);
assert(memfault.isInstalled==1 || memfault.isInstalled==0);
if( install==memfault.isInstalled ){
return SQLITE_ERROR;
}
if( install ){
rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memfault.m);
assert(memfault.m.xMalloc);
if( rc==SQLITE_OK ){
rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &m);
}
sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,
faultsimBeginBenign, faultsimEndBenign
);
}else{
sqlite3_mem_methods m;
assert(memfault.m.xMalloc);
/* One should be able to reset the default memory allocator by storing
** a zeroed allocator then calling GETMALLOC. */
memset(&m, 0, sizeof(m));
sqlite3_config(SQLITE_CONFIG_MALLOC, &m);
sqlite3_config(SQLITE_CONFIG_GETMALLOC, &m);
assert( memcmp(&m, &memfault.m, sizeof(m))==0 );
rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memfault.m);
sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, 0, 0);
}
if( rc==SQLITE_OK ){
memfault.isInstalled = 1;
}
return rc;
}
#ifdef SQLITE_TEST
/*
** This function is implemented in main.c. Returns a pointer to a static
** buffer containing the symbolic SQLite error code that corresponds to
** the least-significant 8-bits of the integer passed as an argument.
** For example:
**
** sqlite3ErrName(1) -> "SQLITE_ERROR"
*/
extern const char *sqlite3ErrName(int);
/*
** Transform pointers to text and back again
*/
static void pointerToText(void *p, char *z){
static const char zHex[] = "0123456789abcdef";
int i, k;
unsigned int u;
sqlite3_uint64 n;
if( p==0 ){
strcpy(z, "0");
return;
}
if( sizeof(n)==sizeof(p) ){
memcpy(&n, &p, sizeof(p));
}else if( sizeof(u)==sizeof(p) ){
memcpy(&u, &p, sizeof(u));
n = u;
}else{
assert( 0 );
}
for(i=0, k=sizeof(p)*2-1; i>= 4;
}
z[sizeof(p)*2] = 0;
}
static int hexToInt(int h){
if( h>='0' && h='a' && hnCall);
apElem[1] = Tcl_NewIntObj(pLog->nByte);
for(ii=0; ii=5 && Tcl_GetIntFromObj(interp, objv[4], &highStress) ){
return TCL_ERROR;
}
if( discardChance100 ){
Tcl_AppendResult(interp, "discard-chance should be between 0 and 100",
(char*)0);
return TCL_ERROR;
}
installTestPCache(installFlag, (unsigned)discardChance, (unsigned)prngSeed,
(unsigned)highStress);
return TCL_OK;
}
/*
** Usage: sqlite3_config_memstatus BOOLEAN
**
** Enable or disable memory status reporting using SQLITE_CONFIG_MEMSTATUS.
*/
static int test_config_memstatus(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int enable, rc;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
return TCL_ERROR;
}
if( Tcl_GetBooleanFromObj(interp, objv[1], &enable) ) return TCL_ERROR;
rc = sqlite3_config(SQLITE_CONFIG_MEMSTATUS, enable);
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
return TCL_OK;
}
/*
** Usage: sqlite3_config_lookaside SIZE COUNT
**
*/
static int test_config_lookaside(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int sz, cnt;
Tcl_Obj *pRet;
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "SIZE COUNT");
return TCL_ERROR;
}
if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
if( Tcl_GetIntFromObj(interp, objv[2], &cnt) ) return TCL_ERROR;
pRet = Tcl_NewObj();
Tcl_ListObjAppendElement(
interp, pRet, Tcl_NewIntObj(sqlite3GlobalConfig.szLookaside)
);
Tcl_ListObjAppendElement(
interp, pRet, Tcl_NewIntObj(sqlite3GlobalConfig.nLookaside)
);
sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, cnt);
Tcl_SetObjResult(interp, pRet);
return TCL_OK;
}
/*
** Usage: sqlite3_db_config_lookaside CONNECTION BUFID SIZE COUNT
**
** There are two static buffers with BUFID 1 and 2. Each static buffer
** is 10KB in size. A BUFID of 0 indicates that the buffer should be NULL
** which will cause sqlite3_db_config() to allocate space on its own.
*/
static int test_db_config_lookaside(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int rc;
int sz, cnt;
sqlite3 *db;
int bufid;
static char azBuf[2][10000];
extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
if( objc!=5 ){
Tcl_WrongNumArgs(interp, 1, objv, "BUFID SIZE COUNT");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
if( Tcl_GetIntFromObj(interp, objv[2], &bufid) ) return TCL_ERROR;
if( Tcl_GetIntFromObj(interp, objv[3], &sz) ) return TCL_ERROR;
if( Tcl_GetIntFromObj(interp, objv[4], &cnt) ) return TCL_ERROR;
if( bufid==0 ){
rc = sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, 0, sz, cnt);
}else if( bufid>=1 && bufid