diff mbox

[2/9] gdb: Select a frame for frame_info.

Message ID 2639f4db719cdba6f50d622e58194bca9840687e.1441996064.git.andrew.burgess@embecosm.com
State New
Headers show

Commit Message

Andrew Burgess Sept. 11, 2015, 6:49 p.m. UTC
Within the 'info frame' command (implemented in the frame_info function)
gdb will create register values, which are then displayed to the user.
These register values will be created as lazy value.  As part of
fetching the value of a lazy register value we must find the frame in
which the value was created based on the frame id, which is stored in
the value, this is done with a call to frame_find_by_id.

In a previous commit I already addressed the problem that
frame_find_by_id did not used to consider the selected frame when trying
to find a frame.  This is now fixed.

However, if in frame_info the user passed a frame specification, then it
is possible that a brand new frame will have been created, a frame
outside of the current backtrace, and also not the currently selected
frame.  So, usually, something like, 'info frame 4' will cause gdb to
crash with an assertion failure.  This can be especially annoying if the
user accidentally types 'info frame 4' intending to ask about the fourth
stack frame, but, there happens to only be 3 stack frames at present.

The solution I propose here is that frame_info should temporarily select
the frame identified by the user.  The previously selected frame will be
restored at the end of the function.

gdb/ChangeLog:

	* frame.h (make_restore_selected_frame_cleanup): Declare new
	function.
	* frame.c (do_restore_selected_frame): New function.
	(make_restore_selected_frame_cleanup): Define new function.
	* stack.c (frame_info): Temporarily select frame for duration of
	this function.

gdb/testsuite/ChangeLog:

	* gdb.base/create-frame.exp: Add test for 'info frame' with
	specific address.
---
 gdb/ChangeLog                         |  9 +++++++++
 gdb/frame.c                           | 21 +++++++++++++++++++++
 gdb/frame.h                           |  4 ++++
 gdb/stack.c                           | 10 ++++++++++
 gdb/testsuite/ChangeLog               |  5 +++++
 gdb/testsuite/gdb.base/frame-cmds.exp |  8 +++++++-
 6 files changed, 56 insertions(+), 1 deletion(-)
diff mbox

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index b4fe9cc..79952c7 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,14 @@ 
 2015-09-11  Andrew Burgess  <andrew.burgess@embecosm.com>
 
+	* frame.h (make_restore_selected_frame_cleanup): Declare new
+	function.
+	* frame.c (do_restore_selected_frame): New function.
+	(make_restore_selected_frame_cleanup): Define new function.
+	* stack.c (frame_info): Temporarily select frame for duration of
+	this function.
+
+2015-09-11  Andrew Burgess  <andrew.burgess@embecosm.com>
+
 	* frame.c (frame_find_by_id): Also check the selected frame.
 
 2015-09-11  Pierre Langlois  <pierre.langlois@arm.com>
diff --git a/gdb/frame.c b/gdb/frame.c
index 114fa6a..f964fd8 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -2753,6 +2753,27 @@  frame_prepare_for_sniffer (struct frame_info *frame,
   return make_cleanup (frame_cleanup_after_sniffer, frame);
 }
 
+/* Used as a clean-up to restore the selected frame.  */
+
+static void
+do_restore_selected_frame (void *arg)
+{
+  struct frame_info *fi = (struct frame_info *) arg;
+
+  select_frame (fi);
+}
+
+/* See frame.h.  */
+
+struct cleanup *
+make_restore_selected_frame_cleanup (void)
+{
+  struct frame_info *fi;
+
+  fi = get_selected_frame_if_set ();
+  return make_cleanup (do_restore_selected_frame, fi);
+}
+
 extern initialize_file_ftype _initialize_frame; /* -Wmissing-prototypes */
 
 static struct cmd_list_element *set_backtrace_cmdlist;
diff --git a/gdb/frame.h b/gdb/frame.h
index 03f3892..c7c90cf 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -820,5 +820,9 @@  extern int frame_unwinder_is (struct frame_info *fi,
 
 extern enum language get_frame_language (struct frame_info *frame);
 
+/* Create a clean-up to restore the selected frame.  This includes
+   restoring the selected frame to NULL if that is its current value.  */
+
+extern struct cleanup * make_restore_selected_frame_cleanup (void);
 
 #endif /* !defined (FRAME_H)  */
diff --git a/gdb/stack.c b/gdb/stack.c
index 7d37dd1..82b366d 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -1434,6 +1434,16 @@  frame_info (char *addr_exp, int from_tty)
   fi = parse_frame_specification_1 (addr_exp, "No stack.", &selected_frame_p);
   gdbarch = get_frame_arch (fi);
 
+  /* During the following value will be created and then displayed.
+     Displaying a value can require using frame_find_by_id, so we must make
+     sure that the frame FI is findable using frame_find_by_id.  As it is
+     possible that the frame specification caused a new frame to be
+     created, one outside of the current stack trace, and different to the
+     currently selected frame, then the only way to ensure that we can find
+     this frame again later is if we temporarily select this new frame.  */
+  make_restore_selected_frame_cleanup ();
+  select_frame (fi);
+
   /* Name of the value returned by get_frame_pc().  Per comments, "pc"
      is not a good name.  */
   if (gdbarch_pc_regnum (gdbarch) >= 0)
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index cf83b3a..ee0ad76 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,10 @@ 
 2015-09-11  Andrew Burgess  <andrew.burgess@embecosm.com>
 
+	* gdb.base/create-frame.exp: Add test for 'info frame' with
+	specific address.
+
+2015-09-11  Andrew Burgess  <andrew.burgess@embecosm.com>
+
 	* gdb.mi/mi-var-frame.c: New file.
 	* gdb.mi/mi-var-frame.exp: New file.
 	* gdb.base/frame-cmds.c: New file.
diff --git a/gdb/testsuite/gdb.base/frame-cmds.exp b/gdb/testsuite/gdb.base/frame-cmds.exp
index 2518463..e285a4e 100644
--- a/gdb/testsuite/gdb.base/frame-cmds.exp
+++ b/gdb/testsuite/gdb.base/frame-cmds.exp
@@ -27,4 +27,10 @@  gdb_test "bt" "#0.*#1.*#2.*" "backtrace at breakpoint"
 gdb_test_no_output "select-frame 3"
 
 gdb_test "info frame" "Stack level 0, frame at 0x3:.*" \
-    "info frame for created frame"
+    "info frame for currently selected frame, at 0x3"
+
+gdb_test "info frame 4" "Stack frame at 0x4:.*" \
+    "info frame for specific frame, created at 0x4"
+
+gdb_test "info frame" "Stack level 0, frame at 0x3:.*" \
+    "info frame for currently selected frame (again), at 0x3"