1# 2007 April 30 2# 3# The author disclaims copyright to this source code. In place of 4# a legal notice, here is a blessing: 5# 6# May you do good and not evil. 7# May you find forgiveness for yourself and forgive others. 8# May you share freely, never taking more than you give. 9# 10#*********************************************************************** 11# This file contains additional out-of-memory checks (see malloc.tcl). 12# 13# $Id: mallocA.test,v 1.2 2007/05/12 15:00:15 drh Exp $ 14 15set testdir [file dirname $argv0] 16source $testdir/tester.tcl 17 18# Only run these tests if memory debugging is turned on. 19# 20if {[info command sqlite_malloc_stat]==""} { 21 puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..." 22 finish_test 23 return 24} 25 26# Usage: do_malloc_test <test number> <options...> 27# 28# The first argument, <test number>, is an integer used to name the 29# tests executed by this proc. Options are as follows: 30# 31# -tclprep TCL script to run to prepare test. 32# -sqlprep SQL script to run to prepare test. 33# -tclbody TCL script to run with malloc failure simulation. 34# -sqlbody TCL script to run with malloc failure simulation. 35# -cleanup TCL script to run after the test. 36# 37# This command runs a series of tests to verify SQLite's ability 38# to handle an out-of-memory condition gracefully. It is assumed 39# that if this condition occurs a malloc() call will return a 40# NULL pointer. Linux, for example, doesn't do that by default. See 41# the "BUGS" section of malloc(3). 42# 43# Each iteration of a loop, the TCL commands in any argument passed 44# to the -tclbody switch, followed by the SQL commands in any argument 45# passed to the -sqlbody switch are executed. Each iteration the 46# Nth call to sqliteMalloc() is made to fail, where N is increased 47# each time the loop runs starting from 1. When all commands execute 48# successfully, the loop ends. 49# 50proc do_malloc_test {tn args} { 51 array unset ::mallocopts 52 array set ::mallocopts $args 53 54 set ::go 1 55 for {set ::n 1} {$::go && $::n < 50000} {incr ::n} { 56 do_test mallocA-$tn.$::n { 57 58 sqlite_malloc_fail 0 59 catch {db close} 60 catch {file delete -force test.db test.db-journal} 61 catch {file copy test.db.bu test.db} 62 sqlite3 db test.db 63 set ::DB [sqlite3_connection_pointer db] 64 65 # Execute any -tclprep and -sqlprep scripts. 66 # 67 if {[info exists ::mallocopts(-tclprep)]} { 68 eval $::mallocopts(-tclprep) 69 } 70 if {[info exists ::mallocopts(-sqlprep)]} { 71 execsql $::mallocopts(-sqlprep) 72 } 73 74 # Now set the ${::n}th malloc() to fail and execute the -tclbody and 75 # -sqlbody scripts. 76 # 77 sqlite_malloc_fail $::n 78 set ::mallocbody {} 79 if {[info exists ::mallocopts(-tclbody)]} { 80 append ::mallocbody "$::mallocopts(-tclbody)\n" 81 } 82 if {[info exists ::mallocopts(-sqlbody)]} { 83 append ::mallocbody "db eval {$::mallocopts(-sqlbody)}" 84 } 85 set v [catch $::mallocbody msg] 86 87 # If the test fails (if $v!=0) and the database connection actually 88 # exists, make sure the failure code is SQLITE_NOMEM. 89 if {$v && [info command db]=="db" && [info exists ::mallocopts(-sqlbody)] 90 && [db errorcode]!=7} { 91 set v 999 92 } 93 94 set leftover [lindex [sqlite_malloc_stat] 2] 95 if {$leftover>0} { 96 if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v Message=$msg"} 97 set ::go 0 98 if {$v} { 99 puts "\nError message returned: $msg" 100 } else { 101 set v {1 1} 102 } 103 } else { 104 set v2 [expr {$msg=="" || [regexp {out of memory} $msg]}] 105 if {!$v2} {puts "\nError message returned: $msg"} 106 lappend v $v2 107 } 108 } {1 1} 109 110 if {[info exists ::mallocopts(-cleanup)]} { 111 catch [list uplevel #0 $::mallocopts(-cleanup)] msg 112 } 113 } 114 unset ::mallocopts 115} 116 117# Construct a test database 118# 119file delete -force test.db.bu 120db eval { 121 CREATE TABLE t1(a COLLATE NOCASE,b,c); 122 INSERT INTO t1 VALUES(1,2,3); 123 INSERT INTO t1 VALUES(1,2,4); 124 INSERT INTO t1 VALUES(2,3,4); 125 CREATE INDEX t1i1 ON t1(a); 126 CREATE INDEX t1i2 ON t1(b,c); 127 CREATE TABLE t2(x,y,z); 128} 129db close 130file copy test.db test.db.bu 131sqlite3 db test.db 132 133 134do_malloc_test 1 -sqlbody { 135 ANALYZE 136} 137do_malloc_test 2 -sqlbody { 138 REINDEX; 139} 140do_malloc_test 3 -sqlbody { 141 REINDEX t1; 142} 143do_malloc_test 4 -sqlbody { 144 REINDEX main.t1; 145} 146do_malloc_test 5 -sqlbody { 147 REINDEX nocase; 148} 149 150# Ensure that no file descriptors were leaked. 151do_test malloc-99.X { 152 catch {db close} 153 set sqlite_open_file_count 154} {0} 155 156file delete -force test.db.bu 157sqlite_malloc_fail 0 158finish_test 159