blob: 76194c724287346ecb61fc2883520a5dcedae012 [file] [log] [blame]
danielk1977c9cf9012007-05-30 10:36:47 +00001
danielk1977cdc3a6b2007-08-25 13:09:26 +00002ifcapable !memdebug {
3 puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
4 finish_test
5 return
6}
7
danielk1977c9cf9012007-05-30 10:36:47 +00008# Usage: do_malloc_test <test number> <options...>
9#
10# The first argument, <test number>, is an integer used to name the
11# tests executed by this proc. Options are as follows:
12#
13# -tclprep TCL script to run to prepare test.
14# -sqlprep SQL script to run to prepare test.
15# -tclbody TCL script to run with malloc failure simulation.
16# -sqlbody TCL script to run with malloc failure simulation.
17# -cleanup TCL script to run after the test.
18#
19# This command runs a series of tests to verify SQLite's ability
20# to handle an out-of-memory condition gracefully. It is assumed
21# that if this condition occurs a malloc() call will return a
22# NULL pointer. Linux, for example, doesn't do that by default. See
23# the "BUGS" section of malloc(3).
24#
25# Each iteration of a loop, the TCL commands in any argument passed
26# to the -tclbody switch, followed by the SQL commands in any argument
27# passed to the -sqlbody switch are executed. Each iteration the
28# Nth call to sqliteMalloc() is made to fail, where N is increased
29# each time the loop runs starting from 1. When all commands execute
30# successfully, the loop ends.
31#
32proc do_malloc_test {tn args} {
33 array unset ::mallocopts
34 array set ::mallocopts $args
35
36 if {[string is integer $tn]} {
37 set tn malloc-$tn
38 }
drhf3a65f72007-08-22 20:18:21 +000039 if {[info exists ::mallocopts(-start)]} {
40 set start $::mallocopts(-start)
41 } else {
42 set start 1
43 }
danielk1977c9cf9012007-05-30 10:36:47 +000044
danielk1977a1644fd2007-08-29 12:31:25 +000045 foreach ::iRepeat {0 1} {
46 set ::go 1
47 for {set ::n $start} {$::go && $::n < 50000} {incr ::n} {
danielk1977c9cf9012007-05-30 10:36:47 +000048
danielk1977a1644fd2007-08-29 12:31:25 +000049 # If $::iRepeat is 0, then the malloc() failure is transient - it
50 # fails and then subsequent calls succeed. If $::iRepeat is 1,
51 # then the failure is persistent - once malloc() fails it keeps
52 # failing.
danielk1977c9cf9012007-05-30 10:36:47 +000053 #
danielk1977a1644fd2007-08-29 12:31:25 +000054 set zRepeat "transient"
55 if {$::iRepeat} {set zRepeat "persistent"}
danielk1977c9cf9012007-05-30 10:36:47 +000056
danielk1977a1644fd2007-08-29 12:31:25 +000057 do_test ${tn}.${zRepeat}.${::n} {
58
59 # Remove all traces of database files test.db and test2.db
60 # from the file-system. Then open (empty database) "test.db"
61 # with the handle [db].
62 #
63 catch {db close}
64 catch {file delete -force test.db}
65 catch {file delete -force test.db-journal}
66 catch {file delete -force test2.db}
67 catch {file delete -force test2.db-journal}
68 if {[info exists ::mallocopts(-testdb)]} {
69 file copy $::mallocopts(-testdb) test.db
danielk1977c9cf9012007-05-30 10:36:47 +000070 }
danielk1977a1644fd2007-08-29 12:31:25 +000071 catch {sqlite3 db test.db}
72
73 # Execute any -tclprep and -sqlprep scripts.
74 #
75 if {[info exists ::mallocopts(-tclprep)]} {
76 eval $::mallocopts(-tclprep)
77 }
78 if {[info exists ::mallocopts(-sqlprep)]} {
79 execsql $::mallocopts(-sqlprep)
80 }
81
82 # Now set the ${::n}th malloc() to fail and execute the -tclbody
83 # and -sqlbody scripts.
84 #
85 sqlite3_memdebug_fail $::n -repeat $::iRepeat
86 set ::mallocbody {}
87 if {[info exists ::mallocopts(-tclbody)]} {
88 append ::mallocbody "$::mallocopts(-tclbody)\n"
89 }
90 if {[info exists ::mallocopts(-sqlbody)]} {
91 append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
92 }
danielk1977c9cf9012007-05-30 10:36:47 +000093
danielk1977a1644fd2007-08-29 12:31:25 +000094 # The following block sets local variables as follows:
95 #
96 # isFail - True if an error (any error) was reported by sqlite.
97 # nFail - The total number of simulated malloc() failures.
98 # nBenign - The number of benign simulated malloc() failures.
99 #
100 set isFail [catch $::mallocbody msg]
101 set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
102#puts "isFail=$isFail nFail=$nFail nBenign=$nBenign msg=$msg"
103
104 # If one or more mallocs failed, run this loop body again.
105 #
106 set go [expr {$nFail>0}]
107
108 if {($nFail-$nBenign)==0} {
109 if {$isFail} {
110 set v2 $msg
111 } else {
112 set isFail 1
113 set v2 1
114 }
115 } elseif {!$isFail} {
116 set v2 $msg
117 } elseif {[info command db]=="" || [db errorcode]==7
118 || $msg=="out of memory"} {
119 set v2 1
120 } else {
121 set v2 $msg
122 }
123 lappend isFail $v2
124 } {1 1}
125
126 if {[info exists ::mallocopts(-cleanup)]} {
127 catch [list uplevel #0 $::mallocopts(-cleanup)] msg
128 }
danielk1977c9cf9012007-05-30 10:36:47 +0000129 }
130 }
131 unset ::mallocopts
132}