4 # Copyright (C) 2000 Red Hat
6 # Procedures for a monkey testing library. These procs are used to
7 # fire random input to a victim component. If sid does not close its
8 # stdin (ie crash), then the test is considered to have passed.
10 # State save/restore functionality is sometimes tested also. It confirms that
11 # standard attributes recover their save-time values at any subsequent restore.
12 # Dejagnu WARNINGs are emitted when something was overlooked.
14 # monkey_start and monkey_stop must be called at the start and end of
15 # the test case. monkey_test can run a complete monkey test on a
20 # Return a random number between 0 and n-1.
22 return [expr [expr int(16777216 * [expr rand()])] % int($n)]
26 # generate string with sequential chars between [low .. high]
27 proc string_spectrum {low high} {
29 for {set i $low} {$i <= $high} {incr i} {
32 return [binary format c* $chars]
36 set charsets(0) "0123456789"; set lenexps(0) 4
37 set charsets(1) [string_spectrum 0 255] ; set lenexps(1) 12
38 set charsets(2) "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; set lenexps(2) 8
39 set charsets(3) "bx-0123456789abcdef"; set lenexps(3) 4
41 proc random_string {} {
42 global charsets lenexps
44 set charset $charsets($class)
45 set lenexp $lenexps($class)
47 set numchars [string length $charset]
48 set len [random [expr pow(2,$lenexp)]]
50 for {set i 0} {$i < $len} {incr i} {
51 append garbage [string index $charset [random $numchars]]
57 # Override these procs in client .exp files to make monkey avoid accessing
58 # specific pins/buses/attributes, based on respective names.
60 proc pin_safe_p {pin_name} { return 1 }
61 proc bus_safe_p {bus_name} { return 1 }
62 proc attr_safe_p {attr_name} { return 1 }
63 proc state_safe_p {} { return 1 }
64 proc state_saved_p {attr_name} { return 1 }
66 # eg., add parameters for operation type, non-name arguments
70 # Run a monkey test for a component.
71 # type -- the component type.
72 # symbol -- the name of the component library header.
73 # lib -- the library name (e.g. libtimers.la).
74 # iterations -- the number of random events to generate for the test.
75 # (may be overridden by a SID_MONKEYS environment variable)
77 proc monkey_test {type sym lib {iterations 30s}} {
79 global pins buses attributes
82 if [info exist env(SID_MONKEYS)] {
83 set iterations $env(SID_MONKEYS)
87 set test "monkey test $type"
88 if {[monkey_start $type $sym $lib] < 0} then { return }
90 # If $iterations ends in a recognised time specifier
91 # (e.g. m => minutes), then run the tests for the specified
92 # duration, rather than as a fixed number of iterations.
94 if {[regexp {[smhd]$} $iterations]} {
95 regexp {([0-9]+)[smhd]$} $iterations ignore units
96 set now [clock seconds]
97 switch [string range $iterations end end] {
98 s { set finishtime [expr $units + $now] }
99 m { set finishtime [expr 60 * $units + $now] }
100 h { set finishtime [expr 3600 * $units + $now] }
101 d { set finishtime [expr 3600 * 24 * $units + $now] }
102 default { error "unknown unit of time in $iterations" }
104 note "Testing $type until [clock format $finishtime]"
105 while {[clock seconds] < $finishtime} {
109 for {set i 0} {$i < $iterations} {incr i} {
114 pass "$test ($iterations iterations)"
116 catch {unset pins buses attributes}
118 # restore hooks for next monkey_test
119 proc pin_safe_p {pin_name} { return 1 }
120 proc bus_safe_p {bus_name} { return 1 }
121 proc attr_safe_p {attr_name} { return 1 }
122 proc state_safe_p {} { return 1 }
123 proc state_saved_p {attr_name} { return 1 }
127 # Do one monkey testing round
128 proc do_one_test {} {
129 global num_state_snapshots
131 set value [random 100]
132 # skew distribution toward effective tests
133 if {$value < 30} then {
135 } elseif {$value < 50} then {
137 } elseif {$value < 96} then {
138 monkey_test_attribute
139 } elseif {$value < 98} then {
140 monkey_test_state_save
142 monkey_test_state_restore
148 # Start the monkey test--only called by monkey_test. Return <0 on failure.
149 proc monkey_start {type sym lib} {
151 global pins buses attributes
152 global num_state_snapshots
153 global state_snapshots
154 global state_snapshot_attrs state_snapshot_bad_attrs
156 sid_config_component_test_with_tracing \
157 "monkey.conf" "load [sid_find_file $lib] $sym" $type
159 sid_start "monkey.conf"
160 if {$victim == ""} then { fail "$test - sid startup"; return -1 }
162 catch {unset pins buses attributes}
163 set pins [sid_cmd [list sid::component::pin_names $victim]]
164 set buses [sid_cmd [list sid::component::bus_names $victim]]
165 set attributes [sid_cmd [list sid::component::attribute_names $victim]]
167 catch {unset num_state_snapshots state_snapshots state_snapshot_attrs state_snapshot_bad_attrs}
168 set num_state_snapshots 0
169 set state_snapshot_bad_attrs [list]
174 # Monkey test a pin (decided at runtime).
175 proc monkey_test_pin {} {
179 if {[llength $pins] == 0} then { return }
180 set pin [lindex $pins [random [llength $pins]]]
181 if {! [pin_safe_p $pin]} then { return }
183 set handle [sid_cmd [list sid::component::find_pin $victim $pin]]
184 if {$handle == ""} then {
185 # probably just output-only pin
189 # Value-carrying pin.
190 set value [random [expr int(pow(2,16))]]
191 sid_cmd [list sid::pin::driven_h4 $handle $value]
194 # Monkey test a bus (transaction type and width decided at runtime).
195 # The entire 2^32 address space is not tested, but the important
196 # addresses lie in the 0 to 1K range.
198 proc monkey_test_bus {} {
202 if {[llength $buses] == 0} then { return }
203 set bus [lindex $buses [random [llength $buses]]]
204 if {! [bus_safe_p $bus]} then { return }
206 set handle [sid_cmd [list sid::component::find_bus $victim $bus]]
208 # Pick an endianness.
209 if {[random 2] > 0} { set endian b } else { set endian l }
217 default { warning "This should never happen!" }
221 set addr [random [expr int(pow(2,30))]]
223 # Pick read or write operation.
224 if {[random 2] > 0} { set op write } else { set op read }
227 if {$op == "write"} {
229 1 { set data [random 256] }
230 2 { set data [random 65536] }
231 4 { set data [random [expr int(pow(2,30))]] }
232 8 { set data [random [expr int(pow(2,30))]] }
234 sid_cmd [list sid::bus::write_h4_$endian$width $handle $addr $data]
236 sid_cmd [list sid::bus::read_h4_$endian$width $handle $addr]
240 # Monkey test an attribute (decided at runtime).
241 # A random string of garbage, composed of a random number of
242 # characters from $charset is used.
244 proc monkey_test_attribute {} {
245 global charset numchars
246 global attributes victim
248 # Pick a random attribute.
249 if {[llength $attributes] == 0} then { return }
250 set attr [lindex $attributes [random [llength $attributes]]]
251 if {! [attr_safe_p $attr]} then { return }
253 set garbage [random_string]
254 sid_cmd [list sid::component::set_attribute_value $victim $attr $garbage]
258 # Accumulate a state snapshot of the target component.
259 # Collect both the "state-snapshot" attribute, and also pin/register/setting attributes.
260 proc monkey_test_state_save {} {
261 global num_state_snapshots
262 global state_snapshots
263 global state_snapshot_attrs
266 # do no work if this is deemed unsafe
267 if {! [state_safe_p]} then { return }
269 set state [sid_cmd [list sid::component::attribute_value $victim state-snapshot]]
270 if {$state == ""} then {
271 # state save/restore apparently not supported
275 incr num_state_snapshots
276 set state_snapshots($num_state_snapshots) $state
278 # fetch all user-visible attribute names
280 foreach category {pin setting register} {
281 set attrlist [sid_cmd [list sid::component::attribute_names_in_category $victim $category]]
282 foreach an $attrlist {
283 lappend attrnames $an
286 # collect their current values
287 foreach attrname $attrnames {
288 set attrvalue [sid_cmd [list sid::component::attribute_value $victim $attrname]]
289 set state_snapshot_attrs($num_state_snapshots,$attrname) $attrvalue
294 # Pick a random state snapshot accumulated from the target component and attempt to
295 # restore it. Confirm that user-visible attributes are back to their old values.
296 proc monkey_test_state_restore {} {
297 global num_state_snapshots
298 global state_snapshots
299 global state_snapshot_attrs state_snapshot_bad_attrs
303 if {$num_state_snapshots == 0} then {
307 set pick [expr [random $num_state_snapshots] + 1]
309 set state $state_snapshots($pick)
310 set result [sid_cmd [list sid::component::set_attribute_value $victim state-snapshot $state]]
311 if {$result != "ok"} then {
312 # don't warn over and over again - dummy attribute
313 set attrname "state-snapshot"
314 if {[lsearch -exact $state_snapshot_bad_attrs $attrname] == -1} then {
315 warning "$test - state restore failed"
316 lappend state_snapshot_bad_attrs $attrname
320 # fetch all user-visible attribute names
322 foreach category {pin setting register} {
323 set attrlist [sid_cmd [list sid::component::attribute_names_in_category $victim $category]]
324 foreach an $attrlist {
325 lappend attrnames $an
328 # compare their current values
329 foreach attrname $attrnames {
330 set attrvalue1 [sid_cmd [list sid::component::attribute_value $victim $attrname]]
331 set attrvalue2 $state_snapshot_attrs($pick,$attrname)
332 if {$attrvalue1 != $attrvalue2} then {
333 # quietly accept failure for known-unsaved attributes
334 if {! [state_saved_p $attrname]} then {
335 lappend state_snapshot_bad_attrs $attrname
337 # don't warn over and over again
338 if {[lsearch -exact $state_snapshot_bad_attrs $attrname] == -1} then {
339 warning "$test - state restore did not preserve $attrname"
340 lappend state_snapshot_bad_attrs $attrname