gdb: Restore selected frame in print_frame_local_vars

PR gdb/23203 reports 'bt full' causing the currently selected frame to
change, this issue is fixed in this commit.

Add a new class scoped_restore_selected_frame that saves and restores
the selected frame.  Make use of this in print_frame_local_vars to
restore the selected frame on exit.

gdb/ChangeLog:

	PR gdb/23203
	* frame.c
	(scoped_restore_selected_frame::scoped_restore_selected_frame):
	Define.
	(scoped_restore_selected_frame::~scoped_restore_selected_frame):
	Define.
	* frame.h (class scoped_restore_selected_frame): New class.
	* stack.c (print_frame_local_vars): Remove catching and rethrowing
	of any exception, use scoped_restore_selected_frame to restore the
	frame instead.

gdb/testsuite/ChangeLog:

	PR gdb/23203
	* gdb.base/bt-selected-frame.c: New file.
	* gdb.base/bt-selected-frame.exp: New file.
	* lib/gdb.exp (get_current_frame_number): New function.
This commit is contained in:
Andrew Burgess 2018-05-18 17:45:16 +01:00
parent d9f6d7f8b6
commit 45f25d6c83
8 changed files with 182 additions and 20 deletions

View File

@ -1,3 +1,16 @@
2018-05-24 Andrew Burgess <andrew.burgess@embecosm.com>
PR gdb/23203
* frame.c
(scoped_restore_selected_frame::scoped_restore_selected_frame):
Define.
(scoped_restore_selected_frame::~scoped_restore_selected_frame):
Define.
* frame.h (class scoped_restore_selected_frame): New class.
* stack.c (print_frame_local_vars): Remove catching and rethrowing
of any exception, use scoped_restore_selected_frame to restore the
frame instead.
2018-05-24 Pedro Alves <palves@redhat.com> 2018-05-24 Pedro Alves <palves@redhat.com>
* darwin-nat.h (darwin_nat_target::create_inferior): Mark with * darwin-nat.h (darwin_nat_target::create_inferior): Mark with

View File

@ -269,6 +269,22 @@ frame_stash_invalidate (void)
htab_empty (frame_stash); htab_empty (frame_stash);
} }
/* See frame.h */
scoped_restore_selected_frame::scoped_restore_selected_frame ()
{
m_fid = get_frame_id (get_selected_frame (NULL));
}
/* See frame.h */
scoped_restore_selected_frame::~scoped_restore_selected_frame ()
{
frame_info *frame = frame_find_by_id (m_fid);
if (frame == NULL)
warning (_("Unable to restore previously selected frame."));
else
select_frame (frame);
}
/* Flag to control debugging. */ /* Flag to control debugging. */
unsigned int frame_debug; unsigned int frame_debug;

View File

@ -164,6 +164,25 @@ struct frame_id
int artificial_depth; int artificial_depth;
}; };
/* Save and restore the currently selected frame. */
class scoped_restore_selected_frame
{
public:
/* Save the currently selected frame. */
scoped_restore_selected_frame ();
/* Restore the currently selected frame. */
~scoped_restore_selected_frame ();
DISABLE_COPY_AND_ASSIGN (scoped_restore_selected_frame);
private:
/* The ID of the previously selected frame. */
struct frame_id m_fid;
};
/* Methods for constructing and comparing Frame IDs. */ /* Methods for constructing and comparing Frame IDs. */
/* For convenience. All fields are zero. This means "there is no frame". */ /* For convenience. All fields are zero. This means "there is no frame". */

View File

@ -2018,7 +2018,6 @@ print_frame_local_vars (struct frame_info *frame, int num_tabs,
struct print_variable_and_value_data cb_data; struct print_variable_and_value_data cb_data;
const struct block *block; const struct block *block;
CORE_ADDR pc; CORE_ADDR pc;
struct gdb_exception except = exception_none;
if (!get_frame_pc_if_available (frame, &pc)) if (!get_frame_pc_if_available (frame, &pc))
{ {
@ -2042,27 +2041,12 @@ print_frame_local_vars (struct frame_info *frame, int num_tabs,
/* Temporarily change the selected frame to the given FRAME. /* Temporarily change the selected frame to the given FRAME.
This allows routines that rely on the selected frame instead This allows routines that rely on the selected frame instead
of being given a frame as parameter to use the correct frame. */ of being given a frame as parameter to use the correct frame. */
scoped_restore_selected_frame restore_selected_frame;
select_frame (frame); select_frame (frame);
TRY iterate_over_block_local_vars (block,
{ do_print_variable_and_value,
iterate_over_block_local_vars (block, &cb_data);
do_print_variable_and_value,
&cb_data);
}
CATCH (ex, RETURN_MASK_ALL)
{
except = ex;
}
END_CATCH
/* Restore the selected frame, and then rethrow if there was a problem. */
select_frame (frame_find_by_id (cb_data.frame_id));
if (except.reason < 0)
throw_exception (except);
/* do_print_variable_and_value invalidates FRAME. */
frame = NULL;
if (!cb_data.values_printed) if (!cb_data.values_printed)
fprintf_filtered (stream, _("No locals.\n")); fprintf_filtered (stream, _("No locals.\n"));

View File

@ -1,3 +1,10 @@
2018-05-24 Andrew Burgess <andrew.burgess@embecosm.com>
PR gdb/23203
* gdb.base/bt-selected-frame.c: New file.
* gdb.base/bt-selected-frame.exp: New file.
* lib/gdb.exp (get_current_frame_number): New function.
2018-05-24 Maciej W. Rozycki <macro@mips.com> 2018-05-24 Maciej W. Rozycki <macro@mips.com>
Pedro Alves <palves@redhat.com> Pedro Alves <palves@redhat.com>

View File

@ -0,0 +1,35 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2018 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/>. */
void
breakpt ()
{
asm ("" ::: "memory");
}
void
func ()
{
breakpt ();
}
int
main ()
{
func ();
return 0;
}

View File

@ -0,0 +1,72 @@
# Copyright 2018 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/>.
# Check that the selected stack frame doesn't change after a backtrace.
standard_testfile
if { [prepare_for_testing "failed to prepare" $testfile $srcfile debug] } {
return -1
}
proc check_selected_frame_after_bt { bt_cmd stack_pattern } {
global binfile
clean_restart $binfile
with_test_prefix $bt_cmd {
if ![runto_main] then {
fail "can't run to main"
return 0
}
gdb_breakpoint "breakpt"
gdb_continue_to_breakpoint "breakpt"
# Visit each frame in the stack and ensure that the selected
# frame doesn't change after executing the backtrace command
# in BT_CMD.
foreach_with_prefix frame {0 1 2} {
if { $frame == 0 } {
gdb_assert {[get_current_frame_number] == $frame} \
"check frame"
} else {
gdb_test "frame $frame" "#$frame .*" "select frame"
}
gdb_test "$bt_cmd" $stack_pattern \
"perform backtrace command"
gdb_assert {[get_current_frame_number] == $frame} \
"check frame is still selected"
}
}
}
check_selected_frame_after_bt "bt" \
[multi_line \
"#0 \[^\n\r\]+" \
"#1 \[^\n\r\]+" \
"#2 \[^\n\r\]+"]
check_selected_frame_after_bt "bt full" \
[multi_line \
"#0 \[^\n\r\]+" \
"No locals\." \
"#1 \[^\n\r\]+" \
"No locals\." \
"#2 \[^\n\r\]+" \
"No locals\."]

View File

@ -5805,6 +5805,22 @@ proc get_var_address { var } {
return "" return ""
} }
# Return the frame number for the currently selected frame
proc get_current_frame_number {{test_name ""}} {
global gdb_prompt
if { $test_name == "" } {
set test_name "get current frame number"
}
set frame_num -1
gdb_test_multiple "frame" $test_name {
-re "#(\[0-9\]+) .*$gdb_prompt $" {
set frame_num $expect_out(1,string)
}
}
return $frame_num
}
# Get the current value for remotetimeout and return it. # Get the current value for remotetimeout and return it.
proc get_remotetimeout { } { proc get_remotetimeout { } {
global gdb_prompt global gdb_prompt