Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -349,11 +349,11 @@ /* ** Zero, unlock, and free the saved database encryption key now. */ db_unsave_encryption_key(); #endif -#if defined(_WIN32) || defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS) +#if defined(_WIN32) || (defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS)) /* ** Free the secure getpass() buffer now. */ freepass(); #endif @@ -388,10 +388,17 @@ */ if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){ if( g.interp ){ Th_DeleteInterp(g.interp); g.interp = 0; } +#if defined(TH_MEMDEBUG) + if( Th_GetOutstandingMalloc()!=0 ){ + fossil_print("Th_GetOutstandingMalloc() => %d\n", + Th_GetOutstandingMalloc()); + } + assert( Th_GetOutstandingMalloc()==0 ); +#endif } } /* ** Convert all arguments from mbcs (or unicode) to UTF-8. Then Index: src/makemake.tcl ================================================================== --- src/makemake.tcl +++ src/makemake.tcl @@ -1478,11 +1478,11 @@ OBJDIR = $(T) OX = $(OBJDIR) O = .obj E = .exe P = .pdb -OPTLEVEL= /Os +DBGOPTS = /Od INSTALLDIR = . !ifdef DESTDIR INSTALLDIR = $(DESTDIR)\$(INSTALLDIR) !endif @@ -1501,10 +1501,15 @@ # Perl is only necessary if OpenSSL support is enabled and it is built from # source code. The PERLDIR environment variable, if it exists, should point # to the directory containing the main Perl executable specified here (i.e. # "perl.exe"). PERL = perl.exe + +# Enable use of available compiler optimizations? +!ifndef OPTIMIZATIONS +OPTIMIZATIONS = 2 +!endif # Enable debugging symbols? !ifndef DEBUG DEBUG = 0 !endif @@ -1673,16 +1678,28 @@ CRTFLAGS = /MTd !else CRTFLAGS = /MT !endif !endif + +!if $(OPTIMIZATIONS)>3 +RELOPTS = /Os +!elseif $(OPTIMIZATIONS)>2 +RELOPTS = /Ox +!elseif $(OPTIMIZATIONS)>1 +RELOPTS = /O2 +!elseif $(OPTIMIZATIONS)>0 +RELOPTS = /O1 +!else +RELOPTS = +!endif !if $(DEBUG)!=0 -CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) /Od /DFOSSIL_DEBUG +CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) $(DBGOPTS) /DFOSSIL_DEBUG /DTH_MEMDEBUG LDFLAGS = $(LDFLAGS) /DEBUG !else -CFLAGS = $(CFLAGS) $(CRTFLAGS) $(OPTLEVEL) +CFLAGS = $(CFLAGS) $(CRTFLAGS) $(RELOPTS) !endif BCC = $(CC) $(CFLAGS) TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) Index: src/printf.c ================================================================== --- src/printf.c +++ src/printf.c @@ -1100,11 +1100,11 @@ /* ** Avoid calling into the JSON support subsystem if it ** has not yet been initialized, e.g. early SQLite log ** messages, etc. */ - assert(json_is_bootstrapped_early()); + if( !json_is_bootstrapped_early() ) json_bootstrap_early(); json_err( 0, z, 1 ); if( g.isHTTP && !g.json.preserveRc ){ rc = 0 /* avoid HTTP 500 */; } if( g.cgiOutput==1 ){ @@ -1230,11 +1230,11 @@ /* ** Avoid calling into the JSON support subsystem if it ** has not yet been initialized, e.g. early SQLite log ** messages, etc. */ - assert(json_is_bootstrapped_early()); + if( !json_is_bootstrapped_early() ) json_bootstrap_early(); json_warn( FSL_JSON_W_UNKNOWN, "%s", z ); }else #endif { if( g.cgiOutput==1 ){ Index: src/th.c ================================================================== --- src/th.c +++ src/th.c @@ -222,11 +222,20 @@ Buffer *pBuffer, const char *zAdd, int nAdd ){ int nNew = (pBuffer->nBuf+nAdd)*2+32; +#if defined(TH_MEMDEBUG) + char *zNew = (char *)Th_Malloc(interp, nNew); + th_memcpy(zNew, pBuffer->zBuf, pBuffer->nBuf); + Th_Free(interp, pBuffer->zBuf); + pBuffer->zBuf = zNew; +#else + int nOld = pBuffer->nBufAlloc; pBuffer->zBuf = Th_Realloc(interp, pBuffer->zBuf, nNew); + memset(pBuffer->zBuf+nOld, 0, nNew-nOld); +#endif pBuffer->nBufAlloc = nNew; th_memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd); pBuffer->nBuf += nAdd; } static void thBufferWriteFast( @@ -1535,10 +1544,37 @@ }else{ return (char *)Th_Malloc(pInterp, 1); } } +#if defined(TH_MEMDEBUG) +/* +** Wrappers around the supplied malloc() and free() +*/ +void *Th_DbgMalloc(Th_Interp *pInterp, int nByte){ + void *p; + Th_Vtab *pVtab = pInterp->pVtab; + if( pVtab ){ + p = pVtab->xMalloc(nByte); + if( p ) memset(p, 0, nByte); + }else{ + p = Th_SysMalloc(pInterp, nByte); + } + return p; +} +void Th_DbgFree(Th_Interp *pInterp, void *z){ + if( z ){ + Th_Vtab *pVtab = pInterp->pVtab; + if( pVtab ){ + pVtab->xFree(z); + }else{ + Th_SysFree(pInterp, z); + } + } +} +#endif + /* ** Install a new th1 command. ** ** If a command of the same name already exists, it is deleted automatically. */ @@ -1821,16 +1857,24 @@ } /* ** Create a new interpreter. */ -Th_Interp * Th_CreateInterp(void){ +Th_Interp * Th_CreateInterp(Th_Vtab *pVtab){ + int nByte = sizeof(Th_Interp) + sizeof(Th_Frame); Th_Interp *p; /* Allocate and initialise the interpreter and the global frame */ - p = Th_Malloc(0, sizeof(Th_Interp) + sizeof(Th_Frame)); - memset(p, 0, sizeof(Th_Interp)); +#if defined(TH_MEMDEBUG) + if( pVtab ){ + p = pVtab->xMalloc(nByte); + memset(p, 0, nByte); + p->pVtab = pVtab; + }else +#endif + p = Th_SysMalloc(0, nByte); + p->paCmd = Th_HashNew(p); thPushFrame(p, (Th_Frame *)&p[1]); thInitialize(p); return p; Index: src/th.h ================================================================== --- src/th.h +++ src/th.h @@ -21,11 +21,11 @@ typedef struct Th_Interp Th_Interp; /* ** Create and delete interpreters. */ -Th_Interp * Th_CreateInterp(void); +Th_Interp * Th_CreateInterp(Th_Vtab *); void Th_DeleteInterp(Th_Interp *); /* ** Evaluate an TH program in the stack frame identified by parameter ** iFrame, according to the following rules: @@ -121,16 +121,31 @@ /* ** Access the memory management functions associated with the specified ** interpreter. */ +#if defined(TH_MEMDEBUG) +void *Th_DbgMalloc(Th_Interp *, int); +void Th_DbgFree(Th_Interp *, void *); +#endif + void *fossil_malloc_zero(size_t); -void *fossil_realloc(void*,size_t); -void fossil_free(void*); -#define Th_Malloc(I,N) fossil_malloc_zero(N) -#define Th_Realloc(I,P,N) fossil_realloc(P,N) -#define Th_Free(I,P) fossil_free(P) +void *fossil_realloc(void *, size_t); +void fossil_free(void *); + +#define Th_SysMalloc(I,N) fossil_malloc_zero((N)) +#define Th_SysRealloc(I,P,N) fossil_realloc((P),(N)) +#define Th_SysFree(I,P) fossil_free((P)) + +#if defined(TH_MEMDEBUG) +# define Th_Malloc(I,N) Th_DbgMalloc((I),(N)) +# define Th_Free(I,P) Th_DbgFree((I),(P)) +#else +# define Th_Malloc(I,N) Th_SysMalloc((I),(N)) +# define Th_Realloc(I,P,N) Th_SysRealloc((I),(P),(N)) +# define Th_Free(I,P) Th_SysFree((I),(P)) +#endif /* ** Functions for handling TH lists. */ int Th_ListAppend(Th_Interp *, char **, int *, const char *, int); Index: src/th_main.c ================================================================== --- src/th_main.c +++ src/th_main.c @@ -68,10 +68,46 @@ ** configuration ("user") database are currently open. */ #define Th_IsRepositoryOpen() (g.repositoryOpen) #define Th_IsConfigOpen() (g.zConfigDbName!=0) +/* +** When memory debugging is enabled, use our custom memory allocator. +*/ +#if defined(TH_MEMDEBUG) +/* +** Global variable counting the number of outstanding calls to malloc() +** made by the th1 implementation. This is used to catch memory leaks +** in the interpreter. Obviously, it also means th1 is not threadsafe. +*/ +static int nOutstandingMalloc = 0; + +/* +** Implementations of malloc() and free() to pass to the interpreter. +*/ +static void *xMalloc(unsigned int n){ + void *p = fossil_malloc(n); + if( p ){ + nOutstandingMalloc++; + } + return p; +} +static void xFree(void *p){ + if( p ){ + nOutstandingMalloc--; + } + free(p); +} +static Th_Vtab vtab = { xMalloc, xFree }; + +/* +** Returns the number of outstanding TH1 memory allocations. +*/ +int Th_GetOutstandingMalloc(){ + return nOutstandingMalloc; +} +#endif /* ** Generate a TH1 trace message if debugging is enabled. */ void Th_Trace(const char *zFormat, ...){ @@ -350,11 +386,11 @@ if(0==pOut && pThOut!=0){ pOut = pThOut; } if(TH_INIT_NO_ENCODE & g.th1Flags){ encode = 0; - } + } if( enableOutput && n ){ if( n<0 ) n = strlen(z); if( encode ){ z = htmlize(z, n); n = strlen(z); @@ -627,10 +663,11 @@ blob_zero(&title); blob_zero(&body); markdown_to_html(&src, &title, &body); Th_ListAppend(interp, &zValue, &nValue, blob_str(&title), blob_size(&title)); Th_ListAppend(interp, &zValue, &nValue, blob_str(&body), blob_size(&body)); Th_SetResult(interp, zValue, nValue); + Th_Free(interp, zValue); return TH_OK; } /* ** TH1 command: decorate STRING @@ -810,15 +847,15 @@ }else if( azCap[i][0]=='*' ){ rc = 1; }else{ rc = login_has_capability(azCap[i], anCap[i], 0); } - break; + break; } Th_Free(interp, azCap); Th_SetResultInt(interp, rc); - return TH_OK; + return TH_OK; } /* ** TH1 command: searchable STRING... @@ -2356,11 +2393,20 @@ } if( forceReset || forceTcl || g.interp==0 ){ int created = 0; int i; if( g.interp==0 ){ - g.interp = Th_CreateInterp(); + Th_Vtab *pVtab = 0; +#if defined(TH_MEMDEBUG) + if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){ + pVtab = &vtab; + if( g.thTrace ){ + Th_Trace("th1-init MEMDEBUG ENABLED
\n"); + } + } +#endif + g.interp = Th_CreateInterp(pVtab); created = 1; } if( forceReset || created ){ th_register_language(g.interp); /* Basic scripting commands. */ } Index: src/user.c ================================================================== --- src/user.c +++ src/user.c @@ -38,11 +38,11 @@ if( z[i]>0 && z[i]<' ' ) z[i] = ' '; } blob_append(pBlob, z, -1); } -#if defined(_WIN32) || defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS) +#if defined(_WIN32) || (defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS)) #ifdef _WIN32 #include #endif /* Index: win/Makefile.msc ================================================================== --- win/Makefile.msc +++ win/Makefile.msc @@ -14,11 +14,11 @@ OBJDIR = $(T) OX = $(OBJDIR) O = .obj E = .exe P = .pdb -OPTLEVEL= /Os +DBGOPTS = /Od INSTALLDIR = . !ifdef DESTDIR INSTALLDIR = $(DESTDIR)\$(INSTALLDIR) !endif @@ -37,10 +37,15 @@ # Perl is only necessary if OpenSSL support is enabled and it is built from # source code. The PERLDIR environment variable, if it exists, should point # to the directory containing the main Perl executable specified here (i.e. # "perl.exe"). PERL = perl.exe + +# Enable use of available compiler optimizations? +!ifndef OPTIMIZATIONS +OPTIMIZATIONS = 2 +!endif # Enable debugging symbols? !ifndef DEBUG DEBUG = 0 !endif @@ -209,16 +214,28 @@ CRTFLAGS = /MTd !else CRTFLAGS = /MT !endif !endif + +!if $(OPTIMIZATIONS)>3 +RELOPTS = /Os +!elseif $(OPTIMIZATIONS)>2 +RELOPTS = /Ox +!elseif $(OPTIMIZATIONS)>1 +RELOPTS = /O2 +!elseif $(OPTIMIZATIONS)>0 +RELOPTS = /O1 +!else +RELOPTS = +!endif !if $(DEBUG)!=0 -CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) /Od /DFOSSIL_DEBUG +CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) $(DBGOPTS) /DFOSSIL_DEBUG /DTH_MEMDEBUG LDFLAGS = $(LDFLAGS) /DEBUG !else -CFLAGS = $(CFLAGS) $(CRTFLAGS) $(OPTLEVEL) +CFLAGS = $(CFLAGS) $(CRTFLAGS) $(RELOPTS) !endif BCC = $(CC) $(CFLAGS) TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL)