/* Cache and manage frames for GDB, the GNU debugger.
- Copyright (C) 1986-2020 Free Software Foundation, Inc.
+ Copyright (C) 1986-2021 Free Software Foundation, Inc.
This file is part of GDB.
/* See frame.h */
scoped_restore_selected_frame::scoped_restore_selected_frame ()
{
- m_fid = get_frame_id (get_selected_frame (NULL));
+ m_lang = current_language->la_language;
+ save_selected_frame (&m_fid, &m_level);
}
/* 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);
+ restore_selected_frame (m_fid, m_level);
+ set_language (m_lang);
}
/* Flag to control debugging. */
value);
}
+/* See frame.h. */
-static void
-fprint_field (struct ui_file *file, const char *name, int p, CORE_ADDR addr)
+std::string
+frame_id::to_string () const
{
- if (p)
- fprintf_unfiltered (file, "%s=%s", name, hex_string (addr));
- else
- fprintf_unfiltered (file, "!%s", name);
-}
+ const struct frame_id &id = *this;
-void
-fprint_frame_id (struct ui_file *file, struct frame_id id)
-{
- fprintf_unfiltered (file, "{");
+ std::string res = "{";
if (id.stack_status == FID_STACK_INVALID)
- fprintf_unfiltered (file, "!stack");
+ res += "!stack";
else if (id.stack_status == FID_STACK_UNAVAILABLE)
- fprintf_unfiltered (file, "stack=<unavailable>");
+ res += "stack=<unavailable>";
else if (id.stack_status == FID_STACK_SENTINEL)
- fprintf_unfiltered (file, "stack=<sentinel>");
+ res += "stack=<sentinel>";
else if (id.stack_status == FID_STACK_OUTER)
- fprintf_unfiltered (file, "stack=<outer>");
+ res += "stack=<outer>";
else
- fprintf_unfiltered (file, "stack=%s", hex_string (id.stack_addr));
-
- fprintf_unfiltered (file, ",");
+ res += std::string ("stack=") + hex_string (id.stack_addr);
- fprint_field (file, "code", id.code_addr_p, id.code_addr);
- fprintf_unfiltered (file, ",");
+ /* Helper function to format 'N=A' if P is true, otherwise '!N'. */
+ auto field_to_string = [] (const char *n, bool p, CORE_ADDR a) -> std::string
+ {
+ if (p)
+ return std::string (n) + "=" + core_addr_to_string (a);
+ else
+ return std::string ("!") + std::string (n);
+ };
- fprint_field (file, "special", id.special_addr_p, id.special_addr);
+ res += (std::string (",")
+ + field_to_string ("code", id.code_addr_p, id.code_addr)
+ + std::string (",")
+ + field_to_string ("special", id.special_addr_p, id.special_addr));
if (id.artificial_depth)
- fprintf_unfiltered (file, ",artificial=%d", id.artificial_depth);
-
- fprintf_unfiltered (file, "}");
+ res += ",artificial=" + std::to_string (id.artificial_depth);
+ res += "}";
+ return res;
}
static void
else if (fi->this_id.p == frame_id_status::COMPUTING)
fprintf_unfiltered (file, "<computing>");
else
- fprint_frame_id (file, fi->this_id.value);
+ fprintf_unfiltered (file, "%s", fi->this_id.value.to_string ().c_str ());
fprintf_unfiltered (file, ",");
fprintf_unfiltered (file, "func=");
fi->this_id.p = frame_id_status::COMPUTED;
if (frame_debug)
- {
- fprintf_unfiltered (gdb_stdlog, "-> ");
- fprint_frame_id (gdb_stdlog, fi->this_id.value);
- fprintf_unfiltered (gdb_stdlog, " }\n");
- }
+ fprintf_unfiltered (gdb_stdlog, "-> %s }\n",
+ fi->this_id.value.to_string ().c_str ());
}
catch (const gdb_exception &ex)
{
/* On error, revert the frame id status to not computed. If the frame
- cache generation changed, the frame object doesn't exist anymore, so
+ cache generation changed, the frame object doesn't exist anymore, so
don't touch it. */
if (get_frame_cache_generation () == entry_generation)
fi->this_id.p = frame_id_status::NOT_COMPUTED;
struct frame_id
frame_id_build_special (CORE_ADDR stack_addr, CORE_ADDR code_addr,
- CORE_ADDR special_addr)
+ CORE_ADDR special_addr)
{
struct frame_id id = null_frame_id;
bool p = l.stack_status != FID_STACK_INVALID;
if (frame_debug)
- {
- fprintf_unfiltered (gdb_stdlog, "{ frame_id_p (l=");
- fprint_frame_id (gdb_stdlog, l);
- fprintf_unfiltered (gdb_stdlog, ") -> %d }\n", p);
- }
+ fprintf_unfiltered (gdb_stdlog, "{ frame_id_p (l=%s) -> %d }\n",
+ l.to_string ().c_str (), p);
return p;
}
eq = true;
if (frame_debug)
- {
- fprintf_unfiltered (gdb_stdlog, "{ frame_id_eq (l=");
- fprint_frame_id (gdb_stdlog, l);
- fprintf_unfiltered (gdb_stdlog, ",r=");
- fprint_frame_id (gdb_stdlog, r);
- fprintf_unfiltered (gdb_stdlog, ") -> %d }\n", eq);
- }
+ fprintf_unfiltered (gdb_stdlog, "{ frame_id_eq (l=%s,r=%s) -> %d }\n",
+ l.to_string ().c_str (), r.to_string ().c_str (), eq);
return eq;
}
inner = gdbarch_inner_than (gdbarch, l.stack_addr, r.stack_addr);
if (frame_debug)
- {
- fprintf_unfiltered (gdb_stdlog, "{ frame_id_inner (l=");
- fprint_frame_id (gdb_stdlog, l);
- fprintf_unfiltered (gdb_stdlog, ",r=");
- fprint_frame_id (gdb_stdlog, r);
- fprintf_unfiltered (gdb_stdlog, ") -> %d }\n", inner);
- }
+ fprintf_unfiltered (gdb_stdlog, "{ frame_id_inner (l=%s,r=%s) -> %d }\n",
+ l.to_string ().c_str (), r.to_string ().c_str (),
+ inner);
return inner;
}
CORE_ADDR addr_in_block;
/* Make certain that this, and not the adjacent, function is
- found. */
+ found. */
if (!get_frame_address_in_block_if_available (this_frame, &addr_in_block))
{
next_frame->prev_func.status = CC_UNAVAILABLE;
bool
get_frame_register_bytes (frame_info *frame, int regnum,
- CORE_ADDR offset, int len, gdb_byte *myaddr,
+ CORE_ADDR offset,
+ gdb::array_view<gdb_byte> buffer,
int *optimizedp, int *unavailablep)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
break; /* This register is not available on this architecture. */
maxsize += thissize;
}
+
+ int len = buffer.size ();
if (len > maxsize)
error (_("Bad debug information detected: "
"Attempt to read %d bytes from registers."), len);
if (curr_len > len)
curr_len = len;
+ gdb_byte *myaddr = buffer.data ();
+
if (curr_len == register_size (gdbarch, regnum))
{
enum lval_type lval;
void
put_frame_register_bytes (struct frame_info *frame, int regnum,
- CORE_ADDR offset, int len, const gdb_byte *myaddr)
+ CORE_ADDR offset,
+ gdb::array_view<const gdb_byte> buffer)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
regnum++;
}
+ int len = buffer.size ();
/* Copy the data. */
while (len > 0)
{
if (curr_len > len)
curr_len = len;
+ const gdb_byte *myaddr = buffer.data ();
if (curr_len == register_size (gdbarch, regnum))
{
put_frame_register (frame, regnum, myaddr);
}
/* The "selected" stack frame is used by default for local and arg
- access. May be zero, for no selected frame. */
-
+ access.
+
+ The "single source of truth" for the selected frame is the
+ SELECTED_FRAME_ID / SELECTED_FRAME_LEVEL pair.
+
+ Frame IDs can be saved/restored across reinitializing the frame
+ cache, while frame_info pointers can't (frame_info objects are
+ invalidated). If we know the corresponding frame_info object, it
+ is cached in SELECTED_FRAME.
+
+ If SELECTED_FRAME_ID / SELECTED_FRAME_LEVEL are null_frame_id / -1,
+ and the target has stack and is stopped, the selected frame is the
+ current (innermost) frame. This means that SELECTED_FRAME_LEVEL is
+ never 0 and SELECTED_FRAME_ID is never the ID of the innermost
+ frame.
+
+ If SELECTED_FRAME_ID / SELECTED_FRAME_LEVEL are null_frame_id / -1,
+ and the target has no stack or is executing, then there's no
+ selected frame. */
+static frame_id selected_frame_id = null_frame_id;
+static int selected_frame_level = -1;
+
+/* The cached frame_info object pointing to the selected frame.
+ Looked up on demand by get_selected_frame. */
static struct frame_info *selected_frame;
+/* See frame.h. */
+
+void
+save_selected_frame (frame_id *frame_id, int *frame_level)
+ noexcept
+{
+ *frame_id = selected_frame_id;
+ *frame_level = selected_frame_level;
+}
+
+/* See frame.h. */
+
+void
+restore_selected_frame (frame_id frame_id, int frame_level)
+ noexcept
+{
+ /* save_selected_frame never returns level == 0, so we shouldn't see
+ it here either. */
+ gdb_assert (frame_level != 0);
+
+ /* FRAME_ID can be null_frame_id only IFF frame_level is -1. */
+ gdb_assert ((frame_level == -1 && !frame_id_p (frame_id))
+ || (frame_level != -1 && frame_id_p (frame_id)));
+
+ selected_frame_id = frame_id;
+ selected_frame_level = frame_level;
+
+ /* Will be looked up later by get_selected_frame. */
+ selected_frame = nullptr;
+}
+
+/* See frame.h. */
+
+void
+lookup_selected_frame (struct frame_id a_frame_id, int frame_level)
+{
+ struct frame_info *frame = NULL;
+ int count;
+
+ /* This either means there was no selected frame, or the selected
+ frame was the current frame. In either case, select the current
+ frame. */
+ if (frame_level == -1)
+ {
+ select_frame (get_current_frame ());
+ return;
+ }
+
+ /* select_frame never saves 0 in SELECTED_FRAME_LEVEL, so we
+ shouldn't see it here. */
+ gdb_assert (frame_level > 0);
+
+ /* Restore by level first, check if the frame id is the same as
+ expected. If that fails, try restoring by frame id. If that
+ fails, nothing to do, just warn the user. */
+
+ count = frame_level;
+ frame = find_relative_frame (get_current_frame (), &count);
+ if (count == 0
+ && frame != NULL
+ /* The frame ids must match - either both valid or both
+ outer_frame_id. The latter case is not failsafe, but since
+ it's highly unlikely the search by level finds the wrong
+ frame, it's 99.9(9)% of the time (for all practical purposes)
+ safe. */
+ && frame_id_eq (get_frame_id (frame), a_frame_id))
+ {
+ /* Cool, all is fine. */
+ select_frame (frame);
+ return;
+ }
+
+ frame = frame_find_by_id (a_frame_id);
+ if (frame != NULL)
+ {
+ /* Cool, refound it. */
+ select_frame (frame);
+ return;
+ }
+
+ /* Nothing else to do, the frame layout really changed. Select the
+ innermost stack frame. */
+ select_frame (get_current_frame ());
+
+ /* Warn the user. */
+ if (frame_level > 0 && !current_uiout->is_mi_like_p ())
+ {
+ warning (_("Couldn't restore frame #%d in "
+ "current thread. Bottom (innermost) frame selected:"),
+ frame_level);
+ /* For MI, we should probably have a notification about current
+ frame change. But this error is not very likely, so don't
+ bother for now. */
+ print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+ }
+}
+
bool
has_stack_frames ()
{
return true;
}
-/* Return the selected frame. Always non-NULL (unless there isn't an
- inferior sufficient for creating a frame) in which case an error is
- thrown. */
+/* See frame.h. */
struct frame_info *
get_selected_frame (const char *message)
{
if (message != NULL && !has_stack_frames ())
error (("%s"), message);
- /* Hey! Don't trust this. It should really be re-finding the
- last selected frame of the currently selected thread. This,
- though, is better than nothing. */
- select_frame (get_current_frame ());
+
+ lookup_selected_frame (selected_frame_id, selected_frame_level);
}
/* There is always a frame. */
gdb_assert (selected_frame != NULL);
return selected_frame;
}
-/* If there is a selected frame, return it. Otherwise, return NULL. */
-
-struct frame_info *
-get_selected_frame_if_set (void)
-{
- return selected_frame;
-}
-
/* This is a variant of get_selected_frame() which can be called when
the inferior does not have a frame; in that case it will return
NULL instead of calling error(). */
return get_selected_frame (NULL);
}
-/* Select frame FI (or NULL - to invalidate the current frame). */
+/* Select frame FI (or NULL - to invalidate the selected frame). */
void
select_frame (struct frame_info *fi)
{
selected_frame = fi;
+ selected_frame_level = frame_relative_level (fi);
+ if (selected_frame_level == 0)
+ {
+ /* Treat the current frame especially -- we want to always
+ save/restore it without warning, even if the frame ID changes
+ (see lookup_selected_frame). E.g.:
+
+ // The current frame is selected, the target had just stopped.
+ {
+ scoped_restore_selected_frame restore_frame;
+ some_operation_that_changes_the_stack ();
+ }
+ // scoped_restore_selected_frame's dtor runs, but the
+ // original frame_id can't be found. No matter whether it
+ // is found or not, we still end up with the now-current
+ // frame selected. Warning in lookup_selected_frame in this
+ // case seems pointless.
+
+ Also get_frame_id may access the target's registers/memory,
+ and thus skipping get_frame_id optimizes the common case.
+
+ Saving the selected frame this way makes get_selected_frame
+ and restore_current_frame return/re-select whatever frame is
+ the innermost (current) then. */
+ selected_frame_level = -1;
+ selected_frame_id = null_frame_id;
+ }
+ else
+ selected_frame_id = get_frame_id (fi);
+
/* NOTE: cagney/2002-05-04: FI can be NULL. This occurs when the
frame is being invalidated. */
if (get_frame_type (this_frame) == INLINE_FRAME)
return get_prev_frame_if_no_cycle (this_frame);
+ /* If this_frame is the current frame, then compute and stash its
+ frame id prior to fetching and computing the frame id of the
+ previous frame. Otherwise, the cycle detection code in
+ get_prev_frame_if_no_cycle() will not work correctly. When
+ get_frame_id() is called later on, an assertion error will be
+ triggered in the event of a cycle between the current frame and
+ its previous frame.
+
+ Note we do this after the INLINE_FRAME check above. That is
+ because the inline frame's frame id computation needs to fetch
+ the frame id of its previous real stack frame. I.e., we need to
+ avoid recursion in that case. This is OK since we're sure the
+ inline frame won't create a cycle with the real stack frame. See
+ inline_frame_this_id. */
+ if (this_frame->level == 0)
+ get_frame_id (this_frame);
+
/* Check that this frame is unwindable. If it isn't, don't try to
unwind to the prev frame. */
this_frame->stop_reason
CORE_ADDR this_pc_in_block;
struct minimal_symbol *morestack_msym;
const char *morestack_name = NULL;
-
+
/* gcc -fsplit-stack __morestack can continue the stack anywhere. */
this_pc_in_block = get_frame_address_in_block (this_frame);
morestack_msym = lookup_minimal_symbol_by_pc (this_pc_in_block).minsym;
size_t size;
/* The error needs to live as long as the frame does.
- Allocate using stack local STOP_STRING then assign the
- pointer to the frame, this allows the STOP_STRING on the
- frame to be of type 'const char *'. */
+ Allocate using stack local STOP_STRING then assign the
+ pointer to the frame, this allows the STOP_STRING on the
+ frame to be of type 'const char *'. */
size = ex.message->size () + 1;
stop_string = (char *) frame_obstack_zalloc (size);
memcpy (stop_string, ex.what (), size);
static bool
inside_main_func (frame_info *this_frame)
{
- if (symfile_objfile == nullptr)
+ if (current_program_space->symfile_object_file == nullptr)
return false;
CORE_ADDR sym_addr;
const char *name = main_name ();
bound_minimal_symbol msymbol
- = lookup_minimal_symbol (name, NULL, symfile_objfile);
+ = lookup_minimal_symbol (name, NULL,
+ current_program_space->symfile_object_file);
if (msymbol.minsym == nullptr)
{
/* In some language (for example Fortran) there will be no minimal
/* Convert any function descriptor addresses into the actual function
code address. */
- sym_addr
- = gdbarch_convert_from_func_ptr_addr (get_frame_arch (this_frame),
- sym_addr, current_top_target ());
+ sym_addr = gdbarch_convert_from_func_ptr_addr
+ (get_frame_arch (this_frame), sym_addr, current_inferior ()->top_target ());
return sym_addr == get_frame_func (this_frame);
}
something should be calling get_selected_frame() or
get_current_frame(). */
gdb_assert (this_frame != NULL);
-
- /* If this_frame is the current frame, then compute and stash
- its frame id prior to fetching and computing the frame id of the
- previous frame. Otherwise, the cycle detection code in
- get_prev_frame_if_no_cycle() will not work correctly. When
- get_frame_id() is called later on, an assertion error will
- be triggered in the event of a cycle between the current
- frame and its previous frame. */
- if (this_frame->level == 0)
- get_frame_id (this_frame);
frame_pc_p = get_frame_pc_if_available (this_frame, &frame_pc);
pcsqh register (space register for the instruction at the head of the
instruction queue) cannot be written directly; the only way to set it
is to branch to code that is in the target space. In order to implement
- frame dummies on HPUX, the called function is made to jump back to where
- the inferior was when the user function was called. If gdb was inside
- the main function when we created the dummy frame, the dummy frame will
+ frame dummies on HPUX, the called function is made to jump back to where
+ the inferior was when the user function was called. If gdb was inside
+ the main function when we created the dummy frame, the dummy frame will
point inside the main function. */
if (this_frame->level >= 0
&& get_frame_type (this_frame) == NORMAL_FRAME
That should provide a far better stopper than the current
heuristics. */
/* NOTE: tausq/2004-10-09: this is needed if, for example, the compiler
- applied tail-call optimizations to main so that a function called
+ applied tail-call optimizations to main so that a function called
from main returns directly to the caller of main. Since we don't
stop at main, we should at least stop at the entry point of the
application. */
{
struct frame_id prev_id;
struct frame_info *frame;
-
+
frame = frame_find_by_id (id);
if (frame != NULL)
void
get_frame_memory (struct frame_info *this_frame, CORE_ADDR addr,
- gdb_byte *buf, int len)
+ gdb::array_view<gdb_byte> buffer)
{
- read_memory (addr, buf, len);
+ read_memory (addr, buffer.data (), buffer.size ());
}
LONGEST
bool
safe_frame_unwind_memory (struct frame_info *this_frame,
- CORE_ADDR addr, gdb_byte *buf, int len)
+ CORE_ADDR addr, gdb::array_view<gdb_byte> buffer)
{
/* NOTE: target_read_memory returns zero on success! */
- return target_read_memory (addr, buf, len) == 0;
+ return target_read_memory (addr, buffer.data (), buffer.size ()) == 0;
}
/* Architecture methods. */
using boolean_option_def
= gdb::option::boolean_option_def<set_backtrace_options>;
-using uinteger_option_def
- = gdb::option::uinteger_option_def<set_backtrace_options>;
const gdb::option::option_def set_backtrace_option_defs[] = {
frame_stash_create ();
- gdb::observers::target_changed.attach (frame_observer_target_changed);
+ gdb::observers::target_changed.attach (frame_observer_target_changed,
+ "frame");
add_basic_prefix_cmd ("backtrace", class_maintenance, _("\
Set backtrace specific variables.\n\
Configure backtrace variables such as the backtrace limit"),
- &set_backtrace_cmdlist, "set backtrace ",
+ &set_backtrace_cmdlist,
0/*allow-unknown*/, &setlist);
add_show_prefix_cmd ("backtrace", class_maintenance, _("\
Show backtrace specific variables.\n\
Show backtrace variables such as the backtrace limit."),
- &show_backtrace_cmdlist, "show backtrace ",
+ &show_backtrace_cmdlist,
0/*allow-unknown*/, &showlist);
add_setshow_uinteger_cmd ("limit", class_obscure,