Re: [PATCH 3/3][gdb/testsuite] Warn about leaked global array

Tom de Vries
2020-06-03T08:47:48Z

On 02-06-2020 22:18, Andrew Burgess wrote:
> * Andrew Burgess <andrew.burgess@embecosm.com> [2020-06-02 18:01:39 +0100]:
>
>> * Tom de Vries <tdevries@suse.de> [2020-06-02 18:31:39 +0200]:
>>
>>> On 02-06-2020 17:52, Andrew Burgess wrote:
>>>> * Andrew Burgess <andrew.burgess@embecosm.com> [2020-06-02 16:38:30 +0100]:
>>>>
>>>>> * Tom de Vries <tdevries@suse.de> [2020-05-19 18:30:06 +0200]:
>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> A variable name cannot be used both as scalar and array without an
>>>>>> intermediate unset. Trying to do so will result in tcl errors, for
>>>>>> example, for:
>>>>>> ...
>>>>>> set var "bla"
>>>>>> set var(1) "bla"
>>>>>> ...
>>>>>> we get:
>>>>>> ...
>>>>>> can't set "var(1)": variable isn't array
>>>>>> ...
>>>>>> and for the reverse statement order we get:
>>>>>> ...
>>>>>> can't set "var": variable is array
>>>>>> ...
>>>>>>
>>>>>> So, since a global name in one test-case can leak to another
>>>>>> test-case, setting a global name in one test-case can result in
>>>>>> a tcl error in another test-case that reuses the name in a different
>>>>>> way.
>>>>>>
>>>>>> Warn about leaking a global array from a test-case.
>>>>>>
>>>>>> Also, add a possibility to skip the warning in a given test-case using
>>>>>> variable gdb_skip_check_global_vars, and use it in gdb.mi/mi2-var-child.exp
>>>>>> and gdb.mi/mi-var-cp.exp.
>>>>>>
>>>>>> Tested on x86_64-linux.
>>>>>>
>>>>>> Any comments?
>>>>>
>>>>> If we're going to add code to loop over all globals anyway, then why
>>>>> not, instead of warning about bad cases, and then wrapping tests in a
>>>>> namespace, just have this code "fix" the leaked globals by deleting
>>>>> them?
>>>>>
>>>>> My thinking is that any global that exists when we start a test should
>>>>> continue to exist at the end of the test. Any other global should
>>>>> just be unset when the test script finishes.
>>>>>
>>>>> If there really is some global state that is lazily created by a
>>>>> particular test script then (a) this seems like a bug anyway, and (b)
>>>>> this is easy to fix by giving it an earlier creation / initialisation.
>>>>>
>>>>> In this way, folk can just write test scripts, dump their junk all
>>>>> over the global namespace as they like, and we'll just clean up for
>>>>> them.
>>>>>
>>>>> Thoughts?
>>>>
>>>> Here's a really quick patch implementing the idea above. It needs
>>>> cleaning up and commenting, etc, but it passes the testsuite with no
>>>> regressions, and taking a look at its debug output, I can see it
>>>> deleting some of the problem global arrays that are causing issues.
>>>>
>>>> What do you think of this approach?
>>>>
>>>> Thanks,
>>>> Andrew
>>>>
>>>> ----
>>>>
>>>> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
>>>> index 444cea01c36..c983c9cc172 100644
>>>> --- a/gdb/testsuite/lib/gdb.exp
>>>> +++ b/gdb/testsuite/lib/gdb.exp
>>>> @@ -5094,7 +5094,11 @@ set banned_procedures { strace }
>>>> # if the banned variables and procedures are already traced.
>>>> set banned_traced 0
>>>>
>>>> +array set known_globals {}
>>>> +
>>>> proc gdb_init { test_file_name } {
>>>> + global known_globals
>>>> +
>>>> # Reset the timeout value to the default. This way, any testcase
>>>> # that changes the timeout value without resetting it cannot affect
>>>> # the timeout used in subsequent testcases.
>>>> @@ -5196,13 +5200,27 @@ proc gdb_init { test_file_name } {
>>>> global gdb_instances
>>>> set gdb_instances 0
>>>>
>>>> - return [default_gdb_init $test_file_name]
>>>> + set result [default_gdb_init $test_file_name]
>>>> +
>>>> + foreach varname [info globals] {
>>>> + set known_globals($varname) 1
>>>> + }
>>>> +
>>>> + return $result
>>>> }
>>>>
>>>> proc gdb_finish { } {
>>>> global gdbserver_reconnect_p
>>>> global gdb_prompt
>>>> global cleanfiles
>>>> + global known_globals
>>>> +
>>>> + foreach varname [info globals] {
>>>> + if {![info exists known_globals($varname)]} {
>>>> + verbose -log "APB: Deleting '$varname'"
>>>> + upvar 0 unset $varname
>>>> + }
>>>> + }
>>>
>>> I'm not against the approach as such (I remember also trying this out
>>> initially).
>>>
>>> I don't think this use of upvar does any unset though.
>>
>> *cough* yeah, I knew that.
>>
>> Fixed, and _confirmed_ that it's deleting the globals this time, not
>> just reporting what it would delete.
>
> Here's a cleaned up version for consideration.
>

I ran this through the testsuite. There seems to be some problems left,
see incomplete dump below.

Thanks,
- Tom

ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp95 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp96 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp96 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: can't read "mi_gdb_prompt": no such variable
while executing
"expect {
-i exp97 -timeout 10
-re "~\"GNU.*\r\n~\".*$mi_gdb_prompt$" {
# We have a new format mi startup prompt. If we are
# running mi1,..."
("uplevel" body line 1)
invoked from within
"uplevel $body" TCL READ VARNAME can't read "mi_gdb_prompt": no such
variable
ERROR: (timeout) GDB never initialized after 10 seconds.
ERROR: tcl error sourcing
/data/gdb_versions/devel/src/gdb/testsuite/gdb.base/jit-elf.exp.
ERROR: can't read "jit_load_address": no such variable
while executing
"expr $jit_load_address + $jit_load_increment * [expr $i-1]"
(procedure "compile_and_download_n_jit_so" line 12)
invoked from within
"compile_and_download_n_jit_so \
$jit_solib_basename $jit_solib_srcfile 2"
(file
"/data/gdb_versions/devel/src/gdb/testsuite/gdb.base/jit-elf.exp" line 134)
invoked from within
"source /data/gdb_versions/devel/src/gdb/testsuite/gdb.base/jit-elf.exp"
("uplevel" body line 1)
invoked from within
"uplevel #0 source
/data/gdb_versions/devel/src/gdb/testsuite/gdb.base/jit-elf.exp"
invoked from within
"catch "uplevel #0 source $test_file_name""
FAIL: gdb.btrace/reconnect.exp: first: record btrace enable
FAIL: gdb.btrace/reconnect.exp: first: stepi 19 (the program is no
longer running)
FAIL: gdb.btrace/reconnect.exp: first: info record
FAIL: gdb.btrace/reconnect.exp: first: disconnect
FAIL: gdb.btrace/reconnect.exp: second: info record
FAIL: gdb.btrace/reconnect.exp: second: record stop
FAIL: gdb.btrace/reconnect.exp: second: disconnect
FAIL: gdb.btrace/reconnect.exp: third: info record
ERROR: tcl error sourcing
/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/derivation.exp.
ERROR: can't read "debug_cp_test_ptype_class": no such variable
while executing
"if {$debug_cp_test_ptype_class} {
verbose -log $msg
}"
(procedure "cp_ptype_class_verbose" line 4)
invoked from within
"cp_ptype_class_verbose "next line element: \"$elem\"""
(procedure "cp_support_internal::next_line" line 12)
invoked from within
"cp_support_internal::next_line $line_queue"
(procedure "cp_test_ptype_class" line 183)
invoked from within
"cp_test_ptype_class \
"a_instance" "" "class" "A" \
{
{ field public "A::value_type a;" }
{ field public "A::value_type aa;" }
{ method p..."
(file
"/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/derivation.exp" line 80)
invoked from within
"source /data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/derivation.exp"
("uplevel" body line 1)
invoked from within
"uplevel #0 source
/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/derivation.exp"
invoked from within
"catch "uplevel #0 source $test_file_name""
ERROR: tcl error sourcing
/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/inherit.exp.
ERROR: can't read "debug_cp_test_ptype_class": no such variable
while executing
"if {$debug_cp_test_ptype_class} {
verbose -log $msg
}"
(procedure "cp_ptype_class_verbose" line 4)
invoked from within
"cp_ptype_class_verbose "next line element: \"$elem\"""
(procedure "cp_support_internal::next_line" line 12)
invoked from within
"cp_support_internal::next_line $line_queue"
(procedure "cp_test_ptype_class" line 183)
invoked from within
"cp_test_ptype_class "A" "ptype A (FIXME)" "class" "A" {
{ field public "int a;" }
{ field public "int x;" }
}"
(procedure "test_ptype_si" line 8)
invoked from within
"test_ptype_si"
(procedure "do_tests" line 10)
invoked from within
"do_tests"
(file
"/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/inherit.exp" line 722)
invoked from within
"source /data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/inherit.exp"
("uplevel" body line 1)
invoked from within
"uplevel #0 source
/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/inherit.exp"
invoked from within
"catch "uplevel #0 source $test_file_name""
ERROR: tcl error sourcing
/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/nested-types.exp.
ERROR: can't read "debug_cp_test_ptype_class": no such variable
while executing
"if {$debug_cp_test_ptype_class} {
verbose -log $msg
}"
(procedure "cp_ptype_class_verbose" line 4)
invoked from within
"cp_ptype_class_verbose "next line element: \"$elem\"""
(procedure "cp_support_internal::next_line" line 12)
invoked from within
"cp_support_internal::next_line $line_queue"
(procedure "cp_test_ptype_class" line 183)
invoked from within
"cp_test_ptype_class $name "ptype $name (limit = $limit)" $key $name
$children"
("uplevel" body line 2)
invoked from within
"uplevel 1 $body"
invoked from within
"with_timeout_factor 1 {
cp_test_ptype_class $name "ptype $name (limit = $limit)"
$key $name $children
}"
("uplevel" body line 1)
invoked from within
"uplevel [list with_timeout_factor $factor $body]"
(procedure "with_read1_timeout_factor" line 8)
invoked from within
"with_read1_timeout_factor $read1_timeout_factor {
cp_test_ptype_class $name "ptype $name (limit = $limit)"
$key $name $children
}"
(procedure "test_nested_limit" line 33)
invoked from within
"test_nested_limit -1 false"
(file
"/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/nested-types.exp"
line 317)
invoked from within
"source /data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/nested-types.exp"
("uplevel" body line 1)
invoked from within
"uplevel #0 source
/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/nested-types.exp"
invoked from within
"catch "uplevel #0 source $test_file_name""
FAIL: gdb.cp/no-dmgl-verbose.exp: setting breakpoint at 'f(std::string)'
ERROR: tcl error sourcing
/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/ptype-flags.exp.
ERROR: can't read "debug_cp_test_ptype_class": no such variable
while executing
"if {$debug_cp_test_ptype_class} {
verbose -log $msg
}"
(procedure "cp_ptype_class_verbose" line 4)
invoked from within
"cp_ptype_class_verbose "next line element: \"$elem\"""
(procedure "cp_support_internal::next_line" line 12)
invoked from within
"cp_support_internal::next_line $line_queue"
(procedure "cp_test_ptype_class" line 183)
invoked from within
"cp_test_ptype_class value $name "class" "Holder<int>" $contents "" {}
$flags"
(procedure "do_check" line 26)
invoked from within
"do_check "basic test""
(file
"/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/ptype-flags.exp" line 65)
invoked from within
"source /data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/ptype-flags.exp"
("uplevel" body line 1)
invoked from within
"uplevel #0 source
/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/ptype-flags.exp"
invoked from within
"catch "uplevel #0 source $test_file_name""
ERROR: tcl error sourcing
/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/rvalue-ref-overload.exp.
ERROR: can't read "debug_cp_test_ptype_class": no such variable
while executing
"if {$debug_cp_test_ptype_class} {
verbose -log $msg
}"
(procedure "cp_ptype_class_verbose" line 4)
invoked from within
"cp_ptype_class_verbose "next line element: \"$elem\"""
(procedure "cp_support_internal::next_line" line 12)
invoked from within
"cp_support_internal::next_line $line_queue"
(procedure "cp_test_ptype_class" line 183)
invoked from within
"cp_test_ptype_class "foo_rr_instance1" "" "class" "foo" \
{
{ method public "foo(void);" }
{ method public "foo(foo_lval_ref);" }
{ method publ..."
(file
"/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/rvalue-ref-overload.exp"
line 44)
invoked from within
"source
/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/rvalue-ref-overload.exp"
("uplevel" body line 1)
invoked from within
"uplevel #0 source
/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/rvalue-ref-overload.exp"
invoked from within
"catch "uplevel #0 source $test_file_name""
ERROR: tcl error sourcing
/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/virtfunc.exp.
ERROR: can't read "debug_cp_test_ptype_class": no such variable
while executing
"if {$debug_cp_test_ptype_class} {
verbose -log $msg
}"
(procedure "cp_ptype_class_verbose" line 4)
invoked from within
"cp_ptype_class_verbose "next line element: \"$elem\"""
(procedure "cp_support_internal::next_line" line 12)
invoked from within
"cp_support_internal::next_line $line_queue"
(procedure "cp_test_ptype_class" line 183)
invoked from within
"cp_test_ptype_class "VA" "" "class" "VA" {
{ field public "int va;" }
}"
(procedure "test_ptype_of_classes" line 5)
invoked from within
"test_ptype_of_classes"
(procedure "do_tests" line 9)
invoked from within
"do_tests"
(file
"/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/virtfunc.exp" line 292)
invoked from within
"source /data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/virtfunc.exp"
("uplevel" body line 1)
invoked from within
"uplevel #0 source
/data/gdb_versions/devel/src/gdb/testsuite/gdb.cp/virtfunc.exp"
invoked from within
"catch "uplevel #0 source $test_file_name""