Add a utility program that looks for assert(), NEVER(), ALWAYS(), and
testcase() macros that have side-effects, and reports errors when they are found. Also fix a bug that this utility detected as it was being tested. FossilOrigin-Name: b0b4624fc5d53bb0cc9fae7dad51984837d946ac
This commit is contained in:
parent
f5818aa560
commit
cc5f8a46b9
@ -583,6 +583,12 @@ sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h
|
||||
sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS)
|
||||
|
||||
srcck1$(BEXE): $(TOP)/tool/srcck1.c
|
||||
$(BCC) -o srcck1$(BEXE) $(TOP)/tool/srcck1.c
|
||||
|
||||
sourcetest: srcck1$(BEXE) sqlite3.c
|
||||
./srcck1 sqlite3.c
|
||||
|
||||
fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(FUZZERSHELL_OPT) \
|
||||
$(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS)
|
||||
@ -1083,7 +1089,7 @@ quicktest: ./testfixture$(TEXE)
|
||||
# This is the common case. Run many tests that do not take too long,
|
||||
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
|
||||
#
|
||||
test: $(TESTPROGS) fastfuzztest
|
||||
test: $(TESTPROGS) sourcetest fastfuzztest
|
||||
./testfixture$(TEXE) $(TOP)/test/veryquick.test $(TESTOPTS)
|
||||
|
||||
# Run a test using valgrind. This can take a really long time
|
||||
|
8
main.mk
8
main.mk
@ -480,6 +480,12 @@ sqldiff$(EXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
|
||||
$(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \
|
||||
$(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
|
||||
srcck1$(EXE): $(TOP)/tool/srcck1.c
|
||||
$(BCC) -o srcck1$(EXE) $(TOP)/tool/srcck1.c
|
||||
|
||||
sourcetest: srcck1$(EXE) sqlite3.c
|
||||
./srcck1 sqlite3.c
|
||||
|
||||
fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
|
||||
$(TCCX) -o fuzzershell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
$(FUZZERSHELL_OPT) $(TOP)/tool/fuzzershell.c sqlite3.c \
|
||||
@ -768,7 +774,7 @@ quicktest: ./testfixture$(EXE)
|
||||
# The default test case. Runs most of the faster standard TCL tests,
|
||||
# and fuzz tests, and sqlite3_analyzer and sqldiff tests.
|
||||
#
|
||||
test: $(TESTPROGS) fastfuzztest
|
||||
test: $(TESTPROGS) sourcetest fastfuzztest
|
||||
./testfixture$(EXE) $(TOP)/test/veryquick.test $(TESTOPTS)
|
||||
|
||||
# Run a test using valgrind. This can take a really long time
|
||||
|
19
manifest
19
manifest
@ -1,6 +1,6 @@
|
||||
C Make\ssure\svariable\sdeclarations\soccur\sat\sthe\sbeginning\sof\sblocks,\seven\nwith\sSQLITE_DEBUG\senabled.
|
||||
D 2016-02-06T19:48:50.321
|
||||
F Makefile.in 027c1603f255390c43a426671055a31c0a65fdb4
|
||||
C Add\sa\sutility\sprogram\sthat\slooks\sfor\sassert(),\sNEVER(),\sALWAYS(),\sand\ntestcase()\smacros\sthat\shave\sside-effects,\sand\sreports\serrors\swhen\sthey\sare\nfound.\s\sAlso\sfix\sa\sbug\sthat\sthis\sutility\sdetected\sas\sit\swas\sbeing\stested.
|
||||
D 2016-02-06T22:32:06.228
|
||||
F Makefile.in 0a957a57243a3d55e96b1514e22ffae5db9ea116
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc 72b7858f02017611c3ac1ddc965251017fed0845
|
||||
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
|
||||
@ -272,7 +272,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk 960071a0bceb043bc5627573986154f507931f33
|
||||
F main.mk f51c0652d2a623160e90a758e01312a6a00f3454
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||
@ -291,7 +291,7 @@ F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
|
||||
F src/backup.c 2869a76c03eb393ee795416e2387005553df72bc
|
||||
F src/bitvec.c 1a78d450a17c5016710eec900bedfc5729bf9bdf
|
||||
F src/btmutex.c bc87dd3b062cc26edfe79918de2200ccb8d41e73
|
||||
F src/btree.c 0b359bcc2316a57acf12f583253974ad22b4654f
|
||||
F src/btree.c 4c8caaeed7878aafdb607c3d2bcbc365bb0d19a1
|
||||
F src/btree.h 368ceeb4bd9312dc8df2ffd64b4b7dbcf4db5f8e
|
||||
F src/btreeInt.h c18b7d2a3494695133e4e60ee36061d37f45d9a5
|
||||
F src/build.c 198eaa849c193f28b802ed135b2483c68ef7a35c
|
||||
@ -313,7 +313,7 @@ F src/insert.c b84359365bace233919db550a15f131923190efc
|
||||
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c b1b0880fc474abfab89e737b0ecfde0bd7a60902
|
||||
F src/loadext.c 84996d7d70a605597d79c1f1d7b2012a5fd34f2b
|
||||
F src/main.c 62b7fe3ed245757d1ff2e6268a7ec0bc30100308
|
||||
F src/main.c b67a45397b93b7ba8fbd6bfcb03423d245baed05
|
||||
F src/malloc.c 337e9808b5231855fe28857950f4f60ae42c417f
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b
|
||||
@ -1416,6 +1416,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||
F tool/speedtest8inst1.c 7ce07da76b5e745783e703a834417d725b7d45fd
|
||||
F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
|
||||
F tool/sqldiff.c 5a26205111e6fa856d9b1535b1637744dcdb930b
|
||||
F tool/srcck1.c 3119733530abcef14f1b0603c66207a342936263
|
||||
F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
|
||||
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
|
||||
F tool/symbols.sh fec58532668296d7c7dc48be9c87f75ccdb5814f
|
||||
@ -1426,7 +1427,7 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P a2952231ac7abe165ed070875728f752ae0be608
|
||||
R da8a8c4dec6af3be8a6f4cff524d4f5f
|
||||
P 2f7778e64d93ef237e23ceac01ea9808df5cf2a1
|
||||
R e75f025e263d6165f7cd0bcd65f5ad12
|
||||
U drh
|
||||
Z f3b1e3c58e3e9ab1cb32cfa377da1181
|
||||
Z 663376736f9ceb33b8ce1b1cb94df97c
|
||||
|
@ -1 +1 @@
|
||||
2f7778e64d93ef237e23ceac01ea9808df5cf2a1
|
||||
b0b4624fc5d53bb0cc9fae7dad51984837d946ac
|
15
src/btree.c
15
src/btree.c
@ -6148,7 +6148,7 @@ static int fillInCell(
|
||||
{
|
||||
CellInfo info;
|
||||
pPage->xParseCell(pPage, pCell, &info);
|
||||
assert( nHeader=(int)(info.pPayload - pCell) );
|
||||
assert( nHeader==(int)(info.pPayload - pCell) );
|
||||
assert( info.nKey==nKey );
|
||||
assert( *pnSize == info.nSize );
|
||||
assert( spaceLeft == info.nLocal );
|
||||
@ -7807,8 +7807,8 @@ static int balance(BtCursor *pCur){
|
||||
u8 aBalanceQuickSpace[13];
|
||||
u8 *pFree = 0;
|
||||
|
||||
TESTONLY( int balance_quick_called = 0 );
|
||||
TESTONLY( int balance_deeper_called = 0 );
|
||||
VVA_ONLY( int balance_quick_called = 0 );
|
||||
VVA_ONLY( int balance_deeper_called = 0 );
|
||||
|
||||
do {
|
||||
int iPage = pCur->iPage;
|
||||
@ -7821,7 +7821,8 @@ static int balance(BtCursor *pCur){
|
||||
** and copy the current contents of the root-page to it. The
|
||||
** next iteration of the do-loop will balance the child page.
|
||||
*/
|
||||
assert( (balance_deeper_called++)==0 );
|
||||
assert( balance_deeper_called==0 );
|
||||
VVA_ONLY( balance_deeper_called++ );
|
||||
rc = balance_deeper(pPage, &pCur->apPage[1]);
|
||||
if( rc==SQLITE_OK ){
|
||||
pCur->iPage = 1;
|
||||
@ -7860,7 +7861,8 @@ static int balance(BtCursor *pCur){
|
||||
** function. If this were not verified, a subtle bug involving reuse
|
||||
** of the aBalanceQuickSpace[] might sneak in.
|
||||
*/
|
||||
assert( (balance_quick_called++)==0 );
|
||||
assert( balance_quick_called==0 );
|
||||
VVA_ONLY( balance_quick_called++ );
|
||||
rc = balance_quick(pParent, pPage, aBalanceQuickSpace);
|
||||
}else
|
||||
#endif
|
||||
@ -9327,7 +9329,8 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
|
||||
sqlite3BtreeEnter(p);
|
||||
assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
|
||||
assert( (nRef = sqlite3PagerRefcount(pBt->pPager))>=0 );
|
||||
VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) );
|
||||
assert( nRef>=0 );
|
||||
sCheck.pBt = pBt;
|
||||
sCheck.pPager = pBt->pPager;
|
||||
sCheck.nPage = btreePagecount(sCheck.pBt);
|
||||
|
@ -3566,7 +3566,7 @@ int sqlite3_test_control(int op, ...){
|
||||
*/
|
||||
case SQLITE_TESTCTRL_ASSERT: {
|
||||
volatile int x = 0;
|
||||
assert( (x = va_arg(ap,int))!=0 );
|
||||
assert( /*side-effects-ok*/ (x = va_arg(ap,int))!=0 );
|
||||
rc = x;
|
||||
break;
|
||||
}
|
||||
|
157
tool/srcck1.c
Normal file
157
tool/srcck1.c
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
** The program does some simple static analysis of the sqlite3.c source
|
||||
** file looking for mistakes.
|
||||
**
|
||||
** Usage:
|
||||
**
|
||||
** ./srcck1 sqlite3.c
|
||||
**
|
||||
** This program looks for instances of assert(), ALWAYS(), NEVER() or
|
||||
** testcase() that contain side-effects and reports errors if any such
|
||||
** instances are found.
|
||||
**
|
||||
** The aim of this utility is to prevent recurrences of errors such
|
||||
** as the one fixed at:
|
||||
**
|
||||
** https://www.sqlite.org/src/info/a2952231ac7abe16
|
||||
**
|
||||
** Note that another similar error was found by this utility when it was
|
||||
** first written. That other error was fixed by the same check-in that
|
||||
** committed the first version of this utility program.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Read the complete text of a file into memory. Return a pointer to
|
||||
** the result. Panic if unable to read the file or allocate memory.
|
||||
*/
|
||||
static char *readFile(const char *zFilename){
|
||||
FILE *in;
|
||||
char *z;
|
||||
long n;
|
||||
size_t got;
|
||||
|
||||
in = fopen(zFilename, "rb");
|
||||
if( in==0 ){
|
||||
fprintf(stderr, "unable to open '%s' for reading\n", zFilename);
|
||||
exit(1);
|
||||
}
|
||||
fseek(in, 0, SEEK_END);
|
||||
n = ftell(in);
|
||||
rewind(in);
|
||||
z = malloc( n+1 );
|
||||
if( z==0 ){
|
||||
fprintf(stderr, "cannot allocate %d bytes to store '%s'\n",
|
||||
(int)(n+1), zFilename);
|
||||
exit(1);
|
||||
}
|
||||
got = fread(z, 1, n, in);
|
||||
fclose(in);
|
||||
if( got!=(size_t)n ){
|
||||
fprintf(stderr, "only read %d of %d bytes from '%s'\n",
|
||||
(int)got, (int)n, zFilename);
|
||||
exit(1);
|
||||
}
|
||||
z[n] = 0;
|
||||
return z;
|
||||
}
|
||||
|
||||
/* Change the C code in the argument to see if it might have
|
||||
** side effects. The only accurate way to know this is to do a full
|
||||
** parse of the C code, which this routine does not do. This routine
|
||||
** uses a simple heuristic of looking for:
|
||||
**
|
||||
** * '=' not immediately after '>', '<', '!', or '='.
|
||||
** * '++'
|
||||
** * '--'
|
||||
**
|
||||
** If the code contains the phrase "side-effects-ok" is inside a
|
||||
** comment, then always return false. This is used to disable checking
|
||||
** for assert()s with deliberate side-effects, such as used by
|
||||
** SQLITE_TESTCTRL_ASSERT - a facility that allows applications to
|
||||
** determine at runtime whether or not assert()s are enabled.
|
||||
** Obviously, that determination cannot be made unless the assert()
|
||||
** has some side-effect.
|
||||
**
|
||||
** Return true if a side effect is seen. Return false if not.
|
||||
*/
|
||||
static int hasSideEffect(const char *z, unsigned int n){
|
||||
unsigned int i;
|
||||
for(i=0; i<n; i++){
|
||||
if( z[i]=='/' && strncmp(&z[i], "/*side-effects-ok*/", 19)==0 ) return 0;
|
||||
if( z[i]=='=' && i>0 && z[i-1]!='=' && z[i-1]!='>'
|
||||
&& z[i-1]!='<' && z[i-1]!='!' && z[i+1]!='=' ) return 1;
|
||||
if( z[i]=='+' && z[i+1]=='+' ) return 1;
|
||||
if( z[i]=='-' && z[i+1]=='-' ) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return the number of bytes in string z[] prior to the first unmatched ')'
|
||||
** character.
|
||||
*/
|
||||
static unsigned int findCloseParen(const char *z){
|
||||
unsigned int nOpen = 0;
|
||||
unsigned i;
|
||||
for(i=0; z[i]; i++){
|
||||
if( z[i]=='(' ) nOpen++;
|
||||
if( z[i]==')' ){
|
||||
if( nOpen==0 ) break;
|
||||
nOpen--;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Search for instances of assert(...), ALWAYS(...), NEVER(...), and/or
|
||||
** testcase(...) where the argument contains side effects.
|
||||
**
|
||||
** Print error messages whenever a side effect is found. Return the number
|
||||
** of problems seen.
|
||||
*/
|
||||
static unsigned int findAllSideEffects(const char *z){
|
||||
unsigned int lineno = 1; /* Line number */
|
||||
unsigned int i;
|
||||
unsigned int nErr = 0;
|
||||
char c, prevC = 0;
|
||||
for(i=0; (c = z[i])!=0; prevC=c, i++){
|
||||
if( c=='\n' ){ lineno++; continue; }
|
||||
if( isalpha(c) && !isalpha(prevC) ){
|
||||
if( strncmp(&z[i],"assert(",7)==0
|
||||
|| strncmp(&z[i],"ALWAYS(",7)==0
|
||||
|| strncmp(&z[i],"NEVER(",6)==0
|
||||
|| strncmp(&z[i],"testcase(",9)==0
|
||||
){
|
||||
unsigned int j, n;
|
||||
const char *z2 = &z[i+5];
|
||||
while( z2[0]!='(' ){ z2++; }
|
||||
z2++;
|
||||
n = findCloseParen(z2);
|
||||
if( hasSideEffect(z2, n) ){
|
||||
nErr++;
|
||||
fprintf(stderr, "side-effect line %u: %.*s\n", lineno,
|
||||
(int)(&z2[n+1] - &z[i]), &z[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nErr;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
char *z;
|
||||
unsigned int nErr = 0;
|
||||
if( argc!=2 ){
|
||||
fprintf(stderr, "Usage: %s FILENAME\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
z = readFile(argv[1]);
|
||||
nErr = findAllSideEffects(z);
|
||||
free(z);
|
||||
if( nErr ){
|
||||
fprintf(stderr, "Found %u undesirable side-effects\n", nErr);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user