-C Add\smore\sstress\stesting\sto\sautovacuum\sin\san\s(unsuccessful)\sattempt\sto\nreproduce\sa\sreported\sproblem.\s\sEven\sthough\sthese\stests\sdid\snot\suncover\nanything\samiss,\sextra\stests\snever\shurt...\s(CVS\s2807)
-D 2005-12-09T02:35:54
+C Many\ssmall\schanges\sto\sensure\smemory\sis\snot\sleaked\safter\smalloc()\sfails.\s(CVS\s2808)
+D 2005-12-09T14:25:08
F Makefile.in e3c6b3a38d734d41574c04f2fc90d18de2b87102
F Makefile.linux-gcc aee18d8a05546dcf1888bd4547e442008a49a092
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
F src/alter.c 7ed4b794c2e3a8ad8c1effe50202eaef42cedc23
F src/analyze.c ea42005eed52c382fcc7ef66969e7f1858597633
-F src/attach.c 39e678033d0be197dea4e9d5eeac2b2df51d6c9f
+F src/attach.c ee70131f128d31a9c6dcb8824e8471c91b18601a
F src/auth.c 31e2304bef67f44d635655f44234387ea7d21454
F src/btree.c aa88194f460becf8fff6196996d6e38f1b37286e
F src/btree.h 1ed561263ca0e335bc3e81d761c9d5ff8c22f61e
F src/os_common.h d74a11728ad2444b6b695b94c28c06881f049e49
F src/os_test.c 49833426101f99aee4bb5f6a44b7c4b2029fda1c
F src/os_test.h 903c93554c23d88f34f667f1979e4a1cee792af3
-F src/os_unix.c 76189e4cb5e1ad451293ce1aeb348c3b829e7e13
+F src/os_unix.c 01648f7fa16cddb1f8a11ef2a582348801b185a6
F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e
F src/os_win.c d962ac2dd0e482847e42b846d46cd044f97d1c32
F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
F src/pager.h e7b41ce8e7b5f629d456708b7ad9a8c8ede37140
F src/parse.y 87080d89439925de19f16189310d3dbc7f9ab3f6
F src/pragma.c a6d3a7c76b4008b8197913de4bd2079e00b145db
-F src/prepare.c e93967011379051316728d316755f533a9bb438c
+F src/prepare.c 7e21ab5e2304e76f5f679d0df19dca3b0e3ff4f2
F src/printf.c 3ea3a17d25d7ac498efc18007c70371a42c968f8
F src/random.c ff5e9a8cad790e2a51cd4d2e7737dc8540e09d1d
F src/select.c 0e4d3627fec4a445b45f6cb471f68aab9c97a8b3
-F src/shell.c 3596c1e559b82663057940d19ba533ad421c7dd3
+F src/shell.c 4872acee1d2a826c73c914961e469e563204b7f9
F src/sqlite.h.in 8e648e1f386e4509f2f96c09ded7c07b0df0c9a2
-F src/sqliteInt.h bbc310a83a32476aa960055a166af4a0ef503a79
+F src/sqliteInt.h 4247cf9ae4280ae33e5bbc7b45358940ff34b9ce
F src/table.c 486dcfce532685b53b5a2b5da8bba0ded6fb2316
F src/tclsqlite.c 328060916c24d328cfab1622c9a0e7ad57c2da8c
-F src/test1.c 0b4bf8ab9afb37f34a83c46f81de54adf519b23d
+F src/test1.c 350129da26943dd62067e75ad71ba85a1a648230
F src/test2.c 36390cdfc70c08e5ee0b466d0654a117f398bbff
F src/test3.c f4e6a16a602091696619a1171bda25c0e3df49f7
F src/test4.c a8fd681e139e1c61f22a77d07fc3a99cb28fff3f
F src/test5.c 64f08b2a50ef371a1bd68ff206829e7b1b9997f5
F src/test6.c cb811391ec0b7c75f29e545d4820a9cf19f3637e
F src/tokenize.c a189d7466524076220f41a21baed05e1639a82f4
-F src/trigger.c 388c13a2f07584decdb88413a63017391f9a7f7a
+F src/trigger.c dfc1f8ee8e71c6482fb269695778c8b51f821c98
F src/update.c ec8e540617b116725b5a55c8d6b4db8bc67fdd7d
F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c
-F src/util.c 88ebafb5708f04002e58a373386e3c8c31bfd858
-F src/vacuum.c 4d64ce98a4cb130002cbf8aba018b2567773e6b1
-F src/vdbe.c 7f4a2affee140b7b57952ab2c081a9c22b950638
+F src/util.c c77727365ce60b0e0087550ba88653ad0fcf24ea
+F src/vacuum.c 3cd457f91b05338269f2ea21c4c58dce93f2eef2
+F src/vdbe.c 4f78f1fe3b5e7675ee54a2cc18f14dea4ff5fd94
F src/vdbe.h 8729a4ee16ff9aeab2af9667df3cf300ff978e13
F src/vdbeInt.h 0055c37eccbf3a189fd893a90f8eb6a5fa60c871
-F src/vdbeapi.c b80b3a1e8bf7866d96ca76d91bba979155bace57
-F src/vdbeaux.c 90143aaffbe232a700fd05e4b2e7428d512b605b
+F src/vdbeapi.c b270b680cbc5d20b5a1abfdb08339667985df94e
+F src/vdbeaux.c b0a4a4b19b42f18d86c0026a9cc548478dc6a7a7
F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5
F src/vdbemem.c 1c70555d615c6001c4490dedb6a62cb2884b92ff
F src/where.c 269569f380ddc018518f67765fe2f0d3c8760e28
F test/main.test b12f01d49a5c805a33fa6c0ef168691f63056e79
F test/malloc.test a5ed721cf7d1b12602ede4f98c11b65ab1582cc0
F test/malloc2.test e6e321db96d6c94cb18bf82ad7215070c41e624e
-F test/malloc3.test 0d60a9d19196581e9279c2d040bebd8162ab495b
-F test/malloc4.test 593ae91bf69ab5abc68f7652e679e5d207e23166
+F test/malloc3.test 56372aafe58cc24a31bad4b24ff3f77cf1cc30ab
+F test/malloc4.test 2e29d155eb4b4808019ef47eeedfcbe9e09e0f05
F test/manydb.test d81debbf5871242e3b5df1d3bb5e14c50431b6f8
F test/memdb.test 1860e060be810bf0775bc57408a5b7c4954bcaea
F test/memleak.test df2b2b96e77f8ba159a332299535b1e5f18e49ac
F test/rowid.test 040a3bef06f970c45f5fcd14b2355f7f4d62f0cf
F test/safety.test 907b64fee719554a3622853812af3886fddbbb4f
F test/schema.test 8a2ae440fb15f5798a68059e8746402f3137be46
-F test/select1.test 480233d4f5a81d7d59a55e40d05084d97e57ecdf
+F test/select1.test 20275797efe032a56e97687aa06ba65d7a0b9b2b
F test/select2.test f3c2678c3a9f3cf08ec4988a3845bda64be6d9e3
F test/select3.test 8fece41cd8f2955131b3f973a7123bec60b6e65e
F test/select4.test c239f516aa31f42f2ef7c6d7cd01105f08f934ca
F test/tableapi.test 6a66d58b37d46dc0f2b3c7d4bd2617d209399bd1
F test/tclsqlite.test 2da3e4b3a79b13c1511c9d0cd995e08f8362e782
F test/temptable.test 7927261befdbc7b0a7ffebb85ecc70a74fa7b15b
-F test/tester.tcl 1dbcac19ed71f68f24b7f5c6c134568d551092ba
+F test/tester.tcl 6effe753bed26313eaab11a382436a99e602fa54
F test/thread1.test 776c9e459b75ba905193b351926ac4019b049f35
F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
F test/threadtest2.c 97a830d53c24c42290501fdfba4a6e5bdd34748b
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b
F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P a7c9e8989c752f6b1148e7cc7bf59bbd8b402e87
-R 0c1000cc2ea9df5aafb3433db2962046
-U drh
-Z 44f91e86d2b44a0b3e1c8417c9a4606c
+P d8a8933ff30b83c0483be214403d92c4dfa9a4af
+R 208b8157372f7c8db3da02b392e698f6
+U danielk1977
+Z 1ece2b1e923caae0853519e926a399e9
-d8a8933ff30b83c0483be214403d92c4dfa9a4af
\ No newline at end of file
+601c335463aaabc2e9918e4b9298cff6161be5c4
\ No newline at end of file
*************************************************************************
** This file contains code used to implement the ATTACH and DETACH commands.
**
-** $Id: attach.c,v 1.36 2005/12/06 17:19:11 danielk1977 Exp $
+** $Id: attach.c,v 1.37 2005/12/09 14:25:08 danielk1977 Exp $
*/
#include "sqliteInt.h"
sqlite3* db = pParse->db;
#ifndef SQLITE_OMIT_AUTHORIZATION
- char *zAuthArg = sqlite3NameFromToken(&pAuthArg->span);
- if( !zAuthArg ){
- goto attach_end;
- }
- if( sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0)!=SQLITE_OK ){
- goto attach_end;
+ assert( sqlite3Tsd()->mallocFailed || pAuthArg );
+ if( pAuthArg ){
+ char *zAuthArg = sqlite3NameFromToken(&pAuthArg->span);
+ if( !zAuthArg ){
+ goto attach_end;
+ }
+ rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0);
+ sqliteFree(zAuthArg);
+ if(rc!=SQLITE_OK ){
+ goto attach_end;
+ }
}
#endif /* SQLITE_OMIT_AUTHORIZATION */
sqlite3ExprDelete(pFilename);
sqlite3ExprDelete(pDbname);
sqlite3ExprDelete(pKey);
- sqliteFree(zAuthArg);
}
/*
static int unixOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){
int rc;
unixFile f;
+ int fd;
assert( 0==*pId );
if( access(zFilename, 0)==0 ){
pNew = sqliteMalloc( sizeof(unixFile) );
if( pNew==0 ){
close(pInit->h);
+ releaseLockInfo(pInit->pLock);
+ releaseOpenCnt(pInit->pOpen);
*pId = 0;
return SQLITE_NOMEM;
}else{
** interface, and routines that contribute to loading the database schema
** from disk.
**
-** $Id: prepare.c,v 1.5 2005/12/06 12:52:59 danielk1977 Exp $
+** $Id: prepare.c,v 1.6 2005/12/09 14:25:08 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
sqlite3Error(db, rc, 0);
}
- sqlite3ClearMallocFailed();
+ sqlite3MallocClearFailed();
return rc;
}
** This file contains code to implement the "sqlite" command line
** utility for accessing SQLite databases.
**
-** $Id: shell.c,v 1.128 2005/09/11 02:03:04 drh Exp $
+** $Id: shell.c,v 1.129 2005/12/09 14:25:08 danielk1977 Exp $
*/
#include <stdlib.h>
#include <string.h>
/*
** Determines if a string is a number of not.
*/
-static int isNumber(const unsigned char *z, int *realnum){
+static int isNumber(const char *z, int *realnum){
if( *z=='-' || *z=='+' ) z++;
if( !isdigit(*z) ){
return 0;
zSelect = appendText(zSelect, " || ' VALUES(' || ", 0);
rc = sqlite3_step(pTableInfo);
while( rc==SQLITE_ROW ){
+ const char *zText = (const char *)sqlite3_column_text(pTableInfo, 1);
zSelect = appendText(zSelect, "quote(", 0);
- zSelect = appendText(zSelect, sqlite3_column_text(pTableInfo, 1), '"');
+ zSelect = appendText(zSelect, zText, '"');
rc = sqlite3_step(pTableInfo);
if( rc==SQLITE_ROW ){
zSelect = appendText(zSelect, ") || ', ' || ", 0);
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.430 2005/12/06 17:19:11 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.431 2005/12/09 14:25:08 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
*/
#define Addr(X) ((uptr)X)
-/*
-** If memory allocation problems are found, recompile with
-**
-** -DSQLITE_DEBUG=1
-**
-** to enable some sanity checking on malloc() and free(). To
-** check for memory leaks, recompile with
-**
-** -DSQLITE_DEBUG=2
-**
-** and a line of text will be written to standard error for
-** each malloc() and free(). This output can be analyzed
-** by an AWK script to determine if there are any leaks.
-*/
#ifdef SQLITE_MEMDEBUG
-# define sqliteMalloc(X) sqlite3Malloc_(X,1,__FILE__,__LINE__)
-# define sqliteMallocRaw(X) sqlite3Malloc_(X,0,__FILE__,__LINE__)
-# define sqliteFree(X) sqlite3Free_(X,__FILE__,__LINE__)
-# define sqliteRealloc(X,Y) sqlite3Realloc_(X,Y,__FILE__,__LINE__)
-# define sqliteStrDup(X) sqlite3StrDup_(X,__FILE__,__LINE__)
-# define sqliteStrNDup(X,Y) sqlite3StrNDup_(X,Y,__FILE__,__LINE__)
-#else
-# define sqliteFree sqlite3FreeX
-# define sqliteMalloc sqlite3Malloc
-# define sqliteMallocRaw sqlite3MallocRaw
-# define sqliteRealloc sqlite3Realloc
-# define sqliteStrDup sqlite3StrDup
-# define sqliteStrNDup sqlite3StrNDup
-#endif
-
/*
** The following global variables are used for testing and debugging
** only. They only work if SQLITE_DEBUG is defined.
*/
-#ifdef SQLITE_MEMDEBUG
extern int sqlite3_nMalloc; /* Number of sqliteMalloc() calls */
extern int sqlite3_nFree; /* Number of sqliteFree() calls */
extern int sqlite3_iMallocFail; /* Fail sqliteMalloc() after this many calls */
extern int sqlite3_iMallocReset; /* Set iMallocFail to this when it reaches 0 */
+#define ENTER_MALLOC (\
+ sqlite3Tsd()->zFile = __FILE__, sqlite3Tsd()->iLine = __LINE__ \
+)
+#else
+#define ENTER_MALLOC 0
#endif
+#define sqliteFree(x) sqlite3FreeX(x)
+#define sqliteMalloc(x) (ENTER_MALLOC, sqlite3Malloc(x))
+#define sqliteMallocRaw(x) (ENTER_MALLOC, sqlite3MallocRaw(x))
+#define sqliteRealloc(x,y) (ENTER_MALLOC, sqlite3Realloc(x,y))
+#define sqliteStrDup(x) (ENTER_MALLOC, sqlite3StrDup(x))
+#define sqliteStrNDup(x,y) (ENTER_MALLOC, sqlite3StrNDup(x,y))
+
+/*
+** An instance of this structure is allocated for each thread that uses SQLite.
+*/
+typedef struct SqliteTsd SqliteTsd;
+struct SqliteTsd {
+ int mallocFailed; /* True after a malloc() has failed */
+#ifndef NDEBUG
+ int mallocAllowed; /* assert() in sqlite3Malloc() if not set */
+#endif
+#ifdef SQLITE_MEMDEBUG
+ int isFail; /* True if all malloc() calls should fail */
+ const char *zFile; /* Filename to associate debugging info with */
+ int iLine; /* Line number to associate debugging info with */
+ void *pFirst; /* Pointer to linked list of allocations */
+#endif
+};
+
/*
** Name of the master database table. The master database table
** is a special table that holds the names and attributes of all
char **pzErrMsg; /* Error message stored here */
} InitData;
-/*
-** An instance of this structure is allocated for each thread that uses SQLite.
-*/
-typedef struct SqliteTsd SqliteTsd;
-struct SqliteTsd {
- int mallocFailed; /* True after a malloc() has failed */
-};
-
/*
* This global flag is set for performance testing of triggers. When it is set
* SQLite will perform the overhead of building new and old trigger references
int sqlite3Compare(const char *, const char *);
int sqlite3SortCompare(const char *, const char *);
void sqlite3RealToSortable(double r, char *);
-#ifdef SQLITE_MEMDEBUG
- void *sqlite3Malloc_(int,int,char*,int);
- void sqlite3Free_(void*,char*,int);
- void *sqlite3Realloc_(void*,int,char*,int);
- char *sqlite3StrDup_(const char*,char*,int);
- char *sqlite3StrNDup_(const char*, int,char*,int);
- void sqlite3CheckMemory(void*,int);
-#else
- void *sqlite3Malloc(int);
- void *sqlite3MallocRaw(int);
- void sqlite3Free(void*);
- void *sqlite3Realloc(void*,int);
- char *sqlite3StrDup(const char*);
- char *sqlite3StrNDup(const char*, int);
+
+void *sqlite3Malloc(int);
+void *sqlite3MallocRaw(int);
+void sqlite3Free(void*);
+void *sqlite3Realloc(void*,int);
+char *sqlite3StrDup(const char*);
+char *sqlite3StrNDup(const char*, int);
# define sqlite3CheckMemory(a,b)
-# define sqlite3MallocX sqlite3Malloc
-#endif
void sqlite3ReallocOrFree(void**,int);
void sqlite3FreeX(void*);
void *sqlite3MallocX(int);
+
char *sqlite3MPrintf(const char*, ...);
char *sqlite3VMPrintf(const char*, va_list);
void sqlite3DebugPrintf(const char*, ...);
void sqlite3RegisterLikeFunctions(sqlite3*, int);
int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
SqliteTsd *sqlite3Tsd();
-void sqlite3ClearMallocFailed();
void sqlite3AttachFunctions(sqlite3 *);
+void sqlite3MallocClearFailed();
+#ifdef NDEBUG
+ #define sqlite3MallocDisallow()
+ #define sqlite3MallocAllow()
+#else
+ void sqlite3MallocDisallow();
+ void sqlite3MallocAllow();
+#endif
+
#ifdef SQLITE_SSE
#include "sseInt.h"
#endif
** is not included in the SQLite library. It is used for automated
** testing of the SQLite library.
**
-** $Id: test1.c,v 1.171 2005/12/06 12:53:00 danielk1977 Exp $
+** $Id: test1.c,v 1.172 2005/12/09 14:25:08 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
Tcl_AppendResult(interp, zBuf, 0);
return TCL_OK;
}
+static int sqlite_malloc_outstanding(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ extern int sqlite3OutstandingMallocs(Tcl_Interp *interp);
+ return sqlite3OutstandingMallocs(interp);
+}
#endif
/*
#ifdef SQLITE_MEMDEBUG
{ "sqlite_malloc_fail", (Tcl_CmdProc*)sqlite_malloc_fail },
{ "sqlite_malloc_stat", (Tcl_CmdProc*)sqlite_malloc_stat },
+ { "sqlite_malloc_outstanding", (Tcl_CmdProc*)sqlite_malloc_outstanding},
#endif
{ "sqlite_bind", (Tcl_CmdProc*)test_bind },
{ "breakpoint", (Tcl_CmdProc*)test_breakpoint },
extern int sqlite3_sync_count, sqlite3_fullsync_count;
extern int sqlite3_opentemp_count;
extern int sqlite3_memUsed;
+ extern int sqlite3_malloc_id;
extern int sqlite3_memMax;
extern int sqlite3_like_count;
#if OS_WIN
(char*)&sqlite3_current_time, TCL_LINK_INT);
Tcl_LinkVar(interp, "sqlite_os_trace",
(char*)&sqlite3_os_trace, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_malloc_id",
+ (char*)&sqlite3_malloc_id, TCL_LINK_STRING);
#if OS_WIN
Tcl_LinkVar(interp, "sqlite_os_type",
(char*)&sqlite3_os_type, TCL_LINK_INT);
pTrig = pParse->pNewTrigger;
pParse->pNewTrigger = 0;
- if( pParse->nErr || pTrig==0 ) goto triggerfinish_cleanup;
+ if( pParse->nErr || !pTrig ) goto triggerfinish_cleanup;
pTrig->step_list = pStepList;
while( pStepList ){
pStepList->pTrig = pTrig;
*/
TriggerStep *sqlite3TriggerSelectStep(Select *pSelect){
TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
- if( pTriggerStep==0 ) return 0;
+ if( pTriggerStep==0 ) {
+ sqlite3SelectDelete(pSelect);
+ return 0;
+ }
pTriggerStep->op = TK_SELECT;
pTriggerStep->pSelect = pSelect;
** This file contains functions for allocating memory, comparing
** strings, and stuff like that.
**
-** $Id: util.c,v 1.149 2005/12/06 12:53:01 danielk1977 Exp $
+** $Id: util.c,v 1.150 2005/12/09 14:25:09 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <stdarg.h>
#include <ctype.h>
-#if SQLITE_MEMDEBUG>2 && defined(__GLIBC__)
-#include <execinfo.h>
-void print_stack_trace(){
- void *bt[30];
- int i;
- int n = backtrace(bt, 30);
+/*
+** MALLOC WRAPPER ARCHITECTURE
+**
+** The sqlite code accesses dynamic memory allocation/deallocation by invoking
+** the following four APIs (which may be implemented as macros).
+**
+** sqlite3Malloc()
+** sqlite3MallocRaw()
+** sqlite3Realloc()
+** sqlite3ReallocOrFree()
+** sqlite3Free()
+**
+** The function sqlite3FreeX performs the same task as sqlite3Free and is
+** guaranteed to be a real function.
+**
+** The above APIs are implemented in terms of the functions provided at the Os
+** level (not in this file). The Os level interface is never accessed directly
+** by code outside of this file.
+**
+** sqlite3OsMalloc()
+** sqlite3OsRealloc()
+** sqlite3OsFree()
+** sqlite3OsAllocationSize()
+**
+** Functions sqlite3MallocRaw() and sqlite3Realloc() may invoke
+** sqlite3_release_memory() if a call to sqlite3OsMalloc() or
+** sqlite3OsRealloc() fails. Function sqlite3Malloc() usually invokes
+** sqlite3MallocRaw().
+**
+** MALLOC TEST WRAPPER ARCHITECTURE
+**
+** The test wrapper provides extra test facilities to ensure the library
+** does not leak memory and handles the failure of the underlying (Os level)
+** allocation system correctly. It is only present if the library is
+** compiled with the SQLITE_MEMDEBUG macro set.
+**
+** * Guardposts to detect overwrites.
+** * Ability to cause a specific Malloc() or Realloc() to fail.
+** * Audit outstanding memory allocations (i.e check for leaks).
+*/
- fprintf(stderr, "STACK: ");
- for(i=0; i<n;i++){
- fprintf(stderr, "%p ", bt[i]);
+/*
+** sqlite3OsMalloc
+** sqlite3OsRealloc
+** sqlite3OsOsFree
+** sqlite3OsAllocationSize
+**
+** Implementation of the os level dynamic memory allocation interface in terms
+** of the standard malloc(), realloc() and free() found in many operating
+** systems. No rocket science here.
+*/
+void *sqlite3OsMalloc(int n){
+ char *p = (char *)malloc(n+8);
+ assert(n>0);
+ assert(sizeof(int)<=8);
+ if( p ){
+ *(int *)p = n;
+ }
+ return (void *)(p + 8);
+}
+void *sqlite3OsRealloc(void *p, int n){
+ char *p2 = ((char *)p - 8);
+ assert(n>0);
+ p2 = realloc(p2, n+8);
+ if( p2 ){
+ *(int *)p2 = n;
}
- fprintf(stderr, "\n");
+ return (void *)((char *)p2 + 8);
}
+void sqlite3OsFree(void *p){
+ assert(p);
+ free((void *)((char *)p - 8));
+}
+int sqlite3OsAllocationSize(void *p){
+ return *(int *)((char *)p - 8);
+}
+
+/*
+** TODO!
+*/
+#define sqlite3_release_memory(x) 0
+
+#ifdef SQLITE_MEMDEBUG
+/*--------------------------------------------------------------------------
+** Begin code for memory allocation system test layer.
+**
+** Memory debugging is turned on by defining the SQLITE_MEMDEBUG macro.
+*/
+
+/* Figure out whether or not to store backtrace() information for each malloc.
+** The backtrace() function is only used if SQLITE_MEMDEBUG is set to 2 or
+** greater and glibc is in use. If we don't want to use backtrace(), then just
+** define it as an empty macro and set the amount of space reserved to 0.
+*/
+#if defined(__GLIBC__) && SQLITE_MEMDEBUG>1
+ extern int backtrace(void **, int);
+ #define TESTALLOC_STACKSIZE 128
+ #define TESTALLOC_STACKFRAMES ((TESTALLOC_STACKSIZE-8)/sizeof(void*))
#else
-#define print_stack_trace()
+ #define backtrace(x, y) 0
+ #define TESTALLOC_STACKSIZE 0
+ #define TESTALLOC_STACKFRAMES 0
#endif
-#if 0
/*
-** If malloc() ever fails, this global variable gets set to 1.
-** This causes the library to abort and never again function.
+** Number of 32-bit guard words. This should probably be a multiple of
+** 2 since on 64-bit machines we want the value returned by sqliteMalloc()
+** to be 8-byte aligned.
*/
-int sqlite3Tsd()->mallocFailed = 0;
-#endif
+#define TESTALLOC_NGUARD 2
/*
-** If SQLITE_MEMDEBUG is defined, then use versions of malloc() and
-** free() that track memory usage and check for buffer overruns.
+** Size reserved for storing file-name along with each malloc()ed blob.
*/
-#ifdef SQLITE_MEMDEBUG
+#define TESTALLOC_FILESIZE 64
+
+/*
+** Size reserved for storing the user string.
+*/
+#define TESTALLOC_USERSIZE 64
+const char *sqlite3_malloc_id = 0;
+
+/*
+** Blocks used by the test layer have the following format:
+**
+** <sizeof(void *) pNext pointer>
+** <sizeof(void *) pPrev pointer>
+** <TESTALLOC_NGUARD 32-bit guard words>
+** <The application level allocation>
+** <TESTALLOC_NGUARD 32-bit guard words>
+** <32-bit line number>
+** <TESTALLOC_FILESIZE bytes containing null-terminated file name>
+** <TESTALLOC_STACKSIZE bytes of backtrace() output>
+*/
+
+#define TESTALLOC_OFFSET_GUARD1(p) (sizeof(void *) * 2)
+#define TESTALLOC_OFFSET_DATA(p) ( \
+ TESTALLOC_OFFSET_GUARD1(p) + sizeof(u32) * TESTALLOC_NGUARD \
+)
+#define TESTALLOC_OFFSET_GUARD2(p) ( \
+ TESTALLOC_OFFSET_DATA(p) + sqlite3OsAllocationSize(p) - TESTALLOC_OVERHEAD \
+)
+#define TESTALLOC_OFFSET_LINENUMBER(p) ( \
+ TESTALLOC_OFFSET_GUARD2(p) + sizeof(u32) * TESTALLOC_NGUARD \
+)
+#define TESTALLOC_OFFSET_FILENAME(p) ( \
+ TESTALLOC_OFFSET_LINENUMBER(p) + sizeof(u32) \
+)
+#define TESTALLOC_OFFSET_USER(p) ( \
+ TESTALLOC_OFFSET_FILENAME(p) + TESTALLOC_FILESIZE \
+)
+#define TESTALLOC_OFFSET_STACK(p) ( \
+ TESTALLOC_OFFSET_USER(p) + TESTALLOC_USERSIZE + 8 - \
+ (TESTALLOC_OFFSET_USER(p) % 8) \
+)
+
+#define TESTALLOC_OVERHEAD ( \
+ sizeof(void *)*2 + /* pPrev and pNext pointers */ \
+ TESTALLOC_NGUARD*sizeof(u32)*2 + /* Guard words */ \
+ sizeof(u32) + TESTALLOC_FILESIZE + /* File and line number */ \
+ TESTALLOC_USERSIZE + /* User string */ \
+ TESTALLOC_STACKSIZE /* backtrace() stack */ \
+)
/*
** For keeping track of the number of mallocs and frees. This
*/
int sqlite3_nMalloc; /* Number of sqliteMalloc() calls */
int sqlite3_nFree; /* Number of sqliteFree() calls */
-int sqlite3_memUsed; /* Total memory obtained from malloc */
-int sqlite3_memMax; /* Mem usage high-water mark */
+int sqlite3_memUsed; /* TODO Total memory obtained from malloc */
+int sqlite3_memMax; /* TODO Mem usage high-water mark */
int sqlite3_iMallocFail; /* Fail sqliteMalloc() after this many calls */
int sqlite3_iMallocReset = -1; /* When iMallocFail reaches 0, set to this */
-#if SQLITE_MEMDEBUG>1
-static int memcnt = 0;
-#endif
-
-/*
-** Number of 32-bit guard words. This should probably be a multiple of
-** 2 since on 64-bit machines we want the value returned by sqliteMalloc()
-** to be 8-byte aligned.
-*/
-#define N_GUARD 2
/*
** Check for a simulated memory allocation failure. Return true if
** the failure should be simulated. Return false to proceed as normal.
*/
-static int simulatedMallocFailure(int n, char *zFile, int line){
+static int failMalloc(){
+ SqliteTsd *pTsd = sqlite3Tsd();
+ if( pTsd->isFail ){
+ return 1;
+ }
if( sqlite3_iMallocFail>=0 ){
sqlite3_iMallocFail--;
if( sqlite3_iMallocFail==0 ){
- sqlite3Tsd()->mallocFailed++;
-#if SQLITE_MEMDEBUG>1
- fprintf(stderr,"**** failed to allocate %d bytes at %s:%d\n",
- n, zFile,line);
-#endif
sqlite3_iMallocFail = sqlite3_iMallocReset;
+ pTsd->isFail = 1;
return 1;
}
}
}
/*
-** Allocate new memory and set it to zero. Return NULL if
-** no memory is available.
+** The argument is a pointer returned by sqlite3OsMalloc() or Realloc().
+** assert() that the first and last (TESTALLOC_NGUARD*4) bytes are set to the
+** values set by the applyGuards() function.
*/
-void *sqlite3Malloc_(int n, int bZero, char *zFile, int line){
- void *p;
- int *pi;
- int i, k;
-
- /* Any malloc() calls between a malloc() failure and clearing the
- ** mallocFailed flag (done before returning control to the user)
- ** automatically fail. Although this restriction may have to be relaxed in
- ** the future, for now it makes the system easier to test.
- **
- ** TODO: This will eventually be done as part of the same wrapper that may
- ** call sqlite3_release_memory(). Above the sqlite3OsMalloc() level.
- */
- if( sqlite3Tsd()->mallocFailed ){
- return 0;
- }
+static void checkGuards(u32 *p)
+{
+ int i;
+ char *zAlloc = (char *)p;
+ char *z;
- if( n==0 ){
- return 0;
+ /* First set of guard words */
+ z = &zAlloc[TESTALLOC_OFFSET_GUARD1(p)];
+ for(i=0; i<TESTALLOC_NGUARD; i++){
+ assert(((u32 *)z)[i]==0xdead1122);
}
- if( simulatedMallocFailure(n, zFile, line) ){
- return 0;
- }
- sqlite3_memUsed += n;
- if( sqlite3_memMax<sqlite3_memUsed ) sqlite3_memMax = sqlite3_memUsed;
- k = (n+sizeof(int)-1)/sizeof(int);
- pi = malloc( (N_GUARD*2+1+k)*sizeof(int));
- if( pi==0 ){
- if( n>0 ) sqlite3Tsd()->mallocFailed++;
- return 0;
+
+ /* Second set of guard words */
+ z = &zAlloc[TESTALLOC_OFFSET_GUARD2(p)];
+ for(i=0; i<TESTALLOC_NGUARD; i++){
+ u32 guard = 0;
+ memcpy(&guard, &z[i*sizeof(u32)], sizeof(u32));
+ assert(guard==0xdead3344);
}
- sqlite3_nMalloc++;
- for(i=0; i<N_GUARD; i++) pi[i] = 0xdead1122;
- pi[N_GUARD] = n;
- for(i=0; i<N_GUARD; i++) pi[k+1+N_GUARD+i] = 0xdead3344;
- p = &pi[N_GUARD+1];
- memset(p, bZero==0, n);
-#if SQLITE_MEMDEBUG>1
- print_stack_trace();
- fprintf(stderr,"%06d malloc %d bytes at 0x%x from %s:%d\n",
- ++memcnt, n, (int)p, zFile,line);
-#endif
- return p;
}
/*
-** This version of malloc is always a real function, never a macro
+** The argument is a pointer returned by sqlite3OsMalloc() or Realloc(). The
+** first and last (TESTALLOC_NGUARD*4) bytes are set to known values for use as
+** guard-posts.
*/
-void *sqlite3MallocX(int n){
- return sqlite3Malloc_(n, 0, __FILE__, __LINE__);
+static void applyGuards(u32 *p)
+{
+ int i;
+ char *z;
+ char *zAlloc = (char *)p;
+
+ /* First set of guard words */
+ z = &zAlloc[TESTALLOC_OFFSET_GUARD1(p)];
+ for(i=0; i<TESTALLOC_NGUARD; i++){
+ ((u32 *)z)[i] = 0xdead1122;
+ }
+
+ /* Second set of guard words */
+ z = &zAlloc[TESTALLOC_OFFSET_GUARD2(p)];
+ for(i=0; i<TESTALLOC_NGUARD; i++){
+ static const int guard = 0xdead3344;
+ memcpy(&z[i*sizeof(u32)], &guard, sizeof(u32));
+ }
+
+ /* Line number */
+ z = &((char *)z)[TESTALLOC_NGUARD*sizeof(u32)]; /* Guard words */
+ z = &zAlloc[TESTALLOC_OFFSET_LINENUMBER(p)];
+ memcpy(z, &sqlite3Tsd()->iLine, sizeof(u32));
+
+ /* File name */
+ z = &zAlloc[TESTALLOC_OFFSET_FILENAME(p)];
+ strncpy(z, sqlite3Tsd()->zFile, TESTALLOC_FILESIZE);
+ z[TESTALLOC_FILESIZE - 1] = '\0';
+
+ /* User string */
+ z = &zAlloc[TESTALLOC_OFFSET_USER(p)];
+ z[0] = 0;
+ if( sqlite3_malloc_id ){
+ strncpy(z, sqlite3_malloc_id, TESTALLOC_USERSIZE);
+ z[TESTALLOC_USERSIZE-1] = 0;
+ }
+
+ /* backtrace() stack */
+ z = &zAlloc[TESTALLOC_OFFSET_STACK(p)];
+ backtrace((void **)z, TESTALLOC_STACKFRAMES);
+
+ /* Sanity check to make sure checkGuards() is working */
+ checkGuards(p);
+}
+
+static void *getOsPointer(void *p)
+{
+ char *z = (char *)p;
+ return (void *)(&z[-1 * TESTALLOC_OFFSET_DATA(p)]);
}
/*
-** Check to see if the given pointer was obtained from sqliteMalloc()
-** and is able to hold at least N bytes. Raise an exception if this
-** is not the case.
-**
-** This routine is used for testing purposes only.
+** The argument points to an Os level allocation. Link it into the threads list
+** of allocations.
*/
-void sqlite3CheckMemory(void *p, int N){
- int *pi = p;
- int n, i, k;
- pi -= N_GUARD+1;
- for(i=0; i<N_GUARD; i++){
- assert( pi[i]==0xdead1122 );
- }
- n = pi[N_GUARD];
- assert( N>=0 && N<n );
- k = (n+sizeof(int)-1)/sizeof(int);
- for(i=0; i<N_GUARD; i++){
- assert( pi[k+N_GUARD+1+i]==0xdead3344 );
+static void linkAlloc(void *p){
+ SqliteTsd *pTsd = sqlite3Tsd();
+ void **pp = (void **)p;
+ pp[0] = 0;
+ pp[1] = pTsd->pFirst;
+ if( pTsd->pFirst ){
+ ((void **)pTsd->pFirst)[0] = p;
}
+ pTsd->pFirst = p;
}
/*
-** Free memory previously obtained from sqliteMalloc()
+** The argument points to an Os level allocation. Unlinke it from the threads
+** list of allocations.
*/
-void sqlite3Free_(void *p, char *zFile, int line){
- if( p ){
- int *pi, i, k, n;
- pi = p;
- pi -= N_GUARD+1;
- sqlite3_nFree++;
- for(i=0; i<N_GUARD; i++){
- if( pi[i]!=0xdead1122 ){
- fprintf(stderr,"Low-end memory corruption at 0x%x\n", (int)p);
- return;
- }
+static void unlinkAlloc(void *p)
+{
+ SqliteTsd *pTsd = sqlite3Tsd();
+ void **pp = (void **)p;
+ if( p==pTsd->pFirst ){
+ assert(!pp[0]);
+ assert(!pp[1] || ((void **)(pp[1]))[0]==p);
+ pTsd->pFirst = pp[1];
+ if( pTsd->pFirst ){
+ ((void **)pTsd->pFirst)[0] = 0;
}
- n = pi[N_GUARD];
- sqlite3_memUsed -= n;
- k = (n+sizeof(int)-1)/sizeof(int);
- for(i=0; i<N_GUARD; i++){
- if( pi[k+N_GUARD+1+i]!=0xdead3344 ){
- fprintf(stderr,"High-end memory corruption at 0x%x\n", (int)p);
- return;
- }
+ }else{
+ void **pprev = pp[0];
+ void **pnext = pp[1];
+ assert(pprev);
+ assert(pprev[1]==p);
+ pprev[1] = (void *)pnext;
+ if( pnext ){
+ assert(pnext[0]==p);
+ pnext[0] = (void *)pprev;
}
- memset(pi, 0xff, (k+N_GUARD*2+1)*sizeof(int));
-#if SQLITE_MEMDEBUG>1
- fprintf(stderr,"%06d free %d bytes at 0x%x from %s:%d\n",
- ++memcnt, n, (int)p, zFile,line);
-#endif
- free(pi);
}
}
/*
-** Resize a prior allocation. If p==0, then this routine
-** works just like sqliteMalloc(). If n==0, then this routine
-** works just like sqliteFree().
+** Pointer p is a pointer to an OS level allocation that has just been
+** realloc()ed. Set the list pointers that point to this entry to it's new
+** location.
*/
-void *sqlite3Realloc_(void *oldP, int n, char *zFile, int line){
- int *oldPi, *pi, i, k, oldN, oldK;
- void *p;
- if( oldP==0 ){
- return sqlite3Malloc_(n,1,zFile,line);
- }
- if( n==0 ){
- sqlite3Free_(oldP,zFile,line);
- return 0;
- }
- if( simulatedMallocFailure(n, zFile, line) ){
- return 0;
+static void relinkAlloc(void *p)
+{
+ void **pp = (void **)p;
+ if( pp[0] ){
+ ((void **)(pp[0]))[1] = p;
+ }else{
+ SqliteTsd *pTsd = sqlite3Tsd();
+ pTsd->pFirst = p;
}
- oldPi = oldP;
- oldPi -= N_GUARD+1;
- if( oldPi[0]!=0xdead1122 ){
- fprintf(stderr,"Low-end memory corruption in realloc at 0x%x\n", (int)oldP);
- return 0;
+ if( pp[1] ){
+ ((void **)(pp[1]))[0] = p;
}
- oldN = oldPi[N_GUARD];
- sqlite3_memUsed -= oldN;
- oldK = (oldN+sizeof(int)-1)/sizeof(int);
- for(i=0; i<N_GUARD; i++){
- if( oldPi[oldK+N_GUARD+1+i]!=0xdead3344 ){
- fprintf(stderr,"High-end memory corruption in realloc at 0x%x\n",
- (int)oldP);
- return 0;
+}
+
+/*
+** This function sets the result of the Tcl interpreter passed as an argument
+** to a list containing an entry for each currently outstanding call made to
+** sqliteMalloc and friends by the current thread.
+**
+** Todo: We could have a version of this function that outputs to stdout,
+** to debug memory leaks when Tcl is not available.
+*/
+#ifdef TCLSH
+#include <tcl.h>
+int sqlite3OutstandingMallocs(Tcl_Interp *interp){
+ void *p;
+ SqliteTsd *pTsd = sqlite3Tsd();
+ Tcl_Obj *pRes = Tcl_NewObj();
+ Tcl_IncrRefCount(pRes);
+
+ for(p=pTsd->pFirst; p; p=((void **)p)[1]){
+ Tcl_Obj *pEntry = Tcl_NewObj();
+ Tcl_Obj *pStack = Tcl_NewObj();
+ char *z;
+ u32 iLine;
+ int nBytes = sqlite3OsAllocationSize(p) - TESTALLOC_OVERHEAD;
+ char *zAlloc = (char *)p;
+ int i;
+
+ Tcl_ListObjAppendElement(0, pEntry, Tcl_NewIntObj(nBytes));
+
+ z = &zAlloc[TESTALLOC_OFFSET_FILENAME(p)];
+ Tcl_ListObjAppendElement(0, pEntry, Tcl_NewStringObj(z, -1));
+
+ z = &zAlloc[TESTALLOC_OFFSET_LINENUMBER(p)];
+ memcpy(&iLine, z, sizeof(u32));
+ Tcl_ListObjAppendElement(0, pEntry, Tcl_NewIntObj(iLine));
+
+ z = &zAlloc[TESTALLOC_OFFSET_USER(p)];
+ Tcl_ListObjAppendElement(0, pEntry, Tcl_NewStringObj(z, -1));
+
+ z = &zAlloc[TESTALLOC_OFFSET_STACK(p)];
+ for(i=0; i<TESTALLOC_STACKFRAMES; i++){
+ char zHex[128];
+ sprintf(zHex, "%p", ((void **)z)[i]);
+ Tcl_ListObjAppendElement(0, pStack, Tcl_NewStringObj(zHex, -1));
}
+
+ Tcl_ListObjAppendElement(0, pEntry, pStack);
+ Tcl_ListObjAppendElement(0, pRes, pEntry);
}
- k = (n + sizeof(int) - 1)/sizeof(int);
- pi = malloc( (k+N_GUARD*2+1)*sizeof(int) );
- if( pi==0 ){
- if( n>0 ) sqlite3Tsd()->mallocFailed++;
- return 0;
- }
- for(i=0; i<N_GUARD; i++) pi[i] = 0xdead1122;
- pi[N_GUARD] = n;
- sqlite3_memUsed += n;
- if( sqlite3_memMax<sqlite3_memUsed ) sqlite3_memMax = sqlite3_memUsed;
- for(i=0; i<N_GUARD; i++) pi[k+N_GUARD+1+i] = 0xdead3344;
- p = &pi[N_GUARD+1];
- memcpy(p, oldP, n>oldN ? oldN : n);
- if( n>oldN ){
- memset(&((char*)p)[oldN], 0x55, n-oldN);
- }
- memset(oldPi, 0xab, (oldK+N_GUARD+2)*sizeof(int));
- free(oldPi);
-#if SQLITE_MEMDEBUG>1
- print_stack_trace();
- fprintf(stderr,"%06d realloc %d to %d bytes at 0x%x to 0x%x at %s:%d\n",
- ++memcnt, oldN, n, (int)oldP, (int)p, zFile, line);
-#endif
- return p;
+
+ Tcl_ResetResult(interp);
+ Tcl_SetObjResult(interp, pRes);
+ Tcl_DecrRefCount(pRes);
+ return TCL_OK;
}
+#endif
/*
-** Make a copy of a string in memory obtained from sqliteMalloc()
+** This is the test layer's wrapper around sqlite3OsMalloc().
*/
-char *sqlite3StrDup_(const char *z, char *zFile, int line){
- char *zNew;
- if( z==0 ) return 0;
- zNew = sqlite3Malloc_(strlen(z)+1, 0, zFile, line);
- if( zNew ) strcpy(zNew, z);
- return zNew;
-}
-char *sqlite3StrNDup_(const char *z, int n, char *zFile, int line){
- char *zNew;
- if( z==0 ) return 0;
- zNew = sqlite3Malloc_(n+1, 0, zFile, line);
- if( zNew ){
- memcpy(zNew, z, n);
- zNew[n] = 0;
+static void * OSMALLOC(int n){
+ if( !failMalloc() ){
+ u32 *p;
+ p = (u32 *)sqlite3OsMalloc(n + TESTALLOC_OVERHEAD);
+ assert(p);
+ sqlite3_nMalloc++;
+ applyGuards(p);
+ linkAlloc(p);
+ return (void *)(&p[TESTALLOC_NGUARD + 2*sizeof(void *)/sizeof(u32)]);
}
- return zNew;
+ return 0;
}
/*
-** A version of sqliteFree that is always a function, not a macro.
+** This is the test layer's wrapper around sqlite3OsFree(). The argument is a
+** pointer to the space allocated for the application to use.
*/
-void sqlite3FreeX(void *p){
- sqliteFree(p);
+void OSFREE(void *pFree){
+ u32 *p = (u32 *)getOsPointer(pFree); /* p points to Os level allocation */
+ checkGuards(p);
+ unlinkAlloc(p);
+ sqlite3OsFree(p);
+ sqlite3_nFree++;
}
-#endif /* SQLITE_MEMDEBUG */
/*
-** The following versions of malloc() and free() are for use in a
-** normal build.
+** This is the test layer's wrapper around sqlite3OsRealloc().
*/
-#if !defined(SQLITE_MEMDEBUG)
+void * OSREALLOC(void *pRealloc, int n){
+ if( !failMalloc() ){
+ u32 *p = (u32 *)getOsPointer(pRealloc);
+ checkGuards(p);
+ p = sqlite3OsRealloc(p, n + TESTALLOC_OVERHEAD);
+ applyGuards(p);
+ relinkAlloc(p);
+ return (void *)(&p[TESTALLOC_NGUARD + 2*sizeof(void *)/sizeof(u32)]);
+ }
+ return 0;
+}
+
+void OSMALLOC_FAILED(){
+ sqlite3Tsd()->isFail = 0;
+}
+#else
+#define OSMALLOC(x) sqlite3OsMalloc(x)
+#define OSREALLOC(x,y) sqlite3OsRealloc(x,y)
+#define OSFREE(x) sqlite3OsFree(x)
+#define OSMALLOC_FAILED()
+#endif
/*
-** Allocate new memory and set it to zero. Return NULL if
-** no memory is available. See also sqliteMallocRaw().
+** End code for memory allocation system test layer.
+**--------------------------------------------------------------------------*/
+
+/*
+** Allocate and return N bytes of uninitialised memory by calling
+** sqlite3OsMalloc(). If the Malloc() call fails, attempt to free memory
+** by calling sqlite3_release_memory().
*/
-void *sqlite3Malloc(int n){
- void *p;
- if( n==0 ) return 0;
- if( (p = malloc(n))==0 ){
- if( n>0 ) sqlite3Tsd()->mallocFailed++;
- }else{
- memset(p, 0, n);
+void *sqlite3MallocRaw(int n){
+ SqliteTsd *pTsd = sqlite3Tsd();
+ void *p = 0;
+
+ if( n>0 && !pTsd->mallocFailed ){
+ while( !(p = OSMALLOC(n)) && sqlite3_release_memory(n) );
+ if( !p ){
+ sqlite3Tsd()->mallocFailed = 1;
+ OSMALLOC_FAILED();
+ }
}
return p;
}
/*
-** Allocate new memory but do not set it to zero. Return NULL if
-** no memory is available. See also sqliteMalloc().
+** Resize the allocation at p to n bytes by calling sqlite3OsRealloc(). The
+** pointer to the new allocation is returned. If the Realloc() call fails,
+** attempt to free memory by calling sqlite3_release_memory().
*/
-void *sqlite3MallocRaw(int n){
- void *p;
- if( n==0 ) return 0;
- if( (p = malloc(n))==0 ){
- if( n>0 ) sqlite3Tsd()->mallocFailed++;
+void *sqlite3Realloc(void *p, int n){
+ SqliteTsd *pTsd = sqlite3Tsd();
+ if( pTsd->mallocFailed ){
+ return 0;
+ }
+
+ if( !p ){
+ return sqlite3Malloc(n);
+ }else{
+ void *np = 0;
+ while( !(np = OSREALLOC(p, n)) && sqlite3_release_memory(n) );
+ if( !np ){
+ pTsd->mallocFailed = 1;
+ OSMALLOC_FAILED();
+ }
+ return np;
}
- return p;
}
/*
-** Free memory previously obtained from sqliteMalloc()
+** Free the memory pointed to by p. p must be either a NULL pointer or a
+** value returned by a previous call to sqlite3Malloc() or sqlite3Realloc().
*/
void sqlite3FreeX(void *p){
if( p ){
- free(p);
+ OSFREE(p);
}
}
/*
-** Resize a prior allocation. If p==0, then this routine
-** works just like sqliteMalloc(). If n==0, then this routine
-** works just like sqliteFree().
+** A version of sqliteMalloc() that is always a function, not a macro.
+** Currently, this is used only to alloc only used drawback.
*/
-void *sqlite3Realloc(void *p, int n){
- void *p2;
- if( p==0 ){
- return sqliteMalloc(n);
- }
- if( n==0 ){
- sqliteFree(p);
- return 0;
+void *sqlite3MallocX(int n){
+ return sqliteMalloc(n);
+}
+
+/*
+** sqlite3Malloc
+** sqlite3ReallocOrFree
+**
+** These two are implemented as wrappers around sqlite3MallocRaw(),
+** sqlite3Realloc() and sqlite3Free().
+*/
+void *sqlite3Malloc(int n){
+ void *p = sqlite3MallocRaw(n);
+ if( p ){
+ memset(p, 0, n);
}
- p2 = realloc(p, n);
- if( p2==0 ){
- if( n>0 ) sqlite3Tsd()->mallocFailed++;
+ return p;
+}
+void sqlite3ReallocOrFree(void **pp, int n){
+ void *p = sqlite3Realloc(*pp, n);
+ if( !p ){
+ sqlite3FreeX(*pp);
}
- return p2;
+ *pp = p;
}
/*
-** Make a copy of a string in memory obtained from sqliteMalloc()
+** Make a copy of a string in memory obtained from sqliteMalloc(). These
+** functions call sqlite3MallocRaw() directly instead of sqliteMalloc(). This
+** is because when memory debugging is turned on, these two functions are
+** called via macros that record the current file and line number in the
+** SqliteTsd structure.
*/
char *sqlite3StrDup(const char *z){
char *zNew;
if( z==0 ) return 0;
- zNew = sqliteMallocRaw(strlen(z)+1);
+ zNew = sqlite3MallocRaw(strlen(z)+1);
if( zNew ) strcpy(zNew, z);
return zNew;
}
char *sqlite3StrNDup(const char *z, int n){
char *zNew;
if( z==0 ) return 0;
- zNew = sqliteMallocRaw(n+1);
+ zNew = sqlite3MallocRaw(n+1);
if( zNew ){
memcpy(zNew, z, n);
zNew[n] = 0;
}
return zNew;
}
-#endif /* !defined(SQLITE_MEMDEBUG) */
-
-/*
-** Reallocate a buffer to a different size. This is similar to
-** sqliteRealloc() except that if the allocation fails the buffer
-** is freed.
-*/
-void sqlite3ReallocOrFree(void **ppBuf, int newSize){
- void *pNew = sqliteRealloc(*ppBuf, newSize);
- if( pNew==0 ){
- sqliteFree(*ppBuf);
- }
- *ppBuf = pNew;
-}
/*
** Create a string from the 2nd and subsequent arguments (up to the
zResult += strlen(zResult);
}
va_end(ap);
-#ifdef SQLITE_MEMDEBUG
-#if SQLITE_MEMDEBUG>1
- fprintf(stderr,"string at 0x%x is %s\n", (int)*pz, *pz);
-#endif
-#endif
}
/*
/*
** Return a pointer to the SqliteTsd associated with the calling thread.
*/
-static SqliteTsd tsd = { 0 };
+static SqliteTsd tsd = {
+ 0 /* mallocFailed flag */
+#ifndef NDEBUG
+ , 1 /* mallocAllowed flag */
+#endif
+#ifndef SQLITE_MEMDEBUG
+ , 0
+ , 0
+ , 0
+ , 0
+#endif
+};
SqliteTsd *sqlite3Tsd(){
return &tsd;
}
-void sqlite3ClearMallocFailed(){
+void sqlite3MallocClearFailed(){
sqlite3Tsd()->mallocFailed = 0;
}
+#ifndef NDEBUG
+/*
+** This function sets a flag in the thread-specific-data structure that will
+** cause an assert to fail if sqliteMalloc() or sqliteRealloc() is called.
+*/
+void sqlite3MallocDisallow(){
+ assert(sqlite3Tsd()->mallocAllowed);
+ sqlite3Tsd()->mallocAllowed = 0;
+}
+
+/*
+** This function clears the flag set in the thread-specific-data structure set
+** by sqlite3MallocDisallow().
+*/
+void sqlite3MallocAllow(){
+ assert(!sqlite3Tsd()->mallocAllowed);
+ sqlite3Tsd()->mallocAllowed = 1;
+}
+#endif
** Most of the code in this file may be omitted by defining the
** SQLITE_OMIT_VACUUM macro.
**
-** $Id: vacuum.c,v 1.51 2005/12/06 17:48:32 danielk1977 Exp $
+** $Id: vacuum.c,v 1.52 2005/12/09 14:25:09 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "vdbeInt.h"
db->autoCommit = 1;
if( pDetach ){
+ int mf = sqlite3Tsd()->mallocFailed;
+ sqlite3Tsd()->mallocFailed = 0;
+ sqlite3MallocDisallow();
((Vdbe *)pDetach)->expired = 0;
sqlite3_step(pDetach);
rc2 = sqlite3_finalize(pDetach);
if( rc==SQLITE_OK ){
rc = rc2;
}
+ sqlite3MallocAllow();
+ sqlite3Tsd()->mallocFailed = mf;
}
/* If one of the execSql() calls above returned SQLITE_NOMEM, then the
** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
-** $Id: vdbe.c,v 1.502 2005/12/06 12:53:01 danielk1977 Exp $
+** $Id: vdbe.c,v 1.503 2005/12/09 14:25:09 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
if( p->magic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE;
assert( db->magic==SQLITE_MAGIC_BUSY );
+ pTos = p->pTos;
+ if( p->rc==SQLITE_NOMEM ){
+ /* This happens if a malloc() inside a call to sqlite3_column_text() or
+ ** sqlite3_column_text16() failed. */
+ goto no_mem;
+ }
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY );
p->rc = SQLITE_OK;
assert( p->explain==0 );
- pTos = p->pTos;
- if( sqlite3Tsd()->mallocFailed ) goto no_mem;
if( p->popStack ){
popStack(&pTos, p->popStack);
p->popStack = 0;
db->init.busy = 1;
assert(0==sqlite3Tsd()->mallocFailed);
rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
+ sqliteFree(zSql);
db->init.busy = 0;
sqlite3SafetyOn(db);
if( rc==SQLITE_NOMEM ){
sqlite3Tsd()->mallocFailed = 1;
goto no_mem;
}
- sqliteFree(zSql);
break;
}
#endif
sqlite3Error(p->db, rc, p->zErrMsg ? "%s" : 0, p->zErrMsg);
- sqlite3ClearMallocFailed();
+ sqlite3MallocClearFailed();
return rc;
}
return &pVm->pTos[(1-vals)+i];
}
+/*
+** This function is called after invoking an sqlite3_value_XXX function on a
+** column value (i.e. a value returned by evaluating an SQL expression in the
+** select list of a SELECT statement) that may cause a malloc() failure. If
+** malloc() has failed, the threads mallocFailed flag is cleared and the result
+** code of statement pStmt set to SQLITE_NOMEM.
+**
+** Specificly, this is called from within:
+**
+** sqlite3_column_int()
+** sqlite3_column_int64()
+** sqlite3_column_text()
+** sqlite3_column_text16()
+** sqlite3_column_real()
+** sqlite3_column_bytes()
+** sqlite3_column_bytes16()
+**
+** But not for sqlite3_column_blob(), which never calls malloc().
+*/
+static void columnMallocFailure(sqlite3_stmt *pStmt)
+{
+ /* If malloc() failed during an encoding conversion within an
+ ** sqlite3_column_XXX API, then set the return code of the statement to
+ ** SQLITE_NOMEM. The next call to _step() (if any) will return SQLITE_ERROR
+ ** and _finalize() will return NOMEM.
+ */
+ if( sqlite3Tsd()->mallocFailed ){
+ ((Vdbe *)pStmt)->rc = SQLITE_NOMEM;
+ sqlite3MallocClearFailed();
+ }
+}
+
/**************************** sqlite3_column_ *******************************
** The following routines are used to access elements of the current row
** in the result set.
*/
const void *sqlite3_column_blob(sqlite3_stmt *pStmt, int i){
- return sqlite3_value_blob( columnMem(pStmt,i) );
+ const void *val;
+ sqlite3MallocDisallow();
+ val = sqlite3_value_blob( columnMem(pStmt,i) );
+ sqlite3MallocAllow();
+ return val;
}
int sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){
- return sqlite3_value_bytes( columnMem(pStmt,i) );
+ int val = sqlite3_value_bytes( columnMem(pStmt,i) );
+ columnMallocFailure(pStmt);
+ return val;
}
int sqlite3_column_bytes16(sqlite3_stmt *pStmt, int i){
- return sqlite3_value_bytes16( columnMem(pStmt,i) );
+ int val = sqlite3_value_bytes16( columnMem(pStmt,i) );
+ columnMallocFailure(pStmt);
+ return val;
}
double sqlite3_column_double(sqlite3_stmt *pStmt, int i){
- return sqlite3_value_double( columnMem(pStmt,i) );
+ double val = sqlite3_value_double( columnMem(pStmt,i) );
+ columnMallocFailure(pStmt);
+ return val;
}
int sqlite3_column_int(sqlite3_stmt *pStmt, int i){
- return sqlite3_value_int( columnMem(pStmt,i) );
+ int val = sqlite3_value_int( columnMem(pStmt,i) );
+ columnMallocFailure(pStmt);
+ return val;
}
sqlite_int64 sqlite3_column_int64(sqlite3_stmt *pStmt, int i){
- return sqlite3_value_int64( columnMem(pStmt,i) );
+ sqlite_int64 val = sqlite3_value_int64( columnMem(pStmt,i) );
+ columnMallocFailure(pStmt);
+ return val;
}
const unsigned char *sqlite3_column_text(sqlite3_stmt *pStmt, int i){
- return sqlite3_value_text( columnMem(pStmt,i) );
+ const unsigned char *val = sqlite3_value_text( columnMem(pStmt,i) );
+ columnMallocFailure(pStmt);
+ return val;
}
#if 0
sqlite3_value *sqlite3_column_value(sqlite3_stmt *pStmt, int i){
#endif
#ifndef SQLITE_OMIT_UTF16
const void *sqlite3_column_text16(sqlite3_stmt *pStmt, int i){
- return sqlite3_value_text16( columnMem(pStmt,i) );
+ const void *val = sqlite3_value_text16( columnMem(pStmt,i) );
+ columnMallocFailure(pStmt);
+ return val;
}
#endif /* SQLITE_OMIT_UTF16 */
int sqlite3_column_type(sqlite3_stmt *pStmt, int i){
/* A malloc may have failed inside of the xFunc() call. If this is the case,
** clear the mallocFailed flag and return NULL.
*/
- sqlite3ClearMallocFailed();
+ sqlite3MallocClearFailed();
return ret;
}
-
/*
** Return the name of the Nth column of the result set returned by SQL
** statement pStmt.
void sqlite3VdbeChangeP3(Vdbe *p, int addr, const char *zP3, int n){
Op *pOp;
assert( p->magic==VDBE_MAGIC_INIT );
- if( p==0 || p->aOp==0 ){
+ if( p==0 || p->aOp==0 || sqlite3Tsd()->mallocFailed ){
if (n != P3_KEYINFO) {
freeP3(n, (void*)*(char**)&zP3);
}
void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
va_list ap;
assert( p->nOp>0 );
- assert( p->aOp==0 || p->aOp[p->nOp-1].p3==0 );
+ assert( p->aOp==0 || p->aOp[p->nOp-1].p3==0 || sqlite3Tsd()->mallocFailed );
va_start(ap, zFormat);
sqlite3VdbeChangeP3(p, -1, sqlite3VMPrintf(zFormat, ap), P3_DYNAMIC);
va_end(ap);
# correctly. The emphasis of these tests are the _prepare(), _step() and
# _finalize() calls.
#
-# $Id: malloc3.test,v 1.3 2005/12/07 06:27:45 danielk1977 Exp $
+# $Id: malloc3.test,v 1.4 2005/12/09 14:25:12 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
default { error "Unknown switch: $k" }
}
+# if {$iFail > ($iFailStart+1)} return
}
}
# Turn of the Tcl interface's prepared statement caching facility.
db cache size 0
-#run_test $::run_test_script 59 97
-run_test $::run_test_script
+run_test $::run_test_script
+# run_test [lrange $::run_test_script 0 3] 0 63
sqlite_malloc_fail 0
+db close
+
+pp_check_for_leaks
+
finish_test
# This file contains tests to ensure that the library handles malloc() failures
# correctly. The emphasis in this file is on sqlite3_column_XXX() APIs.
#
-# $Id: malloc4.test,v 1.1 2005/12/07 06:27:45 danielk1977 Exp $
+# $Id: malloc4.test,v 1.2 2005/12/09 14:25:12 danielk1977 Exp $
#---------------------------------------------------------------------------
# NOTES ON EXPECTED BEHAVIOUR
#
+# [193] When a memory allocation failure occurs during sqlite3_column_name(),
+# sqlite3_column_name16(), sqlite3_column_decltype(), or
+# sqlite3_column_decltype16() the function shall return NULL.
+#
#---------------------------------------------------------------------------
set testdir [file dirname $argv0]
return
}
-
proc do_stmt_test {id sql} {
set ::sql $sql
set go 1
# Test malloc failure in the _name(), _name16(), decltype() and
# decltype16() APIs. Calls that occur after the malloc() failure should
# return NULL. No error is raised though.
+ #
+ # ${testid}.2.1 - Call _name()
+ # ${testid}.2.2 - Call _name16()
+ # ${testid}.2.3 - Call _name()
+ # ${testid}.2.4 - Check that the return values of the above three calls are
+ # consistent with each other and with the simulated
+ # malloc() failures.
+ #
+ # Because the code that implements the _decltype() and _decltype16() APIs
+ # is the same as the _name() and _name16() implementations, we don't worry
+ # about explicitly testing them.
+ #
do_test ${testid}.2.1 {
set mf1 [expr [lindex [sqlite_malloc_stat] 2] <= 0]
set ::name8 [sqlite3_column_name $::STMT 0]
}
} {1}
- if {$::mallocFailed == 0} {
+ # Step the statement so that we can call _text() and _text16(). Before
+ # running sqlite3_step(), make sure that malloc() is not about to fail.
+ # Memory allocation failures that occur within sqlite3_step() are tested
+ # elsewhere.
+ set mf [lindex [sqlite_malloc_stat] 2]
+ sqlite_malloc_fail 0
+ do_test ${testid}.3 {
+ sqlite3_step $::STMT
+ } {SQLITE_ROW}
+ sqlite_malloc_fail $mf
+
+ # Test for malloc() failures within _text() and _text16().
+ #
+ do_test ${testid}.4.1 {
+ set ::text8 [sqlite3_column_text $::STMT 0]
+ set mf [expr [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed]
+ expr {$mf==0 || $::text8 == ""}
+ } {1}
+ do_test ${testid}.4.2 {
+ set ::text16 [sqlite3_column_text16 $::STMT 0]
+ set ::text16 [encoding convertfrom unicode $::text16]
+ set ::text16 [string range $::text16 0 end-1]
+ set mf [expr [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed]
+ expr {$mf==0 || $::text16 == ""}
+ } {1}
+ do_test ${testid}.4.3 {
+ set ::text8_2 [sqlite3_column_text $::STMT 0]
+ set mf [expr [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed]
+ expr {$mf==0 || $::text8_2 == "" || ($::text16 == "" && $::text8 != "")}
+ } {1}
+
+ # Test for malloc() failures within _int(), _int64() and _real(). The only
+ # way this can occur is if the string has to be translated from UTF-16 to
+ # UTF-8 before being converted to a numeric value.
+ do_test ${testid}.4.4.1 {
+ set mf [lindex [sqlite_malloc_stat] 2]
sqlite_malloc_fail 0
- set go 0
- }
+ sqlite3_column_text16 $::STMT 0
+ sqlite_malloc_fail $mf
+ sqlite3_column_int $::STMT 0
+ } {0}
+ do_test ${testid}.4.5 {
+ set mf [lindex [sqlite_malloc_stat] 2]
+ sqlite_malloc_fail 0
+ sqlite3_column_text16 $::STMT 0
+ sqlite_malloc_fail $mf
+ sqlite3_column_int64 $::STMT 0
+ } {0}
+
+ do_test ${testid}.4.6 {
+ set mf [lindex [sqlite_malloc_stat] 2]
+ sqlite_malloc_fail 0
+ sqlite3_column_text16 $::STMT 0
+ sqlite_malloc_fail $mf
+ sqlite3_column_double $::STMT 0
+ } {0.0}
-if 0 {
+ set mallocFailedAfterStep [expr \
+ [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed
+ ]
+
+ sqlite_malloc_fail 0
# Test that if a malloc() failed the next call to sqlite3_step() returns
- # SQLITE_ERROR. If malloc() did not fail, it should return SQLITE_ROW.
+ # SQLITE_ERROR. If malloc() did not fail, it should return SQLITE_DONE.
#
- # Before running sqlite3_step(), make sure that malloc() is not about to
- # fail. Memory allocation failures that occur within sqlite3_step() are
- # tested elsewhere.
- do_test ${testid}.3 {
- set rc [sqlite3_step $::STMT]
- list [expr $rc=="SQLITE_ERROR"] [expr $rc=="SQLITE_ROW"]
- } [list $mallocFailed [expr !$mallocFailed]]
-}
+ do_test ${testid}.5 {
+ sqlite3_step $::STMT
+ } [expr {$mallocFailedAfterStep ? "SQLITE_ERROR" : "SQLITE_DONE"}]
- do_test ${testid}.4 {
+ do_test ${testid}.6 {
sqlite3_finalize $::STMT
- } {SQLITE_OK}
+ } [expr {$mallocFailedAfterStep ? "SQLITE_NOMEM" : "SQLITE_OK"}]
+
+ if {$::mallocFailed == 0 && $mallocFailedAfterStep == 0} {
+ sqlite_malloc_fail 0
+ set go 0
+ }
}
}
CREATE TABLE tbl(
the_first_reasonably_long_column_name that_also_has_quite_a_lengthy_type
);
+ INSERT INTO tbl VALUES(
+ 'An extra long string. Far too long to be stored in NBFS bytes.'
+ );
}
+
do_stmt_test 1 "SELECT * FROM tbl"
sqlite_malloc_fail 0
# This file implements regression tests for SQLite library. The
# focus of this file is testing the SELECT statement.
#
-# $Id: select1.test,v 1.43 2005/09/08 10:37:01 drh Exp $
+# $Id: select1.test,v 1.44 2005/12/09 14:25:12 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
} {x 1 x 3}
} ;# ifcapable compound
-
finish_test
# This file implements some common TCL routines used for regression
# testing the SQLite library
#
-# $Id: tester.tcl,v 1.52 2005/11/26 00:25:04 drh Exp $
+# $Id: tester.tcl,v 1.53 2005/12/09 14:25:12 danielk1977 Exp $
# Make sure tclsqlite3 was compiled correctly. Abort now with an
# error message if not.
#
proc do_test {name cmd expected} {
global argv nErr nTest skip_test maxErr
+ set ::sqlite_malloc_id $name
if {$skip_test} {
set skip_test 0
return
proc finalize_testing {} {
global nTest nErr nProb sqlite_open_file_count
if {$nErr==0} memleak_check
+
catch {db close}
+ catch {db2 close}
+ catch {db3 close}
+
+pp_check_for_leaks
+
puts "$nErr errors out of $nTest tests"
puts "Failures on these tests: $::failList"
if {$nProb>0} {
}
}
+# This command checks for outstanding calls to sqliteMalloc() from within
+# the current thread. A list is returned with one entry for each outstanding
+# malloc. Each list entry is itself a list of 5 items, as follows:
+#
+# { <number-bytes> <file-name> <line-number> <test-case> <stack-dump> }
+#
+proc check_for_leaks {} {
+ set ret [list]
+ foreach alloc [sqlite_malloc_outstanding] {
+ foreach {nBytes file iLine userstring backtrace} $alloc {}
+ set stack [list]
+ set skip 0
+
+ # The first command in this block will probably fail on windows. This
+ # means there will be no stack dump available.
+ catch {
+ set stuff [eval "exec addr2line -e ./testfixture -f $backtrace"]
+ foreach {func line} $stuff {
+ if {$func != "??" || $line != "??:0"} {
+ regexp {.*/(.*)} $line dummy line
+ lappend stack "${func}() $line"
+ } else {
+ if {[lindex $stack end] != "..."} {
+ lappend stack "..."
+ }
+ }
+ }
+ }
+
+ if {!$skip} {
+ lappend ret [list $nBytes $file $iLine $userstring $stack]
+ }
+ }
+ return $ret
+}
+
+# Pretty print a report based on the return value of [check_for_leaks] to
+# stdout.
+proc pp_check_for_leaks {} {
+ set l [check_for_leaks]
+ set n 0
+ foreach leak $l {
+ foreach {nBytes file iLine userstring stack} $leak {}
+ puts "$nBytes bytes leaked at $file:$iLine ($userstring)"
+ foreach frame $stack {
+ puts " $frame"
+ }
+ incr n $nBytes
+ }
+ puts "Memory leaked: $n bytes in [llength $l] allocations"
+ puts ""
+}
+
# If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set
# to non-zero, then set the global variable $AUTOVACUUM to 1.
set AUTOVACUUM $sqlite_options(default_autovacuum)