]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add a utility program that looks for assert(), NEVER(), ALWAYS(), and
authordrh <drh@noemail.net>
Sat, 6 Feb 2016 22:32:06 +0000 (22:32 +0000)
committerdrh <drh@noemail.net>
Sat, 6 Feb 2016 22:32:06 +0000 (22:32 +0000)
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

Makefile.in
main.mk
manifest
manifest.uuid
src/btree.c
src/main.c
tool/srcck1.c [new file with mode: 0644]

index a07279c3c9aa262089b96cfeb003995c6c3949f4..f0c78a4ee77929c270a4cfdf817dde5030dc900c 100644 (file)
@@ -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
diff --git a/main.mk b/main.mk
index 1dda2d153f014c0d1ac347c43810d4cbfcb03278..f4cbbdbb95fa3287bd9d9add944b2830763b1e1d 100644 (file)
--- a/main.mk
+++ b/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
index 098cff6d3a158f3081098d0386840f805e42131e..9220387b532bf737c6ab5f5cf30638db4b19454f 100644 (file)
--- a/manifest
+++ b/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
index c92bb840b3fdf8bf33aaf510a2b1a5a543d917a8..20460304dd00e5a4720a7b8baf2ec407f815497d 100644 (file)
@@ -1 +1 @@
-2f7778e64d93ef237e23ceac01ea9808df5cf2a1
\ No newline at end of file
+b0b4624fc5d53bb0cc9fae7dad51984837d946ac
\ No newline at end of file
index b99820ddbc5e845ff384fa57974656ae7a458f73..c6f9c34f7b32dffb3c79f185688c4365f8d2c71d 100644 (file)
@@ -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);
index 588461b48a4d46256cd8adfbf6a53aec309ca3c5..922af1315a35a3720ecca4be1ec5ee768ca82df5 100644 (file)
@@ -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;
     }
diff --git a/tool/srcck1.c b/tool/srcck1.c
new file mode 100644 (file)
index 0000000..cd4b499
--- /dev/null
@@ -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; 
+}