gdb/testsuite: Add gdb_test_multiple_name variable

Message ID 20191001172850.29867-1-andrew.burgess@embecosm.com
State New
Headers show
Series
  • gdb/testsuite: Add gdb_test_multiple_name variable
Related show

Commit Message

Andrew Burgess Oct. 1, 2019, 5:28 p.m.
This commit adds a new feature to gdb_test_multiple, an automatically
created variable gdb_test_multiple_name.  The idea is to make it
easier to write tests using gdb_test_multiple, and avoid places where
the string passed to pass/fail within an action element is different
to the message passed to the top level gdb_test_multiple.

As an example, previously you might write this:

    gdb_test_multiple "print foo" "test foo" {
       -re "expected output 1" {
           pass "test foo"
       }
       -re "expected output 2" {
           fail "test foo"
       }
    }

This is OK, but it's easy for the pass/fail strings to come out of
sync, or contain a typo.  A better version would look like this:

    set testname "test foo"
    gdb_test_multiple "print foo" $testname {
       -re "expected output 1" {
           pass $testname
       }
       -re "expected output 2" {
           fail $testname
       }
    }

This is better, but its a bit of a drag having to create a new
variable each time.

After this patch you can now write this:

    gdb_test_multiple "print foo" "test foo" {
       -re "expected output 1" {
           pass $gdb_test_multiple_name
       }
       -re "expected output 2" {
           fail $gdb_test_multiple_name
       }
    }

The $gdb_test_multiple_name is setup by gdb_test_multiple, and cleaned
up once the test has completed.  Nested calls to gdb_test_multiple are
supported, though $gdb_test_multiple_name will only ever contain the
inner most test message (which is probably what you want).

My only regret is that '$gdb_test_multiple_name' is so long, but I
wanted something that was unlikely to clash with any existing variable
name, or anything that a user is likely to want to use.

I've tested this on x86-64/GNU Linux and see no test regressions, and
I've converted one test script over to make use of this new technique
both as an example, and to ensure that the new facility doesn't get
broken.  I have no plans to convert all tests over to this technique,
but I hope others will find this useful for writing tests in the
future.

gdb/testsuite/ChangeLog:

	* lib/gdb.exp (gdb_test_multiple): Add gdb_test_multiple_name
	mechanism.
	* gdb.base/annota1.exp: Update to use gdb_test_multiple_name.
---
 gdb/testsuite/ChangeLog            |  6 ++++
 gdb/testsuite/gdb.base/annota1.exp | 58 ++++++++++++++++++--------------------
 gdb/testsuite/lib/gdb.exp          | 38 +++++++++++++++++++++++--
 3 files changed, 70 insertions(+), 32 deletions(-)

-- 
2.14.5

Comments

Keith Seitz Oct. 1, 2019, 5:41 p.m. | #1
On 10/1/19 10:28 AM, Andrew Burgess wrote:
> After this patch you can now write this:

> 

>     gdb_test_multiple "print foo" "test foo" {

>        -re "expected output 1" {

>            pass $gdb_test_multiple_name

>        }

>        -re "expected output 2" {

>            fail $gdb_test_multiple_name

>        }

>     }


OMG, why didn't *I* think of that!?!

> The $gdb_test_multiple_name is setup by gdb_test_multiple, and cleaned

> up once the test has completed.  Nested calls to gdb_test_multiple are

> supported, though $gdb_test_multiple_name will only ever contain the

> inner most test message (which is probably what you want).


Your patch looks good to me (but I am not a maintainer), but I wanted to
ask if you considered using a stack to more robustly solve the nesting
problem? It might be unnecessary today -- even overkill -- but I
would think it is much easier on the eyes.

We have such support in the test suite already. See 
testsuite/lib/data-structures.exp. [I am not suggesting any
changes, just trying to raise awareness for ::Stack et al usage
in the test suite.]

Keith
Pedro Alves Oct. 1, 2019, 6:31 p.m. | #2
On 10/1/19 6:28 PM, Andrew Burgess wrote:

> My only regret is that '$gdb_test_multiple_name' is so long, but I

> wanted something that was unlikely to clash with any existing variable

> name, or anything that a user is likely to want to use.


I'd go for "gdb_test_name", without the "multiple".  I wouldn't
be surprised if we find other uses for this without calling
gdb_test_multiple directly.  I think it's reasonable to claim
that the testsuite itself owns the gdb_ prefix.

Thanks,
Pedro Alves
Andrew Burgess Oct. 2, 2019, 1:03 p.m. | #3
* Keith Seitz <keiths@redhat.com> [2019-10-01 10:41:40 -0700]:

> On 10/1/19 10:28 AM, Andrew Burgess wrote:

> > After this patch you can now write this:

> > 

> >     gdb_test_multiple "print foo" "test foo" {

> >        -re "expected output 1" {

> >            pass $gdb_test_multiple_name

> >        }

> >        -re "expected output 2" {

> >            fail $gdb_test_multiple_name

> >        }

> >     }

> 

> OMG, why didn't *I* think of that!?!

> 

> > The $gdb_test_multiple_name is setup by gdb_test_multiple, and cleaned

> > up once the test has completed.  Nested calls to gdb_test_multiple are

> > supported, though $gdb_test_multiple_name will only ever contain the

> > inner most test message (which is probably what you want).

> 

> Your patch looks good to me (but I am not a maintainer), but I wanted to

> ask if you considered using a stack to more robustly solve the nesting

> problem? It might be unnecessary today -- even overkill -- but I

> would think it is much easier on the eyes.


Is your suggestion that we maintain a separate global test name stack
and push/pop the latest test name to the stack?   This would have the
advantage that a nested action element could access the names of outer
scopes, but this feels to me like something that would be both a
pretty rare requirement, and something that can easily be solved by
the user setting up some global variables as needed.

On the question of robustness, I'd be interested to know if you've
spotted a possible bug.  My intention is that the TCL program stack
should serve to backup any existing value of gdb_test_multiple_name,
so there should never be a case where we have the wrong test name in
place.

Thanks,
Andrew

> 

> We have such support in the test suite already. See 

> testsuite/lib/data-structures.exp. [I am not suggesting any

> changes, just trying to raise awareness for ::Stack et al usage

> in the test suite.]

> 

> Keith
Keith Seitz Oct. 2, 2019, 5:17 p.m. | #4
On 10/2/19 6:03 AM, Andrew Burgess wrote:
> * Keith Seitz <keiths@redhat.com> [2019-10-01 10:41:40 -0700]:

>

>> Your patch looks good to me (but I am not a maintainer), but I wanted to

>> ask if you considered using a stack to more robustly solve the nesting

>> problem? It might be unnecessary today -- even overkill -- but I

>> would think it is much easier on the eyes.

> 

> Is your suggestion that we maintain a separate global test name stack

> and push/pop the latest test name to the stack?


I am not making any suggestions, no. Given

> $gdb_test_multiple_name will only ever contain the inner most test message


I was just curious whether you considered using a stack, whether you had
any thoughts on whether we might ever want to know more than just the
innermost test name.

As I wrote in my original message, my intent was more to raise awareness that
this /could/ be a solution to the problem -- not to recommend/require/demand
any changes.

> On the question of robustness, I'd be interested to know if you've

> spotted a possible bug.  My intention is that the TCL program stack

> should serve to backup any existing value of gdb_test_multiple_name,

> so there should never be a case where we have the wrong test name in

> place.


Nope, nothing pops to mind that this patch as-is shouldn't immediately serve.

I apologize if I was unclear.

Keith
Andrew Burgess Oct. 3, 2019, 10:02 a.m. | #5
* Keith Seitz <keiths@redhat.com> [2019-10-02 10:17:04 -0700]:

> On 10/2/19 6:03 AM, Andrew Burgess wrote:

> > * Keith Seitz <keiths@redhat.com> [2019-10-01 10:41:40 -0700]:

> >

> >> Your patch looks good to me (but I am not a maintainer), but I wanted to

> >> ask if you considered using a stack to more robustly solve the nesting

> >> problem? It might be unnecessary today -- even overkill -- but I

> >> would think it is much easier on the eyes.

> > 

> > Is your suggestion that we maintain a separate global test name stack

> > and push/pop the latest test name to the stack?

> 

> I am not making any suggestions, no. Given

> 

> > $gdb_test_multiple_name will only ever contain the inner most test message

> 

> I was just curious whether you considered using a stack, whether you had

> any thoughts on whether we might ever want to know more than just the

> innermost test name.

> 

> As I wrote in my original message, my intent was more to raise awareness that

> this /could/ be a solution to the problem -- not to recommend/require/demand

> any changes.

> 

> > On the question of robustness, I'd be interested to know if you've

> > spotted a possible bug.  My intention is that the TCL program stack

> > should serve to backup any existing value of gdb_test_multiple_name,

> > so there should never be a case where we have the wrong test name in

> > place.

> 

> Nope, nothing pops to mind that this patch as-is shouldn't immediately serve.

> 

> I apologize if I was unclear.


No problem at all, I value all feedback.

To get a feel for what it might look like I implemented the patch
below which make use of the stack.

While writing the patch I started to think that it was a pretty neat
idea, and offered something over my original solution.  Here's the
example I was typing out when I started to get excited:

    gdb_test_multiple "command" "message" {
      -re "pattern" {
        gdb_test_multiple "another_command" "" {
          -re "another_pattern" {
            pass [gdb_test_name 1]
          }
        }
      }
    }

The idea is that access to the test name stack is hidden behind a proc
'gdb_test_name', which has a default index parameter of 0, so normally
you'd just write [gdb_test_name] where before you'd have written
$gdb_test_name.

But the power is that you can provide the index if you need to, so in
the above example you'd see 'PASS: script.exp: message' even though
pass is called from a nested gdb_test_multiple.

But then I thought; but what about all of the implicit patterns that
get added by 'gdb_test_multiple', if they trigger then we should have
a test name, not the empty string, so then I would probably write
this:

    gdb_test_multiple "command" "message" {
      -re "pattern" {
        gdb_test_multiple "another_command" "inner-command-message" {
          -re "another_pattern" {
            pass [gdb_test_name 1]
          }
        }
      }
    }

OK, that's better, but, that kind-of breaks the rule about using the
same test name for fails as for passes, if the inner command fails
we'll see 'FAIL: script.exp: inner-command-message', but when it
passes, we'll see 'PASS: script.exp: message'.

So then I started asking myself, should we ever reuse a test name for
a nested test (I say /should/, not /do we/ as some of our older test
naming was not as strict as we are today).  My gut feeling is not,
every nested call should have a unique test name.

So then, thinking about my original patch, I realised two things,
first we could do this:

    gdb_test_multiple "command" "message" {
      -re "pattern" {
        gdb_test_multiple "another_command" "$gdb_test_name" {
          -re "another_pattern" {
            pass $gdb_test_name
          }
        }
      }
    }

This is certainly bad practice I think, the nested test shares a test
name with the outer test.  If everything passes we'll not notice due
to there only being one pass, but here we get the same result as the
stack, just using the single global variable, and second with either
solution we can easily do this:

    gdb_test_multiple "command" "message" {
      -re "pattern" {
        gdb_test_multiple "another_command" "$gdb_test_name: inner stuff" {
          -re "another_pattern" {
            pass $gdb_test_name
          }
        }
      }
    }

Just adding a suffix to make things unique, however, in both cases the
need to reference an outer test is gone - we should always end up
referencing the most inner test name.

My conclusion then is that the stack solution makes it easier to do
what is usually going to be a bad thing (reference an outer test
name).  If a user really feels they need it, then within their test
script they can create their own global variable to hold the test
name.

Thanks,
Andrew

---

commit 95cdae42c6d935a16afff2d64b075eec9253bdbd
Author: Andrew Burgess <andrew.burgess@embecosm.com>
Date:   Thu Oct 3 10:46:42 2019 +0100

    gdb: A version of gdb_test_name using a stack
    
    Untested prototype.

diff --git a/gdb/testsuite/gdb.base/annota1.exp b/gdb/testsuite/gdb.base/annota1.exp
index 2d4737f533f..086b8f6a2f6 100644
--- a/gdb/testsuite/gdb.base/annota1.exp
+++ b/gdb/testsuite/gdb.base/annota1.exp
@@ -15,7 +15,6 @@
 
 # This file was written by Elena Zannoni (ezannoni@cygnus.com)
 
-
 # This testcase cannot use runto_main because of the different prompt
 # we get when using annotation level 2.
 #
@@ -97,11 +96,11 @@ gdb_expect {
 #
 gdb_test_multiple "info break" "breakpoint info" {
     -re "\r\n\032\032post-prompt\r\n\r\n\032\032breakpoints-headers\r\n\r\n\032\032field 0\r\nNum     \r\n\032\032field 1\r\nType           \r\n\032\032field 2\r\nDisp \r\n\032\032field 3\r\nEnb \r\n\032\032field 4\r\nAddress    +\r\n\032\032field 5\r\nWhat\r\n\r\n\032\032breakpoints-table\r\n\r\n\032\032record\r\n\r\n\032\032field 0\r\n1       \r\n\032\032field 1\r\nbreakpoint     \r\n\032\032field 2\r\nkeep \r\n\032\032field 3\r\ny   \r\n\032\032field 4\r\n$hex +\r\n\032\032field 5\r\nin main at ${escapedsrcfile}:$main_line\r\n\r\n\032\032breakpoints-table-end\r\n$gdb_prompt$" {
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
     -re "\r\n\032\032post-prompt\r\n\r\n\032\032breakpoints-headers\r\n\r\n\032\032field 0\r\nNum     \r\n\032\032field 1\r\nType           \r\n\032\032field 2\r\nDisp \r\n\032\032field 3\r\nEnb \r\n\032\032field 4\r\nAddress    +\r\n\032\032field 5\r\nWhat\r\n\r\n\032\032breakpoints-table\r\n\r\n\032\032record\r\n\r\n\032\032field 0\r\n1       \r\n\032\032field 1\r\nbreakpoint     \r\n\032\032field 2\r\nkeep \r\n\032\032field 3\r\ny   \r\n\032\032field 4\r\n$hex +\r\n\032\032field 5\r\nin main at .*${srcfile}:$main_line\r\n\r\n\032\032breakpoints-table-end\r\n$gdb_prompt$" {
 	setup_xfail "*-*-*" 1270
-	fail $gdb_test_name
+	fail [gdb_test_name]
     }
 }
 
@@ -147,7 +146,7 @@ gdb_test_multiple "run" "run until main breakpoint" {
 		    "\032\032source.*$srcfile:$main_line:.*:beg:$hex\r\n\r\n" \
 		    "\032\032frame-end\r\n\r\n" \
 		    "\032\032stopped.*$gdb_prompt$" } ] {
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
 }
 #exp_internal 0
@@ -160,7 +159,7 @@ gdb_test_multiple "run" "run until main breakpoint" {
 #
 gdb_test_multiple "next" "go after array init line" {
     -re "source .*annota1.c.*$gdb_prompt$" {
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
 }
 
@@ -179,7 +178,7 @@ gdb_test_multiple "next" "go after array init line" {
 #
 gdb_test_multiple "print my_array" "print array" {
     -re "\r\n\032\032post-prompt\r\n\r\n\032\032value-history-begin 1 -\r\n.*= \r\n\032\032value-history-value\r\n.\r\n\032\032array-section-begin 0 -\r\n1\r\n\032\032elt\r\n, 2\r\n\032\032elt\r\n, 3\r\n\032\032elt\r\n\r\n\032\032array-section-end\r\n.\r\n\r\n\032\032value-history-end\r\n$gdb_prompt$" {
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
 }
 
@@ -193,7 +192,7 @@ gdb_test_multiple "print my_array" "print array" {
 #exp_internal 1
 gdb_test_multiple "print non_existent_value" "print non_existent_value" {
     -re "\r\n\032\032post-prompt\r\n\r\n\032\032error-begin\r\nNo symbol \"non_existent_value\" in current context.\r\n\r\n\032\032error\r\n$gdb_prompt$" {
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
 }
 
@@ -204,7 +203,7 @@ gdb_test_multiple "print non_existent_value" "print non_existent_value" {
 #
 gdb_test_multiple "break handle_USR1" "break handle_USR1" {
     -re  "\r\n\032\032post-prompt\r\nBreakpoint.*at $hex: file.*$srcfile, line.*\r\n\032\032breakpoints-invalid\r\n.*$gdb_prompt$" {
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
 }
 
@@ -213,10 +212,10 @@ gdb_test_multiple "break handle_USR1" "break handle_USR1" {
 #
 gdb_test_multiple "break printf" "break printf" {
     -re  "\r\n\032\032post-prompt\r\nBreakpoint.*at $hex.*\032\032breakpoints-invalid\r\n.*$gdb_prompt$" {
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
     -re  "\r\n\032\032post-prompt\r\nwarning: Breakpoint address adjusted from $hex to $hex.\r\n\r\n\032\032breakpoints-invalid\r\nBreakpoint.*at $hex.*$gdb_prompt$" {
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
 }
 
@@ -229,9 +228,9 @@ set pat_end "\r\n\032\032breakpoint 3\r\n\r\nBreakpoint 3, \r\n\032\032frame-beg
 
 gdb_test_multiple "continue" "continue to printf" {
     -re "${pat_begin}($pat_adjust)?$pat_end" {
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
-    -re ".*$gdb_prompt$"     { fail $gdb_test_name }
+    -re ".*$gdb_prompt$"     { fail [gdb_test_name] }
 }
 
 #
@@ -246,11 +245,11 @@ set pat_end "\r\n\032\032frame-source-file-end\r\n:\r\n\032\032frame-source-line
 
 gdb_test_multiple "backtrace" "backtrace from shlibrary" {
     -re "$pat_begin$escapedsrcfile$pat_end" {
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
     -re "$pat_begin.*$srcfile$pat_end" {
 	setup_xfail "*-*-*" 1270
-	fail $gdb_test_name
+	fail [gdb_test_name]
     }
 }
 
@@ -269,11 +268,11 @@ if [target_info exists gdb,nosignals] {
 } else {
     gdb_test_multiple "signal SIGUSR1" "send SIGUSR1" {
 	-re "\r\n\032\032post-prompt\r\nContinuing with signal SIGUSR1.\r\n\r\n\032\032starting\(\(\r\n\r\n\032\032frames-invalid\)|\(\r\n\r\n\032\032breakpoints-invalid\)\)+\r\n\r\n\032\032breakpoint 2\r\n\r\nBreakpoint 2, \r\n\032\032frame-begin 0 $hex\r\n\r\n\032\032frame-function-name\r\nhandle_USR1\r\n\032\032frame-args\r\n \\(\r\n\032\032arg-begin\r\nsig\r\n\032\032arg-name-end\r\n=\r\n\032\032arg-value -\r\n$decimal\r\n\032\032arg-end\r\n\\)\r\n\032\032frame-source-begin\r\n at \r\n\032\032frame-source-file\r\n${escapedsrcfile}\r\n\032\032frame-source-file-end\r\n:\r\n\032\032frame-source-line\r\n.*\r\n\032\032frame-source-end\r\n\r\n\r\n\032\032source.*annota1.c:.*:.*:beg:$hex\r\n$decimal\[^\r\n\]+\r\n\r\n\032\032frame-end\r\n\r\n\032\032stopped\r\n$gdb_prompt$" {
-	    pass $gdb_test_name
+	    pass [gdb_test_name]
 	}
 	-re "\r\n\032\032post-prompt\r\nContinuing with signal SIGUSR1.\r\n\r\n\032\032starting\(\(\r\n\r\n\032\032frames-invalid\)|\(\r\n\r\n\032\032breakpoints-invalid\)\)+\r\n\r\n\032\032breakpoint 2\r\n\r\nBreakpoint 2, \r\n\032\032frame-begin 0 $hex\r\n\r\n\032\032frame-function-name\r\nhandle_USR1\r\n\032\032frame-args\r\n \\(\r\n\032\032arg-begin\r\nsig\r\n\032\032arg-name-end\r\n=\r\n\032\032arg-value -\r\n$decimal\r\n\032\032arg-end\r\n\\)\r\n\032\032frame-source-begin\r\n at \r\n\032\032frame-source-file\r\n.*${srcfile}\r\n\032\032frame-source-file-end\r\n:\r\n\032\032frame-source-line\r\n.*\r\n\032\032frame-source-end\r\n\r\n\r\n\032\032source.*annota1.c:.*:.*:beg:$hex\r\n$decimal\[^\r\n\]+\r\n\r\n\032\032frame-end\r\n\r\n\032\032stopped\r\n$gdb_prompt$" {
 	    setup_xfail "*-*-*" 1270
-	    fail $gdb_test_name
+	    fail [gdb_test_name]
 	}
     }
 
@@ -283,7 +282,7 @@ if [target_info exists gdb,nosignals] {
     #
     gdb_test_multiple "backtrace" "backtrace @ signal handler" {
 	-re "frame-begin 0 $hex\r\n#0.*frame-end.*frame-begin 1 $hex\r\n#1.*(\032\032signal-handler-caller\r\n.signal handler called.\r\n\r\n)+\032\032frame-end\r\n\r\n\032\032frame-begin 2 $hex\r\n#2.*(frame-begin 3 $hex\r\n#3.*)*frame-end.*$gdb_prompt$" {
-	    pass $gdb_test_name
+	    pass [gdb_test_name]
 	}
     }
 }
@@ -293,19 +292,19 @@ if [target_info exists gdb,nosignals] {
 #
 gdb_test_multiple "delete 1" "delete bp 1" {
     -re "\r\n\032\032post-prompt\r\n${breakpoints_invalid}$gdb_prompt$" {
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
 }
 
 gdb_test_multiple "delete 2" "delete bp 2" {
     -re "\r\n\032\032post-prompt\r\n${breakpoints_invalid}$gdb_prompt$" {
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
 }
 
 gdb_test_multiple "delete 3" "delete bp 3" {
     -re "\r\n\032\032post-prompt\r\n${breakpoints_invalid}$gdb_prompt$" {
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
 }
 
@@ -315,11 +314,11 @@ gdb_test_multiple "delete 3" "delete bp 3" {
 #
 gdb_test_multiple "break ${srcfile}:${main_line}" "break in main" {
     -re "post-prompt.*Breakpoint 4 at $hex: file ${escapedsrcfile}, line $main_line.*\032\032breakpoints-invalid.*$gdb_prompt$" {
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
     -re "post-prompt.*Breakpoint 4 at $hex: file .*${srcfile}, line $main_line.*\032\032breakpoints-invalid.*$gdb_prompt$" {
 	setup_xfail "*-*-*" 1270
-	fail $gdb_test_name
+	fail [gdb_test_name]
     }
 }
 
@@ -408,7 +407,7 @@ set pid -1
 gdb_test_multiple "info inferior 1" "get inferior pid" {
     -re "process (\[0-9\]*).*$gdb_prompt$" {
 	set pid $expect_out(1,string)
-	pass $gdb_test_name
+	pass [gdb_test_name]
     }
 }
 
@@ -427,7 +426,7 @@ if [target_info exists gdb,nosignals] {
 } else {
     gdb_test_multiple "signal SIGTRAP" "signal sent" {
 	-re ".*\032\032post-prompt\r\nContinuing with signal SIGTRAP.\r\n\r\n\032\032starting\(\r\n\r\n\032\032frames-invalid\)+\r\n\r\n\032\032signalled\r\n\r\nProgram terminated with signal \r\n\032\032signal-name\r\nSIGTRAP\r\n\032\032signal-name-end\r\n, \r\n\032\032signal-string\r\nTrace.breakpoint trap\r\n\032\032signal-string-end\r\n.\r\nThe program no longer exists.\r\n\r\n\032\032thread-exited,id=\"${decimal}\",group-id=\"i${decimal}\"\r\n\r\n\032\032stopped\r\n$gdb_prompt$" {
-	    pass $gdb_test_name
+	    pass [gdb_test_name]
 	}
     }
 }
@@ -480,13 +479,13 @@ proc thread_test {} {
 
 	gdb_test_multiple "continue" "new thread" {
 	    -re "\032\032new-thread.*\r\n$gdb_prompt$" {
-		pass $gdb_test_name
+		pass [gdb_test_name]
 	    }
 	}
 
     gdb_test_multiple "continue" "thread exit" {
 	    -re "\032\032thread-exited,id=\"${decimal}\",group-id=\"i${decimal}\".*\r\n$gdb_prompt$" {
-		pass $gdb_test_name
+		pass [gdb_test_name]
 	    }
     }
     }
@@ -495,7 +494,7 @@ proc thread_test {} {
 proc thread_switch {} {
     gdb_test_multiple "thread 1" "thread switch" {
 	-re ".*\032\032thread-changed" {
-	    pass $gdb_test_name
+	    pass [gdb_test_name]
 	}
     }
 }
diff --git a/gdb/testsuite/lib/data-structures.exp b/gdb/testsuite/lib/data-structures.exp
index 5c136d329c5..fac9195320a 100644
--- a/gdb/testsuite/lib/data-structures.exp
+++ b/gdb/testsuite/lib/data-structures.exp
@@ -103,8 +103,24 @@ namespace eval ::Stack {
 	}
     }
 
+    proc at {oid idx} {
+	variable data_
+
+	error_if $oid
+	if {[llength $data_($oid)] < $idx} {
+	    ::error "stack index out of range"
+	}
+
+	verbose -log "Got: $data_($oid)"
+
+	# We need to index from the far end of the array, as that is
+	# where the most recent elements are.
+	set idx [expr [llength $data_($oid)] - $idx - 1]
+	return [lindex $data_($oid) $idx]
+    }
+
     # Export procs to be used.
-    namespace export empty push pop new delete length error_if
+    namespace export empty push pop new delete length error_if at
 
     # Create an ensemble command to use instead of requiring users
     # to type namespace proc names.
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index a35c3d75516..3471007c1ee 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -29,6 +29,7 @@ load_lib libgloss.exp
 load_lib cache.exp
 load_lib gdb-utils.exp
 load_lib memory.exp
+load_lib data-structures.exp
 
 global GDB
 
@@ -694,6 +695,41 @@ proc gdb_internal_error_resync {} {
     return 0
 }
 
+# Global stack used to track current nested calls to gdb_test_multiple.
+set gdb_test_name_stack [::Stack::new]
+
+# Returns the MESSAGE that was passed to gdb_test_multiple.  By
+# default (with no arguments) this returns the MESSAGE from the last
+# call to gdb_test_multiple.  Positive integer arguments index into
+# previously nested calls.  For example, this is the most common use case:
+#
+# gdb_test_multiple "command" "message" {
+#   -re "pattern" {
+#     pass [gdb_test_name]
+#   }
+# }
+#
+# In this case you would expect to see 'PASS: script.exp: message".
+#
+# The INDEX argument can be used like this:
+#
+# gdb_test_multiple "command" "message" {
+#   -re "pattern" {
+#     gdb_test_multiple "another_command" "" {
+#       -re "another_pattern" {
+#         pass [gdb_test_name 1]
+#       }
+#     }
+#   }
+# }
+#
+# In this case you would expect to see "PASS: script.exp: message",
+# even though the pass is actually coming from the nested call to
+# gdb_test_multiple.
+proc gdb_test_name {{index 0}} {
+    global gdb_test_name_stack
+    return [stack at $gdb_test_name_stack $index]
+}
 
 # gdb_test_multiple COMMAND MESSAGE EXPECT_ARGUMENTS PROMPT_REGEXP
 # Send a command to gdb; test the result.
@@ -726,17 +762,17 @@ proc gdb_internal_error_resync {} {
 #    }
 # }
 #
-# Within action elements you can also make use of the variable
-# gdb_test_name, this variable is setup automatically by
-# gdb_test_multiple, and contains the value of MESSAGE.  You can then
-# write this, which is equivalent to the above:
+# Within action elements you can also make use of the procedure
+# gdb_test_name, this returns the MESSAGE that was passed to the last
+# call to gdb_test_multiple.  You can then write this, which is
+# equivalent to the above:
 #
 # gdb_test_multiple "print foo" "test foo" {
 #    -re "expected output 1" {
-#        pass $gdb_test_name
+#        pass [gdb_test_name]
 #    }
 #    -re "expected output 2" {
-#        fail $gdb_test_name
+#        fail [gdb_test_name]
 #    }
 # }
 #
@@ -769,6 +805,7 @@ proc gdb_test_multiple { command message user_code { prompt_regexp "" } } {
     upvar timeout timeout
     upvar expect_out expect_out
     global any_spawn_id
+    global gdb_test_name_stack
 
     if { "$prompt_regexp" == "" } {
 	set prompt_regexp "$gdb_prompt $"
@@ -1052,27 +1089,14 @@ proc gdb_test_multiple { command message user_code { prompt_regexp "" } } {
 	}
     }
 
-    # Create gdb_test_name in the parent scope.  If this variable
-    # already exists, which is might if we have nested calls to
-    # gdb_test_multiple, then preserve the old value, otherwise,
-    # create a new variable in the parent scope.
-    upvar gdb_test_name gdb_test_name
-    if { [info exists gdb_test_name] } {
-	set gdb_test_name_old "$gdb_test_name"
-    }
-    set gdb_test_name "$message"
+    # Add the latest test name to the test name stack.
+    stack push $gdb_test_name_stack "$message"
 
     set result 0
     set code [catch {gdb_expect $code} string]
 
-    # Clean up the gdb_test_name variable.  If we had a
-    # previous value then restore it, otherwise, delete the variable
-    # from the parent scope.
-    if { [info exists gdb_test_name_old] } {
-	set gdb_test_name "$gdb_test_name_old"
-    } else {
-	unset gdb_test_name
-    }
+    # Clean up the test name stack.
+    stack pop $gdb_test_name_stack
 
     if {$code == 1} {
 	global errorInfo errorCode
Tom de Vries Oct. 6, 2019, 8:24 a.m. | #6
On 01-10-19 19:28, Andrew Burgess wrote:
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp

> index 3a1f053cf8a..a472650cc75 100644

> --- a/gdb/testsuite/lib/gdb.exp

> +++ b/gdb/testsuite/lib/gdb.exp

> @@ -719,10 +719,24 @@ proc gdb_internal_error_resync {} {

>  #

>  # gdb_test_multiple "print foo" "test foo" {

>  #    -re "expected output 1" {

> -#        pass "print foo"

> +#        pass "test foo"

>  #    }

>  #    -re "expected output 2" {

> -#        fail "print foo"

> +#        fail "test foo"

> +#    }

> +# }

> +#

> +# Within action elements you can also make use of the variable

> +# gdb_test_multiple_name, this variable is setup automatically by

> +# gdb_test_multiple, and contains the value of MESSAGE.  You can then

> +# write this, which is equivalent to the above:

> +#


I'd split up the first line, f.i. like so:
...
# Within action elements you can also make use of the variable
# gdb_test_multiple_name.  This variable is setup automatically by
# gdb_test_multiple, and contains the value of MESSAGE.
...


> +# gdb_test_multiple "print foo" "test foo" {

> +#    -re "expected output 1" {

> +#        pass $gdb_test_multiple_name

> +#    }

> +#    -re "expected output 2" {

> +#        fail $gdb_test_multiple_name

>  #    }

>  # }

>  #

> @@ -1038,8 +1052,28 @@ proc gdb_test_multiple { command message user_code { prompt_regexp "" } } {

>  	}

>      }

>  

> +    # Create gdb_test_multiple_name in the parent scope.  If this

> +    # variable already exists, which is might if we have nested calls


is -> it

> +    # to gdb_test_multiple, then preserve the old value, otherwise,

> +    # create a new variable in the parent scope.

> +    upvar gdb_test_multiple_name gdb_test_multiple_name

> +    if { [info exists gdb_test_multiple_name] } {

> +	set gdb_test_multiple_name_old "$gdb_test_multiple_name"

> +    }

> +    set gdb_test_multiple_name "$message"

> +

>      set result 0

>      set code [catch {gdb_expect $code} string]

> +

> +    # Clean up the gdb_test_multiple_name variable.  If we had a

> +    # previous value then restore it, otherwise, delete the variable

> +    # from the parent scope.

> +    if { [info exists gdb_test_multiple_name_old] } {

> +	set gdb_test_multiple_name "$gdb_test_multiple_name_old"

> +    } else {

> +	unset gdb_test_multiple_name

> +    }

> +

>      if {$code == 1} {

>  	global errorInfo errorCode

>  	return -code error -errorinfo $errorInfo -errorcode $errorCode $string

> 


Other than these nits, looks good to me.

[ FWIW, Pedro's suggestion of using gdb_test_name instead of
gdb_test_multiple_name is fine by me. ]

Thanks,
- Tom
Andrew Burgess Oct. 7, 2019, 10:28 a.m. | #7
Thank you for all the feedback, I have now pushed this patch.  I
changed the variable name to gdb_test_name as suggested by Pedro, and
merged Tom's comment improvements.

Thanks,
Andrew

Patch

diff --git a/gdb/testsuite/gdb.base/annota1.exp b/gdb/testsuite/gdb.base/annota1.exp
index 3d379f0fd49..96b0fce5cf7 100644
--- a/gdb/testsuite/gdb.base/annota1.exp
+++ b/gdb/testsuite/gdb.base/annota1.exp
@@ -97,11 +97,11 @@  gdb_expect {
 #
 gdb_test_multiple "info break" "breakpoint info" {
     -re "\r\n\032\032post-prompt\r\n\r\n\032\032breakpoints-headers\r\n\r\n\032\032field 0\r\nNum     \r\n\032\032field 1\r\nType           \r\n\032\032field 2\r\nDisp \r\n\032\032field 3\r\nEnb \r\n\032\032field 4\r\nAddress    +\r\n\032\032field 5\r\nWhat\r\n\r\n\032\032breakpoints-table\r\n\r\n\032\032record\r\n\r\n\032\032field 0\r\n1       \r\n\032\032field 1\r\nbreakpoint     \r\n\032\032field 2\r\nkeep \r\n\032\032field 3\r\ny   \r\n\032\032field 4\r\n$hex +\r\n\032\032field 5\r\nin main at ${escapedsrcfile}:$main_line\r\n\r\n\032\032breakpoints-table-end\r\n$gdb_prompt$" {
-	pass "breakpoint info"
+	pass $gdb_test_multiple_name
     }
     -re "\r\n\032\032post-prompt\r\n\r\n\032\032breakpoints-headers\r\n\r\n\032\032field 0\r\nNum     \r\n\032\032field 1\r\nType           \r\n\032\032field 2\r\nDisp \r\n\032\032field 3\r\nEnb \r\n\032\032field 4\r\nAddress    +\r\n\032\032field 5\r\nWhat\r\n\r\n\032\032breakpoints-table\r\n\r\n\032\032record\r\n\r\n\032\032field 0\r\n1       \r\n\032\032field 1\r\nbreakpoint     \r\n\032\032field 2\r\nkeep \r\n\032\032field 3\r\ny   \r\n\032\032field 4\r\n$hex +\r\n\032\032field 5\r\nin main at .*${srcfile}:$main_line\r\n\r\n\032\032breakpoints-table-end\r\n$gdb_prompt$" {
 	setup_xfail "*-*-*" 1270
-	fail "breakpoint info"
+	fail $gdb_test_multiple_name
     }
 }
 
@@ -147,7 +147,7 @@  gdb_test_multiple "run" "run until main breakpoint" {
 		    "\032\032source.*$srcfile:$main_line:.*:beg:$hex\r\n\r\n" \
 		    "\032\032frame-end\r\n\r\n" \
 		    "\032\032stopped.*$gdb_prompt$" } ] {
-	pass "run until main breakpoint"
+	pass $gdb_test_multiple_name
     }
 }
 #exp_internal 0
@@ -160,7 +160,7 @@  gdb_test_multiple "run" "run until main breakpoint" {
 #
 gdb_test_multiple "next" "go after array init line" {
     -re "source .*annota1.c.*$gdb_prompt$" {
-	pass "go after array init line"
+	pass $gdb_test_multiple_name
     }
 }
 
@@ -179,7 +179,7 @@  gdb_test_multiple "next" "go after array init line" {
 #
 gdb_test_multiple "print my_array" "print array" {
     -re "\r\n\032\032post-prompt\r\n\r\n\032\032value-history-begin 1 -\r\n.*= \r\n\032\032value-history-value\r\n.\r\n\032\032array-section-begin 0 -\r\n1\r\n\032\032elt\r\n, 2\r\n\032\032elt\r\n, 3\r\n\032\032elt\r\n\r\n\032\032array-section-end\r\n.\r\n\r\n\032\032value-history-end\r\n$gdb_prompt$" {
-	pass "print array"
+	pass $gdb_test_multiple_name
     }
 }
 
@@ -193,7 +193,7 @@  gdb_test_multiple "print my_array" "print array" {
 #exp_internal 1
 gdb_test_multiple "print non_existent_value" "print non_existent_value" {
     -re "\r\n\032\032post-prompt\r\n\r\n\032\032error-begin\r\nNo symbol \"non_existent_value\" in current context.\r\n\r\n\032\032error\r\n$gdb_prompt$" {
-	pass "print non_existent_value"
+	pass $gdb_test_multiple_name
     }
 }
 
@@ -204,7 +204,7 @@  gdb_test_multiple "print non_existent_value" "print non_existent_value" {
 #
 gdb_test_multiple "break handle_USR1" "break handle_USR1" {
     -re  "\r\n\032\032post-prompt\r\nBreakpoint.*at $hex: file.*$srcfile, line.*\r\n\032\032breakpoints-invalid\r\n.*$gdb_prompt$" {
-	pass "break handle_USR1"
+	pass $gdb_test_multiple_name
     }
 }
 
@@ -213,10 +213,10 @@  gdb_test_multiple "break handle_USR1" "break handle_USR1" {
 #
 gdb_test_multiple "break printf" "break printf" {
     -re  "\r\n\032\032post-prompt\r\nBreakpoint.*at $hex.*\032\032breakpoints-invalid\r\n.*$gdb_prompt$" {
-	pass "break printf" 
+	pass $gdb_test_multiple_name
     }
     -re  "\r\n\032\032post-prompt\r\nwarning: Breakpoint address adjusted from $hex to $hex.\r\n\r\n\032\032breakpoints-invalid\r\nBreakpoint.*at $hex.*$gdb_prompt$" {
-	pass "break printf"
+	pass $gdb_test_multiple_name
     }
 }
 
@@ -229,9 +229,9 @@  set pat_end "\r\n\032\032breakpoint 3\r\n\r\nBreakpoint 3, \r\n\032\032frame-beg
 
 gdb_test_multiple "continue" "continue to printf" {
     -re "${pat_begin}($pat_adjust)?$pat_end" {
-	pass "continue to printf"
+	pass $gdb_test_multiple_name
     }
-    -re ".*$gdb_prompt$"     { fail "continue to printf" }
+    -re ".*$gdb_prompt$"     { fail $gdb_test_multiple_name }
 }
 
 #
@@ -246,11 +246,11 @@  set pat_end "\r\n\032\032frame-source-file-end\r\n:\r\n\032\032frame-source-line
 
 gdb_test_multiple "backtrace" "backtrace from shlibrary" {
     -re "$pat_begin$escapedsrcfile$pat_end" {
-	pass "backtrace from shlibrary"
+	pass $gdb_test_multiple_name
     }
     -re "$pat_begin.*$srcfile$pat_end" {
 	setup_xfail "*-*-*" 1270
-	fail "backtrace from shlibrary"
+	fail $gdb_test_multiple_name
     }
 }
 
@@ -269,11 +269,11 @@  if [target_info exists gdb,nosignals] {
 } else {
     gdb_test_multiple "signal SIGUSR1" "send SIGUSR1" {
 	-re "\r\n\032\032post-prompt\r\nContinuing with signal SIGUSR1.\r\n\r\n\032\032starting\(\(\r\n\r\n\032\032frames-invalid\)|\(\r\n\r\n\032\032breakpoints-invalid\)\)+\r\n\r\n\032\032breakpoint 2\r\n\r\nBreakpoint 2, \r\n\032\032frame-begin 0 $hex\r\n\r\n\032\032frame-function-name\r\nhandle_USR1\r\n\032\032frame-args\r\n \\(\r\n\032\032arg-begin\r\nsig\r\n\032\032arg-name-end\r\n=\r\n\032\032arg-value -\r\n$decimal\r\n\032\032arg-end\r\n\\)\r\n\032\032frame-source-begin\r\n at \r\n\032\032frame-source-file\r\n${escapedsrcfile}\r\n\032\032frame-source-file-end\r\n:\r\n\032\032frame-source-line\r\n.*\r\n\032\032frame-source-end\r\n\r\n\r\n\032\032source.*annota1.c:.*:.*:beg:$hex\r\n$decimal\[^\r\n\]+\r\n\r\n\032\032frame-end\r\n\r\n\032\032stopped\r\n$gdb_prompt$" {
-	    pass "send SIGUSR1"
+	    pass $gdb_test_multiple_name
 	}
 	-re "\r\n\032\032post-prompt\r\nContinuing with signal SIGUSR1.\r\n\r\n\032\032starting\(\(\r\n\r\n\032\032frames-invalid\)|\(\r\n\r\n\032\032breakpoints-invalid\)\)+\r\n\r\n\032\032breakpoint 2\r\n\r\nBreakpoint 2, \r\n\032\032frame-begin 0 $hex\r\n\r\n\032\032frame-function-name\r\nhandle_USR1\r\n\032\032frame-args\r\n \\(\r\n\032\032arg-begin\r\nsig\r\n\032\032arg-name-end\r\n=\r\n\032\032arg-value -\r\n$decimal\r\n\032\032arg-end\r\n\\)\r\n\032\032frame-source-begin\r\n at \r\n\032\032frame-source-file\r\n.*${srcfile}\r\n\032\032frame-source-file-end\r\n:\r\n\032\032frame-source-line\r\n.*\r\n\032\032frame-source-end\r\n\r\n\r\n\032\032source.*annota1.c:.*:.*:beg:$hex\r\n$decimal\[^\r\n\]+\r\n\r\n\032\032frame-end\r\n\r\n\032\032stopped\r\n$gdb_prompt$" {
 	    setup_xfail "*-*-*" 1270
-	    fail "send SIGUSR1"
+	    fail $gdb_test_multiple_name
 	}
     }
 
@@ -283,7 +283,7 @@  if [target_info exists gdb,nosignals] {
     #
     gdb_test_multiple "backtrace" "backtrace @ signal handler" {
 	-re "frame-begin 0 $hex\r\n#0.*frame-end.*frame-begin 1 $hex\r\n#1.*(\032\032signal-handler-caller\r\n.signal handler called.\r\n\r\n)+\032\032frame-end\r\n\r\n\032\032frame-begin 2 $hex\r\n#2.*(frame-begin 3 $hex\r\n#3.*)*frame-end.*$gdb_prompt$" {
-	    pass "backtrace @ signal handler"
+	    pass $gdb_test_multiple_name
 	}
     }
 }
@@ -293,19 +293,19 @@  if [target_info exists gdb,nosignals] {
 #
 gdb_test_multiple "delete 1" "delete bp 1" {
     -re "\r\n\032\032post-prompt\r\n${breakpoints_invalid}$gdb_prompt$" {
-	pass "delete bp 1"
+	pass $gdb_test_multiple_name
     }
 }
 
 gdb_test_multiple "delete 2" "delete bp 2" {
     -re "\r\n\032\032post-prompt\r\n${breakpoints_invalid}$gdb_prompt$" {
-	pass "delete bp 2"
+	pass $gdb_test_multiple_name
     }
 }
 
 gdb_test_multiple "delete 3" "delete bp 3" {
     -re "\r\n\032\032post-prompt\r\n${breakpoints_invalid}$gdb_prompt$" {
-	pass "delete bp 3"
+	pass $gdb_test_multiple_name
     }
 }
 
@@ -313,14 +313,13 @@  gdb_test_multiple "delete 3" "delete bp 3" {
 # break in main, after value is initialized. This is in preparation
 # to test the annotate output for the display command.
 #
-set test "break in main"
-gdb_test_multiple "break ${srcfile}:${main_line}" $test {
+gdb_test_multiple "break ${srcfile}:${main_line}" "break in main" {
     -re "post-prompt.*Breakpoint 4 at $hex: file ${escapedsrcfile}, line $main_line.*\032\032breakpoints-invalid.*$gdb_prompt$" {
-	pass $test
+	pass $gdb_test_multiple_name
     }
     -re "post-prompt.*Breakpoint 4 at $hex: file .*${srcfile}, line $main_line.*\032\032breakpoints-invalid.*$gdb_prompt$" {
 	setup_xfail "*-*-*" 1270
-	fail $test
+	fail $gdb_test_multiple_name
     }
 }
 
@@ -405,12 +404,11 @@  gdb_test_multiple "next" "breakpoint ignore count" {
 
 # Get the inferior's PID for later.
 
-set test "get inferior pid"
 set pid -1
-gdb_test_multiple "info inferior 1" "$test" {
+gdb_test_multiple "info inferior 1" "get inferior pid" {
     -re "process (\[0-9\]*).*$gdb_prompt$" {
 	set pid $expect_out(1,string)
-	pass "$test"
+	pass $gdb_test_multiple_name
     }
 }
 
@@ -429,7 +427,7 @@  if [target_info exists gdb,nosignals] {
 } else {
     gdb_test_multiple "signal SIGTRAP" "signal sent" {
 	-re ".*\032\032post-prompt\r\nContinuing with signal SIGTRAP.\r\n\r\n\032\032starting\(\r\n\r\n\032\032frames-invalid\)+\r\n\r\n\032\032signalled\r\n\r\nProgram terminated with signal \r\n\032\032signal-name\r\nSIGTRAP\r\n\032\032signal-name-end\r\n, \r\n\032\032signal-string\r\nTrace.breakpoint trap\r\n\032\032signal-string-end\r\n.\r\nThe program no longer exists.\r\n\r\n\032\032thread-exited,id=\"${decimal}\",group-id=\"i${decimal}\"\r\n\r\n\032\032stopped\r\n$gdb_prompt$" {
-	    pass "signal sent"
+	    pass $gdb_test_multiple_name
 	}
     }
 }
@@ -482,13 +480,13 @@  proc thread_test {} {
 
 	gdb_test_multiple "continue" "new thread" {
 	    -re "\032\032new-thread.*\r\n$gdb_prompt$" {
-		pass "new thread"
+		pass $gdb_test_multiple_name
 	    }
 	}
 
     gdb_test_multiple "continue" "thread exit" {
 	    -re "\032\032thread-exited,id=\"${decimal}\",group-id=\"i${decimal}\".*\r\n$gdb_prompt$" {
-		pass "thread exit"
+		pass $gdb_test_multiple_name
 	    }
     }
     }
@@ -497,7 +495,7 @@  proc thread_test {} {
 proc thread_switch {} {
     gdb_test_multiple "thread 1" "thread switch" {
 	-re ".*\032\032thread-changed" {
-	    pass "thread switch"
+	    pass $gdb_test_multiple_name
 	}
     }
 }
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 3a1f053cf8a..a472650cc75 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -719,10 +719,24 @@  proc gdb_internal_error_resync {} {
 #
 # gdb_test_multiple "print foo" "test foo" {
 #    -re "expected output 1" {
-#        pass "print foo"
+#        pass "test foo"
 #    }
 #    -re "expected output 2" {
-#        fail "print foo"
+#        fail "test foo"
+#    }
+# }
+#
+# Within action elements you can also make use of the variable
+# gdb_test_multiple_name, this variable is setup automatically by
+# gdb_test_multiple, and contains the value of MESSAGE.  You can then
+# write this, which is equivalent to the above:
+#
+# gdb_test_multiple "print foo" "test foo" {
+#    -re "expected output 1" {
+#        pass $gdb_test_multiple_name
+#    }
+#    -re "expected output 2" {
+#        fail $gdb_test_multiple_name
 #    }
 # }
 #
@@ -1038,8 +1052,28 @@  proc gdb_test_multiple { command message user_code { prompt_regexp "" } } {
 	}
     }
 
+    # Create gdb_test_multiple_name in the parent scope.  If this
+    # variable already exists, which is might if we have nested calls
+    # to gdb_test_multiple, then preserve the old value, otherwise,
+    # create a new variable in the parent scope.
+    upvar gdb_test_multiple_name gdb_test_multiple_name
+    if { [info exists gdb_test_multiple_name] } {
+	set gdb_test_multiple_name_old "$gdb_test_multiple_name"
+    }
+    set gdb_test_multiple_name "$message"
+
     set result 0
     set code [catch {gdb_expect $code} string]
+
+    # Clean up the gdb_test_multiple_name variable.  If we had a
+    # previous value then restore it, otherwise, delete the variable
+    # from the parent scope.
+    if { [info exists gdb_test_multiple_name_old] } {
+	set gdb_test_multiple_name "$gdb_test_multiple_name_old"
+    } else {
+	unset gdb_test_multiple_name
+    }
+
     if {$code == 1} {
 	global errorInfo errorCode
 	return -code error -errorinfo $errorInfo -errorcode $errorCode $string