[v3,2/2] gdb: add shadowed field in '-stack-list-locals/variables' mi commands

Message ID 20230918164738.17082-3-abdul.b.ijaz@intel.com
State New
Headers
Series add annotation in 'info locals' command for variables shadowing case |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 fail Testing failed
linaro-tcwg-bot/tcwg_gdb_check--master-arm fail Testing failed

Commit Message

Ijaz, Abdul B Sept. 18, 2023, 4:47 p.m. UTC
  From: "Ijaz, Abdul B" <abdul.b.ijaz@intel.com>

For C/C++/Fortran languages GDB prints same name variable multiple times in
case of variable shadowing and it is confusing for user to identify which
variable belongs to the current scope.  So GDB now prints location information
for shadowed variables and add 'shadowed' field also in '-stack-list-locals'
and '-stack-list-variables' mi commands for super-block shadowed variable.

Suppose we have test.c file

1:int x = 42;
2:  {
3:    int x = 99;
4:    int y = 52;
4:    x = 99; /* break here */
5:  }

The "-stack-list-locals" and "-stack-list-variables" mi commands at the
"break here" line gives the following output:

Before the change:

~~~
(gdb)
-stack-list-locals 0
^done,locals=[name="x",name="y",name="x"]
(gdb)
-stack-list-locals 1
^done,locals=[{name="x",value="99"},{name="y",value="52"},{name="x",value="42"}]
(gdb)
-stack-list-locals 2
^done,locals=[{name="x",type="int",value="99"},{name="y",type="int",value="52"},{name="x",type="int",value="42"}]
(gdb)
-stack-list-variables 0
^done,variables=[{name="x"},{name="y"},{name="x"}]
(gdb)
-stack-list-variables 1
^done,variables=[{name="x",value="99"},{name="y",value="52"},{name="x",value="42"}]
(gdb)
-stack-list-variables 2
^done,variables=[{name="x",type="int",value="99"},{name="y",type="int",value="52"},{name="x",type="int",value="42"}]
~~~

With this patch we obtain:

~~~
(gdb)
-stack-list-locals 0
^done,locals=[name="x",name="y",name="x"]
(gdb)
-stack-list-locals 1
^done,locals=[{name="x",value="99",file="test.c",line="4"},{name="y",value="52"},{name="x",value="42",file="test.c",line="2",Shadowed="True"}]
(gdb)
-stack-list-locals 2
^done,locals=[{name="x",type="int",value="99",file="test.c",line="4"},{name="y",type="int",value="52"},{name="x",type="int",value="42",file="test.c",line="2",Shadowed="True"}]
(gdb)
-stack-list-variables 0
^done,variables=[{name="x",file="test.c",line="4"},{name="y"},{name="x",file="test.c",line="2",Shadowed="True"}]
(gdb)
-stack-list-variables 1
^done,variables=[{name="x",value="99",file="test.c",line="4"},{name="y",value="52"},{name="x",value="42",file="test.c",line="2",Shadowed="True"}]
(gdb)
-stack-list-variables 2
^done,variables=[{name="x",type="int",value="99",file="test.c",line="4"},{name="y",type="int",value="52"},{name="x",type="int",value="42",file="test.c",line="2",Shadowed="True"}]
~~~
---
 gdb/doc/gdb.texinfo                       |  13 ++
 gdb/mi/mi-cmd-stack.c                     |  73 ++++++++++-
 gdb/testsuite/gdb.mi/mi-var-shadowing.c   |  48 ++++++++
 gdb/testsuite/gdb.mi/mi-var-shadowing.exp | 141 ++++++++++++++++++++++
 4 files changed, 271 insertions(+), 4 deletions(-)
 create mode 100644 gdb/testsuite/gdb.mi/mi-var-shadowing.c
 create mode 100644 gdb/testsuite/gdb.mi/mi-var-shadowing.exp
  

Comments

Lancelot SIX Sept. 18, 2023, 10:53 p.m. UTC | #1
Hi Abdul,

I think some of the comments I had on the previous patch also apply to
this one.

> diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
> index e473be7d465..7e4658ef415 100644
> --- a/gdb/mi/mi-cmd-stack.c
> +++ b/gdb/mi/mi-cmd-stack.c
> @@ -559,6 +575,30 @@ list_arg_or_local (const struct frame_arg *arg, enum what_to_list what,
>  	}
>        uiout->field_stream ("value", stb);
>      }
> +
> +  /* Only for C/C++/Fortran/Ada languages, in case of variables shadowing
> +     print shadowed field after the superblock variable and only location
> +     of the variables in the innerblock.  */
> +  if ((current_language->la_language == language_c
> +       || current_language->la_language == language_cplus
> +       || current_language->la_language == language_fortran
> +       || current_language->la_language == language_ada)
> +       && !(values == PRINT_NO_VALUES && what == locals)
> +       && shadowed)
> +    {
> +      std::string file_path = arg->sym->owner.symtab->filename;
> +      size_t slash_index = file_path.find_last_of ("\\/");
> +      std::string file_name = (!file_path.empty ()
> +			       ? file_path.substr (slash_index + 1)
> +			       : "NA");
> +      uiout->field_string ("file", file_name);
> +      if (arg->sym->m_line > 0)
> +	uiout->field_unsigned ("line", arg->sym->m_line);
> +      else
> +	uiout->field_string ("line", "NA");
> +      if (already_printed)
> +	uiout->field_string ("Shadowed", "True");
                              ^
I don't use MI much, but the uppercase 'S' seems odd.  For consistency,
I think it would be better left lowercase.  WDYT?

Best,
Lancelot.

> +    }
>  }
>  
>  /* Print a list of the objects for the frame FI in a certain form,
  
Terekhov, Mikhail via Gdb-patches Sept. 19, 2023, 11:41 a.m. UTC | #2
Hi Lancelot,

Thanks for the feedback, will apply the changes similar to previous patch in V4 Patch series and will use " ("shadowed", "True");"  to be consistent with other fields.

Best Regards,
Abdul Basit

-----Original Message-----
From: Lancelot SIX <lsix@lancelotsix.com> 
Sent: Tuesday, September 19, 2023 12:53 AM
To: Ijaz, Abdul B <abdul.b.ijaz@intel.com>
Cc: gdb-patches@sourceware.org; pedro@palves.net
Subject: Re: [PATCH v3 2/2] gdb: add shadowed field in '-stack-list-locals/variables' mi commands

Hi Abdul,

I think some of the comments I had on the previous patch also apply to this one.

> diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c index 
> e473be7d465..7e4658ef415 100644
> --- a/gdb/mi/mi-cmd-stack.c
> +++ b/gdb/mi/mi-cmd-stack.c
> @@ -559,6 +575,30 @@ list_arg_or_local (const struct frame_arg *arg, enum what_to_list what,
>  	}
>        uiout->field_stream ("value", stb);
>      }
> +
> +  /* Only for C/C++/Fortran/Ada languages, in case of variables shadowing
> +     print shadowed field after the superblock variable and only location
> +     of the variables in the innerblock.  */
> +  if ((current_language->la_language == language_c
> +       || current_language->la_language == language_cplus
> +       || current_language->la_language == language_fortran
> +       || current_language->la_language == language_ada)
> +       && !(values == PRINT_NO_VALUES && what == locals)
> +       && shadowed)
> +    {
> +      std::string file_path = arg->sym->owner.symtab->filename;
> +      size_t slash_index = file_path.find_last_of ("\\/");
> +      std::string file_name = (!file_path.empty ()
> +			       ? file_path.substr (slash_index + 1)
> +			       : "NA");
> +      uiout->field_string ("file", file_name);
> +      if (arg->sym->m_line > 0)
> +	uiout->field_unsigned ("line", arg->sym->m_line);
> +      else
> +	uiout->field_string ("line", "NA");
> +      if (already_printed)
> +	uiout->field_string ("Shadowed", "True");
                              ^
I don't use MI much, but the uppercase 'S' seems odd.  For consistency, I think it would be better left lowercase.  WDYT?

Best,
Lancelot.

> +    }
>  }
>  
>  /* Print a list of the objects for the frame FI in a certain form,
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
  
Lancelot SIX Sept. 19, 2023, 1:20 p.m. UTC | #3
On Tue, Sep 19, 2023 at 11:41:47AM +0000, Ijaz, Abdul B wrote:
> Hi Lancelot,
> 
> Thanks for the feedback, will apply the changes similar to previous patch in V4 Patch series and will use " ("shadowed", "True");"  to be consistent with other fields.
> 

Hi,

I was not sure if "True" or "true" should be used.  Looking around (with
`git grep "field_string" | grep -i true` show that "true" is usualy used
(see gdb/mi/mi-cmd-var.c and gdb/mi/mi-cmd-info.c).

Best,
Lancelot.
  
Terekhov, Mikhail via Gdb-patches Sept. 19, 2023, 3:46 p.m. UTC | #4
Hi Lancelot,

> I was not sure if "True" or "true" should be used.  Looking around (with `git grep "field_string" | grep -i true` show that "true" is usualy used (see gdb/mi/mi-cmd-var.c and gdb/mi/mi-cmd-info.c).

Thanks for the feedback updating to use " ("shadowed", "true");" in V4 patch series. Thanks

Best Regards
Abdul Basit

-----Original Message-----
From: Lancelot SIX <lsix@lancelotsix.com> 
Sent: Tuesday, September 19, 2023 3:20 PM
To: Ijaz, Abdul B <abdul.b.ijaz@intel.com>
Cc: gdb-patches@sourceware.org; pedro@palves.net
Subject: Re: [PATCH v3 2/2] gdb: add shadowed field in '-stack-list-locals/variables' mi commands

On Tue, Sep 19, 2023 at 11:41:47AM +0000, Ijaz, Abdul B wrote:
> Hi Lancelot,
> 
> Thanks for the feedback, will apply the changes similar to previous patch in V4 Patch series and will use " ("shadowed", "True");"  to be consistent with other fields.
> 

Hi,

I was not sure if "True" or "true" should be used.  Looking around (with `git grep "field_string" | grep -i true` show that "true" is usualy used (see gdb/mi/mi-cmd-var.c and gdb/mi/mi-cmd-info.c).

Best,
Lancelot.

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
  

Patch

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 6a22bbf2531..d281aa7099e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -34430,6 +34430,19 @@  If the @code{--skip-unavailable} option is specified, local variables
 and arguments that are not available are not listed.  Partially
 available arguments and local variables are still displayed, however.
 
+@smallexample
+1: int x = 3;
+2: @{
+3:       int x = 4; // breakpt
+4: @}
+(gdb) -stack-list-variables 2
+^done,variables=[@{name="x",type="int",value="4",file="name.c",line="3"@},@{name="x",type="int",value="3",file="name.c",line="1",Shadowed="True"@}]
+@end smallexample
+
+If a variable is shadowed, then location information is printed and for
+the outermost scope variables, the @code{Shadowed} attribute is printed
+as well.
+
 @subsubheading Example
 
 @smallexample
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index e473be7d465..7e4658ef415 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -38,6 +38,7 @@ 
 #include "gdbsupport/gdb-safe-ctype.h"
 #include "inferior.h"
 #include "observable.h"
+#include <unordered_set>
 
 enum what_to_list { locals, arguments, all };
 
@@ -491,7 +492,9 @@  mi_cmd_stack_list_variables (const char *command, const char *const *argv,
 
 static void
 list_arg_or_local (const struct frame_arg *arg, enum what_to_list what,
-		   enum print_values values, int skip_unavailable)
+		   enum print_values values, int skip_unavailable,
+		   std::unordered_set<std::string> shadowed_vars,
+		   std::unordered_set<std::string> &printed_vars)
 {
   struct ui_out *uiout = current_uiout;
 
@@ -520,6 +523,19 @@  list_arg_or_local (const struct frame_arg *arg, enum what_to_list what,
     tuple_emitter.emplace (uiout, nullptr);
 
   string_file stb;
+  const char *name = arg->sym->print_name ();
+  bool already_printed
+    = printed_vars.find (name) != printed_vars.end ();
+  printed_vars.insert (name);
+  bool shadowed = shadowed_vars.find (name) != shadowed_vars.end ();
+
+  /* In case of Rust language it is possible to declare variable with
+     same name multiple times and only latest declaration of variable
+     is accessible.  So print only the first instance and there is no
+     need of printing duplicates.  */
+  if (current_language->la_language == language_rust
+      && shadowed && already_printed)
+    return;
 
   stb.puts (arg->sym->print_name ());
   if (arg->entry_kind == print_entry_values_only)
@@ -559,6 +575,30 @@  list_arg_or_local (const struct frame_arg *arg, enum what_to_list what,
 	}
       uiout->field_stream ("value", stb);
     }
+
+  /* Only for C/C++/Fortran/Ada languages, in case of variables shadowing
+     print shadowed field after the superblock variable and only location
+     of the variables in the innerblock.  */
+  if ((current_language->la_language == language_c
+       || current_language->la_language == language_cplus
+       || current_language->la_language == language_fortran
+       || current_language->la_language == language_ada)
+       && !(values == PRINT_NO_VALUES && what == locals)
+       && shadowed)
+    {
+      std::string file_path = arg->sym->owner.symtab->filename;
+      size_t slash_index = file_path.find_last_of ("\\/");
+      std::string file_name = (!file_path.empty ()
+			       ? file_path.substr (slash_index + 1)
+			       : "NA");
+      uiout->field_string ("file", file_name);
+      if (arg->sym->m_line > 0)
+	uiout->field_unsigned ("line", arg->sym->m_line);
+      else
+	uiout->field_string ("line", "NA");
+      if (already_printed)
+	uiout->field_string ("Shadowed", "True");
+    }
 }
 
 /* Print a list of the objects for the frame FI in a certain form,
@@ -572,9 +612,10 @@  list_args_or_locals (const frame_print_options &fp_opts,
 		     enum what_to_list what, enum print_values values,
 		     frame_info_ptr fi, int skip_unavailable)
 {
-  const struct block *block;
+  const struct block *block, *orig_block;
   const char *name_of_result;
   struct ui_out *uiout = current_uiout;
+  std::unordered_set<std::string> collected_vars, shadowed_vars, printed_vars;
 
   block = get_frame_block (fi, 0);
 
@@ -595,6 +636,26 @@  list_args_or_locals (const frame_print_options &fp_opts,
 
   ui_out_emit_list list_emitter (uiout, name_of_result);
 
+
+  orig_block = block;
+  /* Stored list of shadowed variables later help in identifying them
+     from the rest.  */
+  while (block)
+    {
+      for (struct symbol *sym : block_iterator_range (block))
+	{
+	  const char *name = sym->print_name ();
+	  if (collected_vars.find (name) != collected_vars.end ())
+	    shadowed_vars.insert (name);
+	  else
+	    collected_vars.insert (name);
+	}
+	if (block->function ())
+	  break;
+	block = block->superblock ();
+    }
+
+  block = orig_block;
   while (block != 0)
     {
       for (struct symbol *sym : block_iterator_range (block))
@@ -663,9 +724,13 @@  list_args_or_locals (const frame_print_options &fp_opts,
 		}
 
 	      if (arg.entry_kind != print_entry_values_only)
-		list_arg_or_local (&arg, what, values, skip_unavailable);
+		list_arg_or_local (&arg, what, values,
+				   skip_unavailable, shadowed_vars,
+				   printed_vars);
 	      if (entryarg.entry_kind != print_entry_values_no)
-		list_arg_or_local (&entryarg, what, values, skip_unavailable);
+		list_arg_or_local (&entryarg, what, values,
+				   skip_unavailable, shadowed_vars,
+				   printed_vars);
 	    }
 	}
 
diff --git a/gdb/testsuite/gdb.mi/mi-var-shadowing.c b/gdb/testsuite/gdb.mi/mi-var-shadowing.c
new file mode 100644
index 00000000000..f2deb74d142
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-var-shadowing.c
@@ -0,0 +1,48 @@ 
+/* Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <stdlib.h>
+
+void
+shadowing ()
+{
+  int a;	/* entry bp */
+  unsigned int val1 = 1;		/* val1-d1 */
+  unsigned int val2 = 2;		/* val2-d1 */
+  a = 101;  /* bp for locals 1 */
+  {
+    unsigned int val2 = 3;		/* val2-d2 */
+    unsigned int val3 = 4;		/* val3-d1 */
+    a = 102;  /* bp for locals 2 */
+    {
+      unsigned int val1 = 5;		/* val1-d2 */
+      a = 103;  /* bp for locals 3 */
+      {
+	unsigned int val1 = 6;		/* val1-d3 */
+	unsigned int val2 = 7;		/* val2-d3 */
+	unsigned int val3 = 8;		/* val3-d2 */
+	a = 104;  /* bp for locals 4 */
+      }
+    }
+  }
+  a = 105; /* bp for locals 5 */
+}
+
+int
+main (void)
+{
+  shadowing ();
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.mi/mi-var-shadowing.exp b/gdb/testsuite/gdb.mi/mi-var-shadowing.exp
new file mode 100644
index 00000000000..40411cc553f
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-var-shadowing.exp
@@ -0,0 +1,141 @@ 
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+
+standard_testfile
+
+set opts {debug}
+if [build_executable ${testfile}.exp ${testfile} ${srcfile} $opts] {
+    return -1
+}
+
+mi_delete_breakpoints
+mi_gdb_reinitialize_dir $srcdir/$subdir
+mi_gdb_load ${binfile}
+mi_runto main
+
+set bp_line1 [gdb_get_line_number "bp for locals 1" ${srcfile}]
+set bp_line2 [gdb_get_line_number "bp for locals 2" ${srcfile}]
+set bp_line3 [gdb_get_line_number "bp for locals 3" ${srcfile}]
+set bp_line4 [gdb_get_line_number "bp for locals 4" ${srcfile}]
+set bp_line5 [gdb_get_line_number "bp for locals 5" ${srcfile}]
+
+set val1_d1 [gdb_get_line_number "val1-d1" ${srcfile}]
+set val1_d2 [gdb_get_line_number "val1-d2" ${srcfile}]
+set val1_d3 [gdb_get_line_number "val1-d3" ${srcfile}]
+set val2_d1 [gdb_get_line_number "val2-d1" ${srcfile}]
+set val2_d2 [gdb_get_line_number "val2-d2" ${srcfile}]
+set val2_d3 [gdb_get_line_number "val2-d3" ${srcfile}]
+set val3_d1 [gdb_get_line_number "val3-d1" ${srcfile}]
+set val3_d2 [gdb_get_line_number "val3-d2" ${srcfile}]
+set a_line [gdb_get_line_number "entry bp" ${srcfile}]
+
+set stack_test1_regx "\\^done,(locals|variables)=\\\[\{name=\"a\",type=\"int\",value=\"$decimal\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\"\},{name=\"val2\",type=\"unsigned int\",value=\"2\"\}\\\]"
+set stack_test2_regx "\\^done,(locals|variables)=\\\[\{name=\"val2\",type=\"unsigned int\",value=\"3\",file=\"$srcfile\",line=\"$val2_d2\"\},\{name=\"val3\",type=\"unsigned int\",value=\"4\"\},\{name=\"a\",type=\"int\",value=\"101\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\",file=\"$srcfile\",line=\"$val2_d1\",Shadowed=\"True\"\}\\\]"
+set stack_test3_regx "\\^done,(locals|variables)=\\\[\{name=\"val1\",type=\"unsigned int\",value=\"5\",file=\"$srcfile\",line=\"$val1_d2\"\},\{name=\"val2\",type=\"unsigned int\",value=\"3\",file=\"$srcfile\",line=\"$val2_d2\"\},\{name=\"val3\",type=\"unsigned int\",value=\"4\"\},\{name=\"a\",type=\"int\",value=\"102\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\",file=\"$srcfile\",line=\"$val1_d1\",Shadowed=\"True\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\",file=\"$srcfile\",line=\"$val2_d1\",Shadowed=\"True\"\}\\\]"
+set stack_test4_regx "\\^done,(locals|variables)=\\\[\{name=\"val1\",type=\"unsigned int\",value=\"6\",file=\"$srcfile\",line=\"$val1_d3\"\},\{name=\"val2\",type=\"unsigned int\",value=\"7\",file=\"$srcfile\",line=\"$val2_d3\"\},\{name=\"val3\",type=\"unsigned int\",value=\"8\",file=\"$srcfile\",line=\"$val3_d2\"\},\{name=\"val1\",type=\"unsigned int\",value=\"5\",file=\"$srcfile\",line=\"$val1_d2\",Shadowed=\"True\"\},\{name=\"val2\",type=\"unsigned int\",value=\"3\",file=\"$srcfile\",line=\"$val2_d2\",Shadowed=\"True\"\},\{name=\"val3\",type=\"unsigned int\",value=\"4\",file=\"$srcfile\",line=\"$val3_d1\",Shadowed=\"True\"\},\{name=\"a\",type=\"int\",value=\"103\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\",file=\"$srcfile\",line=\"$val1_d1\",Shadowed=\"True\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\",file=\"$srcfile\",line=\"$val2_d1\",Shadowed=\"True\"\}\\\]"
+set stack_test5_regx "\\^done,(locals|variables)=\\\[\{name=\"a\",type=\"int\",value=\"104\"\},\{name=\"val1\",type=\"unsigned int\",value=\"1\"\},\{name=\"val2\",type=\"unsigned int\",value=\"2\"\}\\\]"
+
+mi_gdb_test \
+    "-break-insert --source ${srcfile} --line ${bp_line1}"  \
+    "\\^done.*source ${srcfile} \\-line ${bp_line1}.*"  \
+    "bp at outermost level"
+mi_execute_to "exec-continue" "breakpoint-hit" ".*" ".*" ".*" "${bp_line1}"  \
+    { "" "disp=\"keep\"" } "continue to outermost level"
+mi_gdb_test "-stack-list-locals 0" \
+    "\\^done,locals=\\\[name=\"a\",name=\"val1\",name=\"val2\"\\\]" \
+    "-stack-list-locals 0 at outermost level"
+mi_gdb_test "-stack-list-variables 0" \
+    "\\^done,variables=\\\[{name=\"a\"},{name=\"val1\"},{name=\"val2\"}\\\]" \
+    "-stack-list-variables 0 at outermost level"
+mi_gdb_test "-stack-list-locals 2" "${stack_test1_regx}"  \
+    "-stack-list-locals 2 at outermost level"
+mi_gdb_test "-stack-list-variables 2" "${stack_test1_regx}"  \
+    "-stack-list-variables 2 at outermost level"
+
+mi_gdb_test  \
+    "-break-insert --source ${srcfile} --line ${bp_line2}"  \
+    "\\^done.*source ${srcfile} \\-line ${bp_line2}.*"  \
+    "bp at first level"
+mi_execute_to "exec-continue" "breakpoint-hit" ".*" ".*" ".*" "${bp_line2}"  \
+    { "" "disp=\"keep\"" } "continue to first level"
+mi_gdb_test "-stack-list-locals 0"  \
+    "\\^done,locals=\\\[name=\"val2\",name=\"val3\",name=\"a\",name=\"val1\",name=\"val2\"\\\]"  \
+    "-stack-list-locals 0 at first level"
+mi_gdb_test "-stack-list-variables 0" \
+    "\\^done,variables=\\\[{name=\"val2\",file=\"$srcfile\",line=\"$val2_d2\"},{name=\"val3\"},{name=\"a\"},{name=\"val1\"},{name=\"val2\",file=\"$srcfile\",line=\"$val2_d1\",Shadowed=\"True\"\}\\\]"  \
+    "-stack-list-variables 0 at first level"
+mi_gdb_test "-stack-list-locals 2" "${stack_test2_regx}"  \
+    "-stack-list-locals 2 at first level"
+mi_gdb_test "-stack-list-variables 2" "${stack_test2_regx}"  \
+    "-stack-list-variables 2 at first level"
+
+mi_gdb_test  \
+    "-break-insert --source ${srcfile} --line ${bp_line3}"  \
+    "\\^done.*source ${srcfile} \\-line ${bp_line3}.*"  \
+    "bp at second level"
+mi_execute_to "exec-continue" "breakpoint-hit" ".*" ".*" ".*" "${bp_line3}"  \
+    { "" "disp=\"keep\"" } "continue to second level"
+mi_gdb_test "-stack-list-locals 0"  \
+     "\\^done,locals=\\\[name=\"val1\",name=\"val2\",name=\"val3\",name=\"a\",name=\"val1\",name=\"val2\"\\\]"  \
+    "-stack-list-locals 0 at second level"
+mi_gdb_test "-stack-list-variables 0"  \
+     "\\^done,variables=\\\[{name=\"val1\",file=\"$srcfile\",line=\"$val1_d2\"},{name=\"val2\",file=\"$srcfile\",line=\"$val2_d2\"},{name=\"val3\"},{name=\"a\"},{name=\"val1\",file=\"$srcfile\",line=\"$val1_d1\",Shadowed=\"True\"\},{name=\"val2\",file=\"$srcfile\",line=\"$val2_d1\",Shadowed=\"True\"\}\\\]"  \
+    "-stack-list-variables 0 at second level"
+mi_gdb_test "-stack-list-locals 2" "${stack_test3_regx}"  \
+     "-stack-list-locals 2 at second level"
+mi_gdb_test "-stack-list-variables 2" "${stack_test3_regx}"  \
+     "-stack-list-variables 2 at second level"
+
+mi_gdb_test  \
+    "-break-insert --source ${srcfile} --line ${bp_line4}"  \
+    "\\^done.*source ${srcfile} \\-line ${bp_line4}.*"  \
+    "bp at third level"
+mi_execute_to "exec-continue" "breakpoint-hit" ".*" ".*" ".*" "${bp_line4}"  \
+    { "" "disp=\"keep\"" } "continue to third level"
+mi_gdb_test "-stack-list-locals 0"  \
+    "\\^done,locals=\\\[name=\"val1\",name=\"val2\",name=\"val3\",name=\"val1\",name=\"val2\",name=\"val3\",name=\"a\",name=\"val1\",name=\"val2\"\\\]"  \
+    "-stack-list-locals 0 at third level"
+mi_gdb_test "-stack-list-variables 0" \
+    "\\^done,variables=\\\[{name=\"val1\",file=\"$srcfile\",line=\"$val1_d3\"},{name=\"val2\",file=\"$srcfile\",line=\"$val2_d3\"},{name=\"val3\",file=\"$srcfile\",line=\"$val3_d2\"},{name=\"val1\",file=\"$srcfile\",line=\"$val1_d2\",Shadowed=\"True\"\},\{name=\"val2\",file=\"$srcfile\",line=\"$val2_d2\",Shadowed=\"True\"\},\{name=\"val3\",file=\"$srcfile\",line=\"$val3_d1\",Shadowed=\"True\"\},{name=\"a\"},{name=\"val1\",file=\"$srcfile\",line=\"$val1_d1\",Shadowed=\"True\"\},{name=\"val2\",file=\"$srcfile\",line=\"$val2_d1\",Shadowed=\"True\"\}\\\]"  \
+    "-stack-list-variables 0 at third level"
+mi_gdb_test "-stack-list-locals 2" "${stack_test4_regx}"  \
+    "-stack-list-locals 2 at third level"
+mi_gdb_test "-stack-list-variables 2" "${stack_test4_regx}"  \
+    "-stack-list-variables 2 at third level"
+
+mi_gdb_test  \
+    "-break-insert --source ${srcfile} --line ${bp_line5}"  \
+    "\\^done.*source ${srcfile} \\-line ${bp_line5}.*"  \
+    "bp at outermost level last"
+mi_execute_to "exec-continue" "breakpoint-hit" ".*" ".*" ".*" "${bp_line5}"  \
+    { "" "disp=\"keep\"" } "continue to outermost level last"
+mi_gdb_test "-stack-list-locals 0"  \
+    "\\^done,locals=\\\[name=\"a\",name=\"val1\",name=\"val2\"\\\]"  \
+    "-stack-list-locals 0 at outermost level last"
+mi_gdb_test "-stack-list-variables 0"  \
+    "\\^done,variables=\\\[{name=\"a\"},{name=\"val1\"},{name=\"val2\"}\\\]"  \
+    "-stack-list-variables at outermost level last"
+mi_gdb_test "-stack-list-locals 2" "${stack_test5_regx}"  \
+    "-stack-list-locals 2 at outermost level last"
+mi_gdb_test "-stack-list-variables 2" "${stack_test5_regx}"  \
+    "-stack-list-variables 2 at outermost level last"