Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH] Fix nested gdb_caching_proc with args; Fix gdb.rocm/ tests
@ 2025-09-15 21:28 Pedro Alves
  2025-09-15 21:54 ` Pedro Alves
  0 siblings, 1 reply; 6+ messages in thread
From: Pedro Alves @ 2025-09-15 21:28 UTC (permalink / raw)
  To: gdb-patches

Commit d09eba07 ("Make get_compiler_info use gdb_caching_proc")
regressed the gdb.rocm/ testsuite.  E.g.:

 Running /home/pedro/rocm/gdb/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.rocm/simple.exp ...
 ERROR: tcl error sourcing /home/pedro/rocm/gdb/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.rocm/simple.exp.
 ERROR: tcl error code TCL WRONGARGS
 ERROR: wrong # args: should be "gdb_real__get_compiler_info_1 language"
     while executing
 "gdb_real__get_compiler_info_1"
     ("uplevel" body line 1)
     invoked from within
 "uplevel 2 $real_name"
     (procedure "gdb_do_cache_wrap" line 3)
     invoked from within
 "gdb_do_cache_wrap $real_name {*}$args"
     (procedure "gdb_do_cache" line 98)
     invoked from within

This is actually a latent problem in gdb_do_cache_wrap, introduced in:

 commit 71f1ab80f1aabd70bce526635f84c7b849e8a0f4
 CommitDate: Mon Mar 6 16:49:19 2023 +0100

    [gdb/testsuite] Allow args in gdb_caching_proc

This change:

   # Call proc real_name and return the result, while ignoring calls to pass.
  -proc gdb_do_cache_wrap {real_name} {
  +proc gdb_do_cache_wrap {real_name args} {
       if { [info procs save_pass] != "" } {
	  return [uplevel 2 $real_name]   <<<<<<<<<<<<<<<<<<<<<<< HERE
       }
  @@ -31,7 +31,7 @@ proc gdb_do_cache_wrap {real_name} {
       rename pass save_pass
       rename ignore_pass pass

  -    set code [catch {uplevel 2 $real_name} result]
  +    set code [catch {uplevel 2 [list $real_name {*}$args]} result]

Missed updating the line marked with HERE above, to pass down $args.
So the case of a caching proc calling another caching proc with args
isn't handled correctly.

We could fix this by fixing the HERE line like so:

 -	  return [uplevel 2 $real_name]
 +	  return [uplevel 2 [list $real_name {*}$args]]

However, we have with_override nowadays that we can use here which
eliminates the duplicated logic, which was what was missed originally.

A new test that exposes the problem is added to
gdb.testsuite/gdb-caching-proc.exp.

This also adds a new test to gdb.testsuite/with-override.exp that I
think was missing, making sure that the inner foo override restores
the outer foo override.

Change-Id: I8b2a7366bf910902fe5f547bde58c3b475bf5133
---
 .../gdb.testsuite/gdb-caching-proc.exp        | 21 +++++++++++++------
 gdb/testsuite/gdb.testsuite/with-override.exp |  4 ++++
 gdb/testsuite/lib/cache.exp                   | 21 ++-----------------
 3 files changed, 21 insertions(+), 25 deletions(-)

diff --git a/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp b/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp
index f9610afd076..6b46b1c555a 100644
--- a/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp
+++ b/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp
@@ -22,23 +22,32 @@ gdb_caching_proc gdb_testsuite_gdb_caching_proc_exp_arg { arg } {
     return $arg
 }
 
+gdb_caching_proc gdb_testsuite_gdb_caching_proc_exp_arg_nested { arg } {
+    incr ::count
+    return [gdb_testsuite_gdb_caching_proc_exp_arg $arg]
+}
+
+# List of "expected $::count after running expression" and
+# "expression".
 set assertions {
-    { [gdb_testsuite_gdb_caching_proc_exp_noarg] == 1 }
-    { [gdb_testsuite_gdb_caching_proc_exp_arg 1] == 1 }
-    { [gdb_testsuite_gdb_caching_proc_exp_arg "foo foo"] == "foo foo" }
+    1 { [gdb_testsuite_gdb_caching_proc_exp_noarg] == 1 }
+    1 { [gdb_testsuite_gdb_caching_proc_exp_arg 1] == 1 }
+    1 { [gdb_testsuite_gdb_caching_proc_exp_arg "foo foo"] == "foo foo" }
+    1 { [gdb_testsuite_gdb_caching_proc_exp_arg_nested "foo foo"] == "foo foo" }
+    2 { [gdb_testsuite_gdb_caching_proc_exp_arg_nested "bar bar"] == "bar bar" }
 }
 
 set assertion_nr 0
-foreach assertion $assertions {
+foreach {expected_count assertion} $assertions {
     with_test_prefix $assertion_nr {
 	set ::count 0
 
 	gdb_assert $assertion
-	gdb_assert { $::count == 1 }
+	gdb_assert { $::count == $expected_count }
 
 	with_test_prefix cached {
 	    gdb_assert $assertion
-	    gdb_assert { $::count == 1 }
+	    gdb_assert { $::count == $expected_count }
 	}
     }
     incr assertion_nr
diff --git a/gdb/testsuite/gdb.testsuite/with-override.exp b/gdb/testsuite/gdb.testsuite/with-override.exp
index 1a4ee6adbbb..2a316f904e4 100644
--- a/gdb/testsuite/gdb.testsuite/with-override.exp
+++ b/gdb/testsuite/gdb.testsuite/with-override.exp
@@ -42,6 +42,10 @@ with_test_prefix no-args {
 		gdb_assert { [foo] == 2 }
 	    }
 	}
+
+	with_test_prefix "foo1 again" {
+	    gdb_assert { [foo] == 1 }
+	}
     }
 
     with_test_prefix after {
diff --git a/gdb/testsuite/lib/cache.exp b/gdb/testsuite/lib/cache.exp
index 6ca3f183f9f..b4c5fab2b98 100644
--- a/gdb/testsuite/lib/cache.exp
+++ b/gdb/testsuite/lib/cache.exp
@@ -24,26 +24,9 @@ proc ignore_pass { msg } {
 
 # Call proc real_name and return the result, while ignoring calls to pass.
 proc gdb_do_cache_wrap {real_name args} {
-    if { [info procs save_pass] != "" } {
-	return [uplevel 2 $real_name]
+    with_override pass ignore_pass {
+	return [$real_name {*}$args]
     }
-
-    rename pass save_pass
-    rename ignore_pass pass
-
-    set code [catch {uplevel 2 [list $real_name {*}$args]} result]
-
-    rename pass ignore_pass
-    rename save_pass pass
-
-    if {$code == 1} {
-	global errorInfo errorCode
-	return -code error -errorinfo $errorInfo -errorcode $errorCode $result
-    } elseif {$code > 1} {
-	return -code $code $result
-    }
-
-    return $result
 }
 
 # Global written to by gdb_exit_called proc.  Is set to true to

base-commit: d09eba07ca013e6b95eeafd67d79c32ebf6f28eb
-- 
2.51.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] Fix nested gdb_caching_proc with args; Fix gdb.rocm/ tests
  2025-09-15 21:28 [PATCH] Fix nested gdb_caching_proc with args; Fix gdb.rocm/ tests Pedro Alves
@ 2025-09-15 21:54 ` Pedro Alves
  2025-09-15 23:57   ` [PATCH] Make gdb_caching_proc support namespaces Pedro Alves
  2025-09-16  9:31   ` [PATCH v2] Fix nested gdb_caching_proc with args; Fix gdb.rocm/ tests Pedro Alves
  0 siblings, 2 replies; 6+ messages in thread
From: Pedro Alves @ 2025-09-15 21:54 UTC (permalink / raw)
  To: gdb-patches

I should have mentioned something here:

On 2025-09-15 22:28, Pedro Alves wrote:

> --- a/gdb/testsuite/lib/cache.exp
> +++ b/gdb/testsuite/lib/cache.exp
> @@ -24,26 +24,9 @@ proc ignore_pass { msg } {
>  
>  # Call proc real_name and return the result, while ignoring calls to pass.
>  proc gdb_do_cache_wrap {real_name args} {
> -    if { [info procs save_pass] != "" } {
> -	return [uplevel 2 $real_name]
> +    with_override pass ignore_pass {
> +	return [$real_name {*}$args]
>      }

The previous code was using "uplevel 2" here.  But I'm struggling to see why that is the
right level.

When gdb_do_cache_wrap is called via gdb_caching_proc => gdb_do_cache, that takes us to the scope of
the caller of the gdb_caching_proc, which off hand would seem right.

However, when gdb_do_cache_wrap is called directly by gdb.testsuite/gdb-caching-proc-consistency.exp, it
takes us to ... the caller of test_proc?

So I'm wondering, what could we possibly want to reference from a higher level in gdb_do_cache_wrap
that not having the uplevel in gdb_do_cache_wrap would get wrong?  A caching proc IMO shouldn't be doing
something like taking the name of a variable as argument instead of a value, as then the procedure wouldn't
be guaranteed to be idempotent?  So I dropped the uplevel, and found that that doesn't cause any
testsuite regression.  But maybe I'm missing something.  If I am, it'd be nice to add a testcase for it.

Pedro Alves


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH] Make gdb_caching_proc support namespaces
  2025-09-15 21:54 ` Pedro Alves
@ 2025-09-15 23:57   ` Pedro Alves
  2025-09-16  3:55     ` Simon Marchi
  2025-09-16  9:31   ` [PATCH v2] Fix nested gdb_caching_proc with args; Fix gdb.rocm/ tests Pedro Alves
  1 sibling, 1 reply; 6+ messages in thread
From: Pedro Alves @ 2025-09-15 23:57 UTC (permalink / raw)
  To: gdb-patches

On 2025-09-15 22:54, Pedro Alves wrote:
> I should have mentioned something here:
> 
> On 2025-09-15 22:28, Pedro Alves wrote:
> 
>> --- a/gdb/testsuite/lib/cache.exp
>> +++ b/gdb/testsuite/lib/cache.exp
>> @@ -24,26 +24,9 @@ proc ignore_pass { msg } {
>>  
>>  # Call proc real_name and return the result, while ignoring calls to pass.
>>  proc gdb_do_cache_wrap {real_name args} {
>> -    if { [info procs save_pass] != "" } {
>> -	return [uplevel 2 $real_name]
>> +    with_override pass ignore_pass {
>> +	return [$real_name {*}$args]
>>      }
> 
> The previous code was using "uplevel 2" here.  But I'm struggling to see why that is the
> right level.
> 
> When gdb_do_cache_wrap is called via gdb_caching_proc => gdb_do_cache, that takes us to the scope of
> the caller of the gdb_caching_proc, which off hand would seem right.
> 
> However, when gdb_do_cache_wrap is called directly by gdb.testsuite/gdb-caching-proc-consistency.exp, it
> takes us to ... the caller of test_proc?
> 
> So I'm wondering, what could we possibly want to reference from a higher level in gdb_do_cache_wrap
> that not having the uplevel in gdb_do_cache_wrap would get wrong?  A caching proc IMO shouldn't be doing
> something like taking the name of a variable as argument instead of a value, as then the procedure wouldn't
> be guaranteed to be idempotent?  So I dropped the uplevel, and found that that doesn't cause any
> testsuite regression.  But maybe I'm missing something.  If I am, it'd be nice to add a testcase for it.

Trying to find a testcase for the above, I thought to try a gdb_caching_proc defined in a namespace,
and have that proc access a namespace variable, in its own namespace.

That ran into the fact that gdb_caching_proc doesn't support namespaces today.  So I fixed that, with
the patch below (on top of the previous one), which includes tests.  That still didn't require any
uplevel in gdb_do_cache_wrap.

I don't have a current use for this, but I figure that eventually we will find a use, and since
I wrote it...

== 8< ===
From e9bad21e186f6024cbbecc97b4b0f593bc8ed7ea Mon Sep 17 00:00:00 2001
From: Pedro Alves <pedro@palves.net>
Date: Tue, 16 Sep 2025 00:41:30 +0100
Subject: [PATCH] Make gdb_caching_proc support namespaces

Currently, defining a gdb_caching_proc in a namespace, like:

namespace eval myns {
    gdb_caching_proc nsproc {} {
      return 1
    }
}

Incorrectly defines the advertised procedure in the global namespace
instead of in the namespace of the procedure being wrapped.

I.e., for the example above, calling myns::nsproc would fail, and
calling "::nsproc" would succeed.

This patch fixes it, and adds tests to
gdb.testsuite/gdb-caching-proc.exp.

Change-Id: I1853f9f411406331eb44d53d028d65a3d400db7f
---
 .../gdb.testsuite/gdb-caching-proc.exp        | 39 ++++++++++----
 gdb/testsuite/lib/cache.exp                   | 54 ++++++++++++-------
 2 files changed, 65 insertions(+), 28 deletions(-)

diff --git a/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp b/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp
index 6b46b1c555a..8b5168e5394 100644
--- a/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp
+++ b/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp
@@ -27,27 +27,46 @@ gdb_caching_proc gdb_testsuite_gdb_caching_proc_exp_arg_nested { arg } {
     return [gdb_testsuite_gdb_caching_proc_exp_arg $arg]
 }
 
-# List of "expected $::count after running expression" and
+namespace eval myns {
+    variable ns_count 1
+
+    gdb_caching_proc nsproc {} {
+	variable ns_count
+	incr ns_count
+	return 1
+    }
+}
+
+gdb_caching_proc ::fq_proc {} {
+    incr ::count
+    return 1
+}
+
+# List of "VAR, expected value of VAR after running expression", and
 # "expression".
 set assertions {
-    1 { [gdb_testsuite_gdb_caching_proc_exp_noarg] == 1 }
-    1 { [gdb_testsuite_gdb_caching_proc_exp_arg 1] == 1 }
-    1 { [gdb_testsuite_gdb_caching_proc_exp_arg "foo foo"] == "foo foo" }
-    1 { [gdb_testsuite_gdb_caching_proc_exp_arg_nested "foo foo"] == "foo foo" }
-    2 { [gdb_testsuite_gdb_caching_proc_exp_arg_nested "bar bar"] == "bar bar" }
+    count 2 { [gdb_testsuite_gdb_caching_proc_exp_noarg] == 1 }
+    count 2 { [gdb_testsuite_gdb_caching_proc_exp_arg 1] == 1 }
+    count 2 { [gdb_testsuite_gdb_caching_proc_exp_arg "foo foo"] == "foo foo" }
+    count 2 { [gdb_testsuite_gdb_caching_proc_exp_arg_nested "foo foo"] == "foo foo" }
+    count 3 { [gdb_testsuite_gdb_caching_proc_exp_arg_nested "bar bar"] == "bar bar" }
+    count 2 { [fq_proc] == 1 }
+    myns::ns_count 2 { [myns::nsproc] == 1 }
 }
 
 set assertion_nr 0
-foreach {expected_count assertion} $assertions {
+foreach {var expected_var_value assertion} $assertions {
     with_test_prefix $assertion_nr {
-	set ::count 0
+	# Start at 1 to be sure the "incr" in the test procs isn't the
+	# one creating the variable.
+	set $var 1
 
 	gdb_assert $assertion
-	gdb_assert { $::count == $expected_count }
+	gdb_assert { [set $var] == $expected_var_value }
 
 	with_test_prefix cached {
 	    gdb_assert $assertion
-	    gdb_assert { $::count == $expected_count }
+	    gdb_assert { [set $var] == $expected_var_value }
 	}
     }
     incr assertion_nr
diff --git a/gdb/testsuite/lib/cache.exp b/gdb/testsuite/lib/cache.exp
index b4c5fab2b98..bbe07387b21 100644
--- a/gdb/testsuite/lib/cache.exp
+++ b/gdb/testsuite/lib/cache.exp
@@ -78,31 +78,29 @@ proc gdb_cache_maybe_gdb_exit { name do_exit also_called } {
 	return
     }
 
-    # To track if this proc has been called for NAME we create a
-    # global variable.  In gdb_cleanup_globals (see gdb.exp) this
-    # global will be deleted when the test has finished.
-    set global_name __${name}__cached_gdb_exit_called
-    if { ![info exists ::${global_name}] } {
+    # To track if this proc has been called for NAME we use a global
+    # array.  In gdb_cleanup_globals (see gdb.exp) this array will be
+    # deleted when the test has finished.
+    if { ![info exists ::gdb_caching_proc_called($name)] } {
 	gdb_exit
 	verbose -log "gdb_caching_proc $name caused gdb_exit to be called"
-	set ::${global_name} true
-	verbose -log "  gdb_caching_proc $name marked as called"
+	set ::gdb_caching_proc_called($name) true
+	verbose -log "	gdb_caching_proc $name marked as called"
 
 	foreach other_name $also_called {
 	    verbose -log "  gdb_caching_proc $other_name marked as called"
-	    set global_name __${other_name}__cached_gdb_exit_called
-	    set ::${global_name} true
+	    set ::gdb_caching_proc_called($other_name) true
 	}
     }
 }
 
 # A helper for gdb_caching_proc that handles the caching.
 
-proc gdb_do_cache {name args} {
+proc gdb_do_cache {name real_name args} {
     global gdb_data_cache objdir
     global GDB_PARALLEL
 
-    verbose -log "gdb_do_cache: $name ( $args )"
+    verbose -log "gdb_do_cache: $name $real_name ( $args )"
 
     # Normally, if we have a cached value, we skip computation and return
     # the cached value.  If set to 1, instead don't skip computation and
@@ -115,11 +113,15 @@ proc gdb_do_cache {name args} {
 	set cache_verify 1
     }
 
+    # NAME is always a fully qualified name here.  Don't include the
+    # leading "::" in the cache file name.
+    set cache_file_name [string range $name 2 end]
+
     # See if some other process wrote the cache file.  Cache value per
     # "board" to handle runs with multiple options
     # (e.g. unix/{-m32,-64}) correctly.  We use "file join" here
     # because we later use this in a real filename.
-    set cache_name [file join [target_info name] $name {*}$args]
+    set cache_name [file join [target_info name] $cache_file_name {*}$args]
 
     set is_cached 0
     if {[info exists gdb_data_cache(${cache_name},value)]} {
@@ -194,7 +196,6 @@ proc gdb_do_cache {name args} {
     set old_gdb_nested_caching_proc_calls $::gdb_nested_caching_proc_calls
     set ::gdb_nested_caching_proc_calls {}
 
-    set real_name gdb_real__$name
     set gdb_data_cache(${cache_name},value) [gdb_do_cache_wrap $real_name {*}$args]
     set gdb_data_cache(${cache_name},exit) $::gdb_exit_called
     set gdb_data_cache(${cache_name},also_called) \
@@ -276,15 +277,32 @@ proc gdb_do_cache {name args} {
 #
 
 proc gdb_caching_proc {name arglist body} {
-    # Define the underlying proc that we'll call.
-    set real_name gdb_real__$name
-    proc $real_name $arglist $body
+    # Figure out the caller's namespace.
+    set ns [uplevel 1 {namespace current}]
+
+    # Normalize the advertised name.
+    if {[string match ::* $name]} {
+	set fq_name $name
+    } elseif {$ns eq "::"} {
+	set fq_name ::$name
+    } else {
+	set fq_name ${ns}::$name
+    }
+
+    set tail [namespace tail $fq_name]
+    set qual [namespace qualifiers $fq_name]
+
+    # The name of the underlying proc that we'll call.
+    set fq_real_name ${qual}::gdb_real__$tail
+
+    # Define it.
+    proc $fq_real_name $arglist $body
 
     # Define the advertised proc.
-    set caching_proc_body [list gdb_do_cache $name]
+    set caching_proc_body [list gdb_do_cache $fq_name $fq_real_name]
     foreach arg $arglist {
 	lappend caching_proc_body $$arg
     }
     set caching_proc_body [join $caching_proc_body]
-    proc $name $arglist $caching_proc_body
+    proc $fq_name $arglist $caching_proc_body
 }

base-commit: d09eba07ca013e6b95eeafd67d79c32ebf6f28eb
prerequisite-patch-id: 7193acb0815df9f5fbc926bb2169131538e88bd5
-- 
2.51.0



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] Make gdb_caching_proc support namespaces
  2025-09-15 23:57   ` [PATCH] Make gdb_caching_proc support namespaces Pedro Alves
@ 2025-09-16  3:55     ` Simon Marchi
  0 siblings, 0 replies; 6+ messages in thread
From: Simon Marchi @ 2025-09-16  3:55 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches



On 2025-09-15 19:57, Pedro Alves wrote:
> On 2025-09-15 22:54, Pedro Alves wrote:
>> I should have mentioned something here:
>>
>> On 2025-09-15 22:28, Pedro Alves wrote:
>>
>>> --- a/gdb/testsuite/lib/cache.exp
>>> +++ b/gdb/testsuite/lib/cache.exp
>>> @@ -24,26 +24,9 @@ proc ignore_pass { msg } {
>>>  
>>>  # Call proc real_name and return the result, while ignoring calls to pass.
>>>  proc gdb_do_cache_wrap {real_name args} {
>>> -    if { [info procs save_pass] != "" } {
>>> -	return [uplevel 2 $real_name]
>>> +    with_override pass ignore_pass {
>>> +	return [$real_name {*}$args]
>>>      }
>>
>> The previous code was using "uplevel 2" here.  But I'm struggling to see why that is the
>> right level.
>>
>> When gdb_do_cache_wrap is called via gdb_caching_proc => gdb_do_cache, that takes us to the scope of
>> the caller of the gdb_caching_proc, which off hand would seem right.
>>
>> However, when gdb_do_cache_wrap is called directly by gdb.testsuite/gdb-caching-proc-consistency.exp, it
>> takes us to ... the caller of test_proc?
>>
>> So I'm wondering, what could we possibly want to reference from a higher level in gdb_do_cache_wrap
>> that not having the uplevel in gdb_do_cache_wrap would get wrong?  A caching proc IMO shouldn't be doing
>> something like taking the name of a variable as argument instead of a value, as then the procedure wouldn't
>> be guaranteed to be idempotent?  So I dropped the uplevel, and found that that doesn't cause any
>> testsuite regression.  But maybe I'm missing something.  If I am, it'd be nice to add a testcase for it.
> 
> Trying to find a testcase for the above, I thought to try a gdb_caching_proc defined in a namespace,
> and have that proc access a namespace variable, in its own namespace.
> 
> That ran into the fact that gdb_caching_proc doesn't support namespaces today.  So I fixed that, with
> the patch below (on top of the previous one), which includes tests.  That still didn't require any
> uplevel in gdb_do_cache_wrap.
> 
> I don't have a current use for this, but I figure that eventually we will find a use, and since
> I wrote it...

I haven't looked at the code, but it fixes the problem for me.

Tested-By: Simon Marchi <simon.marchi@efficios.com>

Simon

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2] Fix nested gdb_caching_proc with args; Fix gdb.rocm/ tests
  2025-09-15 21:54 ` Pedro Alves
  2025-09-15 23:57   ` [PATCH] Make gdb_caching_proc support namespaces Pedro Alves
@ 2025-09-16  9:31   ` Pedro Alves
  2025-09-16 10:56     ` Pedro Alves
  1 sibling, 1 reply; 6+ messages in thread
From: Pedro Alves @ 2025-09-16  9:31 UTC (permalink / raw)
  To: gdb-patches

On 2025-09-15 22:54, Pedro Alves wrote:
> I should have mentioned something here:
> 
> On 2025-09-15 22:28, Pedro Alves wrote:
> 
>> --- a/gdb/testsuite/lib/cache.exp
>> +++ b/gdb/testsuite/lib/cache.exp
>> @@ -24,26 +24,9 @@ proc ignore_pass { msg } {
>>  
>>  # Call proc real_name and return the result, while ignoring calls to pass.
>>  proc gdb_do_cache_wrap {real_name args} {
>> -    if { [info procs save_pass] != "" } {
>> -	return [uplevel 2 $real_name]
>> +    with_override pass ignore_pass {
>> +	return [$real_name {*}$args]
>>      }
> 
> The previous code was using "uplevel 2" here.  But I'm struggling to see why that is the
> right level.
> 
> When gdb_do_cache_wrap is called via gdb_caching_proc => gdb_do_cache, that takes us to the scope of
> the caller of the gdb_caching_proc, which off hand would seem right.
> 
> However, when gdb_do_cache_wrap is called directly by gdb.testsuite/gdb-caching-proc-consistency.exp, it
> takes us to ... the caller of test_proc?
> 
> So I'm wondering, what could we possibly want to reference from a higher level in gdb_do_cache_wrap
> that not having the uplevel in gdb_do_cache_wrap would get wrong?  A caching proc IMO shouldn't be doing
> something like taking the name of a variable as argument instead of a value, as then the procedure wouldn't
> be guaranteed to be idempotent?  So I dropped the uplevel, and found that that doesn't cause any
> testsuite regression.  But maybe I'm missing something.  If I am, it'd be nice to add a testcase for it.

I guess that is sort of a tangent.  I should preserve the uplevel so that the patch does just one thing.
A change to that detail can always be done separately.

Here's a new version of the patch without that change.  With that out of the way, this patch should be a
lot more obvious.

I'd like to merge it soon to unbreak the gdb.rocm/ tests.

=== 8< ===
From 50e4abd32c3f162f6221bdb4946ef7c18f29d7ed Mon Sep 17 00:00:00 2001
From: Pedro Alves <pedro@palves.net>
Date: Mon, 15 Sep 2025 22:12:28 +0100
Subject: [PATCH v2] Fix nested gdb_caching_proc with args; Fix gdb.rocm/ tests

Commit d09eba07 ("Make get_compiler_info use gdb_caching_proc")
regressed the gdb.rocm/ testsuite.  E.g.:

 Running /home/pedro/rocm/gdb/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.rocm/simple.exp ...
 ERROR: tcl error sourcing /home/pedro/rocm/gdb/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.rocm/simple.exp.
 ERROR: tcl error code TCL WRONGARGS
 ERROR: wrong # args: should be "gdb_real__get_compiler_info_1 language"
     while executing
 "gdb_real__get_compiler_info_1"
     ("uplevel" body line 1)
     invoked from within
 "uplevel 2 $real_name"
     (procedure "gdb_do_cache_wrap" line 3)
     invoked from within
 "gdb_do_cache_wrap $real_name {*}$args"
     (procedure "gdb_do_cache" line 98)
     invoked from within

This is actually a latent problem in gdb_do_cache_wrap, introduced in:

 commit 71f1ab80f1aabd70bce526635f84c7b849e8a0f4
 CommitDate: Mon Mar 6 16:49:19 2023 +0100

    [gdb/testsuite] Allow args in gdb_caching_proc

This change:

   # Call proc real_name and return the result, while ignoring calls to pass.
  -proc gdb_do_cache_wrap {real_name} {
  +proc gdb_do_cache_wrap {real_name args} {
       if { [info procs save_pass] != "" } {
	  return [uplevel 2 $real_name]   <<<<<<<<<<<<<<<<<<<<<<< HERE
       }
  @@ -31,7 +31,7 @@ proc gdb_do_cache_wrap {real_name} {
       rename pass save_pass
       rename ignore_pass pass

  -    set code [catch {uplevel 2 $real_name} result]
  +    set code [catch {uplevel 2 [list $real_name {*}$args]} result]

Missed updating the line marked with HERE above, to pass down $args.
So the case of a caching proc calling another caching proc with args
isn't handled correctly.

We could fix this by fixing the HERE line like so:

 -	  return [uplevel 2 $real_name]
 +	  return [uplevel 2 [list $real_name {*}$args]]

However, we have with_override nowadays that we can use here which
eliminates the duplicated logic, which was what was missed originally.

A new test that exposes the problem is added to
gdb.testsuite/gdb-caching-proc.exp.

This also adds a new test to gdb.testsuite/with-override.exp that I
think was missing, making sure that the inner foo override restores
the outer foo override.

Tested-By: Simon Marchi <simon.marchi@efficios.com>
Change-Id: I8b2a7366bf910902fe5f547bde58c3b475bf5133
---
 .../gdb.testsuite/gdb-caching-proc.exp        | 21 +++++++++++++------
 gdb/testsuite/gdb.testsuite/with-override.exp |  4 ++++
 gdb/testsuite/lib/cache.exp                   | 21 ++-----------------
 3 files changed, 21 insertions(+), 25 deletions(-)

diff --git a/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp b/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp
index f9610afd076..6b46b1c555a 100644
--- a/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp
+++ b/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp
@@ -22,23 +22,32 @@ gdb_caching_proc gdb_testsuite_gdb_caching_proc_exp_arg { arg } {
     return $arg
 }
 
+gdb_caching_proc gdb_testsuite_gdb_caching_proc_exp_arg_nested { arg } {
+    incr ::count
+    return [gdb_testsuite_gdb_caching_proc_exp_arg $arg]
+}
+
+# List of "expected $::count after running expression" and
+# "expression".
 set assertions {
-    { [gdb_testsuite_gdb_caching_proc_exp_noarg] == 1 }
-    { [gdb_testsuite_gdb_caching_proc_exp_arg 1] == 1 }
-    { [gdb_testsuite_gdb_caching_proc_exp_arg "foo foo"] == "foo foo" }
+    1 { [gdb_testsuite_gdb_caching_proc_exp_noarg] == 1 }
+    1 { [gdb_testsuite_gdb_caching_proc_exp_arg 1] == 1 }
+    1 { [gdb_testsuite_gdb_caching_proc_exp_arg "foo foo"] == "foo foo" }
+    1 { [gdb_testsuite_gdb_caching_proc_exp_arg_nested "foo foo"] == "foo foo" }
+    2 { [gdb_testsuite_gdb_caching_proc_exp_arg_nested "bar bar"] == "bar bar" }
 }
 
 set assertion_nr 0
-foreach assertion $assertions {
+foreach {expected_count assertion} $assertions {
     with_test_prefix $assertion_nr {
 	set ::count 0
 
 	gdb_assert $assertion
-	gdb_assert { $::count == 1 }
+	gdb_assert { $::count == $expected_count }
 
 	with_test_prefix cached {
 	    gdb_assert $assertion
-	    gdb_assert { $::count == 1 }
+	    gdb_assert { $::count == $expected_count }
 	}
     }
     incr assertion_nr
diff --git a/gdb/testsuite/gdb.testsuite/with-override.exp b/gdb/testsuite/gdb.testsuite/with-override.exp
index 1a4ee6adbbb..2a316f904e4 100644
--- a/gdb/testsuite/gdb.testsuite/with-override.exp
+++ b/gdb/testsuite/gdb.testsuite/with-override.exp
@@ -42,6 +42,10 @@ with_test_prefix no-args {
 		gdb_assert { [foo] == 2 }
 	    }
 	}
+
+	with_test_prefix "foo1 again" {
+	    gdb_assert { [foo] == 1 }
+	}
     }
 
     with_test_prefix after {
diff --git a/gdb/testsuite/lib/cache.exp b/gdb/testsuite/lib/cache.exp
index 6ca3f183f9f..4ebb8259957 100644
--- a/gdb/testsuite/lib/cache.exp
+++ b/gdb/testsuite/lib/cache.exp
@@ -24,26 +24,9 @@ proc ignore_pass { msg } {
 
 # Call proc real_name and return the result, while ignoring calls to pass.
 proc gdb_do_cache_wrap {real_name args} {
-    if { [info procs save_pass] != "" } {
-	return [uplevel 2 $real_name]
+    with_override pass ignore_pass {
+	return [uplevel 2 [list $real_name {*}$args]]
     }
-
-    rename pass save_pass
-    rename ignore_pass pass
-
-    set code [catch {uplevel 2 [list $real_name {*}$args]} result]
-
-    rename pass ignore_pass
-    rename save_pass pass
-
-    if {$code == 1} {
-	global errorInfo errorCode
-	return -code error -errorinfo $errorInfo -errorcode $errorCode $result
-    } elseif {$code > 1} {
-	return -code $code $result
-    }
-
-    return $result
 }
 
 # Global written to by gdb_exit_called proc.  Is set to true to

base-commit: d09eba07ca013e6b95eeafd67d79c32ebf6f28eb
-- 
2.51.0



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2] Fix nested gdb_caching_proc with args; Fix gdb.rocm/ tests
  2025-09-16  9:31   ` [PATCH v2] Fix nested gdb_caching_proc with args; Fix gdb.rocm/ tests Pedro Alves
@ 2025-09-16 10:56     ` Pedro Alves
  0 siblings, 0 replies; 6+ messages in thread
From: Pedro Alves @ 2025-09-16 10:56 UTC (permalink / raw)
  To: gdb-patches

On 2025-09-16 10:31, Pedro Alves wrote:
> On 2025-09-15 22:54, Pedro Alves wrote:
>> I should have mentioned something here:
>>
>> On 2025-09-15 22:28, Pedro Alves wrote:
>>
>>> --- a/gdb/testsuite/lib/cache.exp
>>> +++ b/gdb/testsuite/lib/cache.exp
>>> @@ -24,26 +24,9 @@ proc ignore_pass { msg } {
>>>  
>>>  # Call proc real_name and return the result, while ignoring calls to pass.
>>>  proc gdb_do_cache_wrap {real_name args} {
>>> -    if { [info procs save_pass] != "" } {
>>> -	return [uplevel 2 $real_name]
>>> +    with_override pass ignore_pass {
>>> +	return [$real_name {*}$args]
>>>      }
>>
>> The previous code was using "uplevel 2" here.  But I'm struggling to see why that is the
>> right level.
>>
>> When gdb_do_cache_wrap is called via gdb_caching_proc => gdb_do_cache, that takes us to the scope of
>> the caller of the gdb_caching_proc, which off hand would seem right.
>>
>> However, when gdb_do_cache_wrap is called directly by gdb.testsuite/gdb-caching-proc-consistency.exp, it
>> takes us to ... the caller of test_proc?
>>
>> So I'm wondering, what could we possibly want to reference from a higher level in gdb_do_cache_wrap
>> that not having the uplevel in gdb_do_cache_wrap would get wrong?  A caching proc IMO shouldn't be doing
>> something like taking the name of a variable as argument instead of a value, as then the procedure wouldn't
>> be guaranteed to be idempotent?  So I dropped the uplevel, and found that that doesn't cause any
>> testsuite regression.  But maybe I'm missing something.  If I am, it'd be nice to add a testcase for it.
> 
> I guess that is sort of a tangent.  I should preserve the uplevel so that the patch does just one thing.
> A change to that detail can always be done separately.
> 
> Here's a new version of the patch without that change.  With that out of the way, this patch should be a
> lot more obvious.
> 
> I'd like to merge it soon to unbreak the gdb.rocm/ tests.


I noticed now that while a full test run did not expose this issue, which explains why I didn't see it
originally, I'm able to trigger this with tests other than gdb.rocm/, as long as you run them in
isolation, so that the cache is cold for the nested procs.  For example, 

  make check TESTS="gdb.base/attach.exp"

triggers it too.

I'm going ahead and merging it, to minimize disturbances to everyone, with the adjusted commit log below.

=== 8< ===
From 2e7ea115f4bddcd7d3f81e0e9026b76fef4d4253 Mon Sep 17 00:00:00 2001
From: Pedro Alves <pedro@palves.net>
Date: Mon, 15 Sep 2025 22:12:28 +0100
Subject: [PATCH] Fix nested gdb_caching_proc with args

Commit d09eba07 ("Make get_compiler_info use gdb_caching_proc")
regressed some tests when you run them in isolation (as this depends
on the order the gdb_caching_proc procs' results are cached).

E.g.:

 Running /home/pedro/rocm/gdb/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.rocm/simple.exp ...
 ERROR: tcl error sourcing /home/pedro/rocm/gdb/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.rocm/simple.exp.
 ERROR: tcl error code TCL WRONGARGS
 ERROR: wrong # args: should be "gdb_real__get_compiler_info_1 language"
     while executing
 "gdb_real__get_compiler_info_1"
     ("uplevel" body line 1)
     invoked from within
 "uplevel 2 $real_name"
     (procedure "gdb_do_cache_wrap" line 3)
     invoked from within
 "gdb_do_cache_wrap $real_name {*}$args"
     (procedure "gdb_do_cache" line 98)
     invoked from within

gdb.base/attach.exp triggers it too, for example.

This is actually a latent problem in gdb_do_cache_wrap, introduced in:

 commit 71f1ab80f1aabd70bce526635f84c7b849e8a0f4
 CommitDate: Mon Mar 6 16:49:19 2023 +0100

    [gdb/testsuite] Allow args in gdb_caching_proc

This change:

   # Call proc real_name and return the result, while ignoring calls to pass.
  -proc gdb_do_cache_wrap {real_name} {
  +proc gdb_do_cache_wrap {real_name args} {
       if { [info procs save_pass] != "" } {
	  return [uplevel 2 $real_name]   <<<<<<<<<<<<<<<<<<<<<<< HERE
       }
  @@ -31,7 +31,7 @@ proc gdb_do_cache_wrap {real_name} {
       rename pass save_pass
       rename ignore_pass pass

  -    set code [catch {uplevel 2 $real_name} result]
  +    set code [catch {uplevel 2 [list $real_name {*}$args]} result]

Missed updating the line marked with HERE above, to pass down $args.
So the case of a caching proc calling another caching proc with args
isn't handled correctly.

We could fix this by fixing the HERE line like so:

 -	  return [uplevel 2 $real_name]
 +	  return [uplevel 2 [list $real_name {*}$args]]

However, we have with_override nowadays that we can use here which
eliminates the duplicated logic, which was what was missed originally.

A new test that exposes the problem is added to
gdb.testsuite/gdb-caching-proc.exp.

This also adds a new test to gdb.testsuite/with-override.exp that I
think was missing, making sure that the inner foo override restores
the outer foo override.

Tested-By: Simon Marchi <simon.marchi@efficios.com>
Change-Id: I8b2a7366bf910902fe5f547bde58c3b475bf5133
---
 .../gdb.testsuite/gdb-caching-proc.exp        | 21 +++++++++++++------
 gdb/testsuite/gdb.testsuite/with-override.exp |  4 ++++
 gdb/testsuite/lib/cache.exp                   | 21 ++-----------------
 3 files changed, 21 insertions(+), 25 deletions(-)

diff --git a/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp b/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp
index f9610afd076..6b46b1c555a 100644
--- a/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp
+++ b/gdb/testsuite/gdb.testsuite/gdb-caching-proc.exp
@@ -22,23 +22,32 @@ gdb_caching_proc gdb_testsuite_gdb_caching_proc_exp_arg { arg } {
     return $arg
 }
 
+gdb_caching_proc gdb_testsuite_gdb_caching_proc_exp_arg_nested { arg } {
+    incr ::count
+    return [gdb_testsuite_gdb_caching_proc_exp_arg $arg]
+}
+
+# List of "expected $::count after running expression" and
+# "expression".
 set assertions {
-    { [gdb_testsuite_gdb_caching_proc_exp_noarg] == 1 }
-    { [gdb_testsuite_gdb_caching_proc_exp_arg 1] == 1 }
-    { [gdb_testsuite_gdb_caching_proc_exp_arg "foo foo"] == "foo foo" }
+    1 { [gdb_testsuite_gdb_caching_proc_exp_noarg] == 1 }
+    1 { [gdb_testsuite_gdb_caching_proc_exp_arg 1] == 1 }
+    1 { [gdb_testsuite_gdb_caching_proc_exp_arg "foo foo"] == "foo foo" }
+    1 { [gdb_testsuite_gdb_caching_proc_exp_arg_nested "foo foo"] == "foo foo" }
+    2 { [gdb_testsuite_gdb_caching_proc_exp_arg_nested "bar bar"] == "bar bar" }
 }
 
 set assertion_nr 0
-foreach assertion $assertions {
+foreach {expected_count assertion} $assertions {
     with_test_prefix $assertion_nr {
 	set ::count 0
 
 	gdb_assert $assertion
-	gdb_assert { $::count == 1 }
+	gdb_assert { $::count == $expected_count }
 
 	with_test_prefix cached {
 	    gdb_assert $assertion
-	    gdb_assert { $::count == 1 }
+	    gdb_assert { $::count == $expected_count }
 	}
     }
     incr assertion_nr
diff --git a/gdb/testsuite/gdb.testsuite/with-override.exp b/gdb/testsuite/gdb.testsuite/with-override.exp
index 1a4ee6adbbb..2a316f904e4 100644
--- a/gdb/testsuite/gdb.testsuite/with-override.exp
+++ b/gdb/testsuite/gdb.testsuite/with-override.exp
@@ -42,6 +42,10 @@ with_test_prefix no-args {
 		gdb_assert { [foo] == 2 }
 	    }
 	}
+
+	with_test_prefix "foo1 again" {
+	    gdb_assert { [foo] == 1 }
+	}
     }
 
     with_test_prefix after {
diff --git a/gdb/testsuite/lib/cache.exp b/gdb/testsuite/lib/cache.exp
index 6ca3f183f9f..4ebb8259957 100644
--- a/gdb/testsuite/lib/cache.exp
+++ b/gdb/testsuite/lib/cache.exp
@@ -24,26 +24,9 @@ proc ignore_pass { msg } {
 
 # Call proc real_name and return the result, while ignoring calls to pass.
 proc gdb_do_cache_wrap {real_name args} {
-    if { [info procs save_pass] != "" } {
-	return [uplevel 2 $real_name]
+    with_override pass ignore_pass {
+	return [uplevel 2 [list $real_name {*}$args]]
     }
-
-    rename pass save_pass
-    rename ignore_pass pass
-
-    set code [catch {uplevel 2 [list $real_name {*}$args]} result]
-
-    rename pass ignore_pass
-    rename save_pass pass
-
-    if {$code == 1} {
-	global errorInfo errorCode
-	return -code error -errorinfo $errorInfo -errorcode $errorCode $result
-    } elseif {$code > 1} {
-	return -code $code $result
-    }
-
-    return $result
 }
 
 # Global written to by gdb_exit_called proc.  Is set to true to

base-commit: d09eba07ca013e6b95eeafd67d79c32ebf6f28eb
-- 
2.51.0



^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2025-09-16 10:57 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-09-15 21:28 [PATCH] Fix nested gdb_caching_proc with args; Fix gdb.rocm/ tests Pedro Alves
2025-09-15 21:54 ` Pedro Alves
2025-09-15 23:57   ` [PATCH] Make gdb_caching_proc support namespaces Pedro Alves
2025-09-16  3:55     ` Simon Marchi
2025-09-16  9:31   ` [PATCH v2] Fix nested gdb_caching_proc with args; Fix gdb.rocm/ tests Pedro Alves
2025-09-16 10:56     ` Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox