Blame SOURCES/gdb-rhbz1909902-frame_id_p-assert-1.patch

4a80f0
From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
4a80f0
From: Pedro Alves <pedro@palves.net>
4a80f0
Date: Fri, 30 Oct 2020 18:26:15 +0100
4a80f0
Subject: gdb-rhbz1909902-frame_id_p-assert-1.patch
4a80f0
4a80f0
;; Backport fix for frame_id_p assertion failure (RH BZ 1909902).
4a80f0
4a80f0
Make scoped_restore_current_thread's cdtors exception free (RFC)
4a80f0
4a80f0
If the remote target closes while we're reading registers/memory for
4a80f0
restoring the selected frame in scoped_restore_current_thread's dtor,
4a80f0
the corresponding TARGET_CLOSE_ERROR error is swallowed by the
4a80f0
scoped_restore_current_thread's dtor, because letting exceptions
4a80f0
escape from a dtor is bad.  It isn't great to lose that errors like
4a80f0
that, though.  I've been thinking about how to avoid it, and I came up
4a80f0
with this patch.
4a80f0
4a80f0
The idea here is to make scoped_restore_current_thread's dtor do as
4a80f0
little as possible, to avoid any work that might throw in the first
4a80f0
place.  And to do that, instead of having the dtor call
4a80f0
restore_selected_frame, which re-finds the previously selected frame,
4a80f0
just record the frame_id/level of the desired selected frame, and have
4a80f0
get_selected_frame find the frame the next time it is called.  In
4a80f0
effect, this implements most of Cagney's suggestion, here:
4a80f0
4a80f0
  /* On demand, create the selected frame and then return it.  If the
4a80f0
     selected frame can not be created, this function prints then throws
4a80f0
     an error.  When MESSAGE is non-NULL, use it for the error message,
4a80f0
     otherwize use a generic error message.  */
4a80f0
  /* FIXME: cagney/2002-11-28: At present, when there is no selected
4a80f0
     frame, this function always returns the current (inner most) frame.
4a80f0
     It should instead, when a thread has previously had its frame
4a80f0
     selected (but not resumed) and the frame cache invalidated, find
4a80f0
     and then return that thread's previously selected frame.  */
4a80f0
  extern struct frame_info *get_selected_frame (const char *message);
4a80f0
4a80f0
The only thing missing to fully implement that would be to make
4a80f0
reinit_frame_cache just clear selected_frame instead of calling
4a80f0
select_frame(NULL), and the call select_frame(NULL) explicitly in the
4a80f0
places where we really wanted reinit_frame_cache to go back to the
4a80f0
current frame too.  That can done separately, though, I'm not
4a80f0
proposing to do that in this patch.
4a80f0
4a80f0
Note that this patch renames restore_selected_frame to
4a80f0
lookup_selected_frame, and adds a new restore_selected_frame function
4a80f0
that doesn't throw, to be paired with the also-new save_selected_frame
4a80f0
function.
4a80f0
4a80f0
There's a restore_selected_frame function in infrun.c that I think can
4a80f0
be replaced by the new one in frame.c.
4a80f0
4a80f0
Also done in this patch is make the get_selected_frame's parameter be
4a80f0
optional, so that we don't have to pass down nullptr explicitly all
4a80f0
over the place.
4a80f0
4a80f0
lookup_selected_frame should really move from thread.c to frame.c, but
4a80f0
I didn't do that here, just to avoid churn in the patch while it
4a80f0
collects comments.  I did make it extern and declared it in frame.h
4a80f0
already, preparing for the move.  I will do the move as a follow up
4a80f0
patch if people agree with this approach.
4a80f0
4a80f0
Incidentally, this patch alone would fix the crashes fixed by the
4a80f0
previous patches in the series, because with this,
4a80f0
scoped_restore_current_thread's constructor doesn't throw either.
4a80f0
4a80f0
gdb/ChangeLog:
4a80f0
4a80f0
	* blockframe.c (block_innermost_frame): Use get_selected_frame.
4a80f0
	* frame.c
4a80f0
	(scoped_restore_selected_frame::scoped_restore_selected_frame):
4a80f0
	Use save_selected_frame.  Save language as well.
4a80f0
	(scoped_restore_selected_frame::~scoped_restore_selected_frame):
4a80f0
	Use restore_selected_frame, and restore language as well.
4a80f0
	(selected_frame_id, selected_frame_level): New.
4a80f0
	(selected_frame): Update comments.
4a80f0
	(save_selected_frame, restore_selected_frame): New.
4a80f0
	(get_selected_frame): Use lookup_selected_frame.
4a80f0
	(get_selected_frame_if_set): Delete.
4a80f0
	(select_frame): Record selected_frame_level and selected_frame_id.
4a80f0
	* frame.h (scoped_restore_selected_frame) <m_level, m_lang>: New
4a80f0
	fields.
4a80f0
	(get_selected_frame): Make 'message' parameter optional.
4a80f0
	(get_selected_frame_if_set): Delete declaration.
4a80f0
	(select_frame): Update comments.
4a80f0
	(save_selected_frame, restore_selected_frame)
4a80f0
	(lookup_selected_frame): Declare.
4a80f0
	* gdbthread.h (scoped_restore_current_thread) <m_lang>: New field.
4a80f0
	* infrun.c (struct infcall_control_state) <selected_frame_level>:
4a80f0
	New field.
4a80f0
	(save_infcall_control_state): Use save_selected_frame.
4a80f0
	(restore_selected_frame): Delete.
4a80f0
	(restore_infcall_control_state): Use restore_selected_frame.
4a80f0
	* stack.c (select_frame_command_core, frame_command_core): Use
4a80f0
	get_selected_frame.
4a80f0
	* thread.c (restore_selected_frame): Rename to ...
4a80f0
	(lookup_selected_frame): ... this and make extern.  Select the
4a80f0
	current frame if the frame level is -1.
4a80f0
	(scoped_restore_current_thread::restore): Also restore the
4a80f0
	language.
4a80f0
	(scoped_restore_current_thread::~scoped_restore_current_thread):
4a80f0
	Don't try/catch.
4a80f0
	(scoped_restore_current_thread::scoped_restore_current_thread):
4a80f0
	Save the language as well.  Use save_selected_frame.
4a80f0
4a80f0
Change-Id: I73fd1cfc40d8513c28e5596383b7ecd8bcfe700f
4a80f0
4a80f0
diff --git a/gdb/blockframe.c b/gdb/blockframe.c
4a80f0
--- a/gdb/blockframe.c
4a80f0
+++ b/gdb/blockframe.c
4a80f0
@@ -464,14 +464,10 @@ find_gnu_ifunc_target_type (CORE_ADDR resolver_funaddr)
4a80f0
 struct frame_info *
4a80f0
 block_innermost_frame (const struct block *block)
4a80f0
 {
4a80f0
-  struct frame_info *frame;
4a80f0
-
4a80f0
   if (block == NULL)
4a80f0
     return NULL;
4a80f0
 
4a80f0
-  frame = get_selected_frame_if_set ();
4a80f0
-  if (frame == NULL)
4a80f0
-    frame = get_current_frame ();
4a80f0
+  frame_info *frame = get_selected_frame ();
4a80f0
   while (frame != NULL)
4a80f0
     {
4a80f0
       const struct block *frame_block = get_frame_block (frame, NULL);
4a80f0
diff --git a/gdb/frame.c b/gdb/frame.c
4a80f0
--- a/gdb/frame.c
4a80f0
+++ b/gdb/frame.c
4a80f0
@@ -317,17 +317,15 @@ frame_stash_invalidate (void)
4a80f0
 /* See frame.h  */
4a80f0
 scoped_restore_selected_frame::scoped_restore_selected_frame ()
4a80f0
 {
4a80f0
-  m_fid = get_frame_id (get_selected_frame (NULL));
4a80f0
+  m_lang = current_language->la_language;
4a80f0
+  save_selected_frame (&m_fid, &m_level);
4a80f0
 }
4a80f0
 
4a80f0
 /* See frame.h  */
4a80f0
 scoped_restore_selected_frame::~scoped_restore_selected_frame ()
4a80f0
 {
4a80f0
-  frame_info *frame = frame_find_by_id (m_fid);
4a80f0
-  if (frame == NULL)
4a80f0
-    warning (_("Unable to restore previously selected frame."));
4a80f0
-  else
4a80f0
-    select_frame (frame);
4a80f0
+  restore_selected_frame (m_fid, m_level);
4a80f0
+  set_language (m_lang);
4a80f0
 }
4a80f0
 
4a80f0
 /* Flag to control debugging.  */
4a80f0
@@ -1685,10 +1683,63 @@ get_current_frame (void)
4a80f0
 }
4a80f0
 
4a80f0
 /* The "selected" stack frame is used by default for local and arg
4a80f0
-   access.  May be zero, for no selected frame.  */
4a80f0
-
4a80f0
+   access.
4a80f0
+
4a80f0
+   The "single source of truth" for the selected frame is the
4a80f0
+   SELECTED_FRAME_ID / SELECTED_FRAME_LEVEL pair.
4a80f0
+
4a80f0
+   Frame IDs can be saved/restored across reinitializing the frame
4a80f0
+   cache, while frame_info pointers can't (frame_info objects are
4a80f0
+   invalidated).  If we know the corresponding frame_info object, it
4a80f0
+   is cached in SELECTED_FRAME.
4a80f0
+
4a80f0
+   If SELECTED_FRAME_ID / SELECTED_FRAME_LEVEL are null_frame_id / -1,
4a80f0
+   and the target has stack and is stopped, the selected frame is the
4a80f0
+   current (innermost) frame.  This means that SELECTED_FRAME_LEVEL is
4a80f0
+   never 0 and SELECTED_FRAME_ID is never the ID of the innermost
4a80f0
+   frame.
4a80f0
+
4a80f0
+   If SELECTED_FRAME_ID / SELECTED_FRAME_LEVEL are null_frame_id / -1,
4a80f0
+   and the target has no stack or is executing, then there's no
4a80f0
+   selected frame.  */
4a80f0
+static frame_id selected_frame_id = null_frame_id;
4a80f0
+static int selected_frame_level = -1;
4a80f0
+
4a80f0
+/* The cached frame_info object pointing to the selected frame.
4a80f0
+   Looked up on demand by get_selected_frame.  */
4a80f0
 static struct frame_info *selected_frame;
4a80f0
 
4a80f0
+/* See frame.h.  */
4a80f0
+
4a80f0
+void
4a80f0
+save_selected_frame (frame_id *frame_id, int *frame_level)
4a80f0
+  noexcept
4a80f0
+{
4a80f0
+  *frame_id = selected_frame_id;
4a80f0
+  *frame_level = selected_frame_level;
4a80f0
+}
4a80f0
+
4a80f0
+/* See frame.h.  */
4a80f0
+
4a80f0
+void
4a80f0
+restore_selected_frame (frame_id frame_id, int frame_level)
4a80f0
+  noexcept
4a80f0
+{
4a80f0
+  /* save_selected_frame never returns level == 0, so we shouldn't see
4a80f0
+     it here either.  */
4a80f0
+  gdb_assert (frame_level != 0);
4a80f0
+
4a80f0
+  /* FRAME_ID can be null_frame_id only IFF frame_level is -1.  */
4a80f0
+  gdb_assert ((frame_level == -1 && !frame_id_p (frame_id))
4a80f0
+	      || (frame_level != -1 && frame_id_p (frame_id)));
4a80f0
+
4a80f0
+  selected_frame_id = frame_id;
4a80f0
+  selected_frame_level = frame_level;
4a80f0
+
4a80f0
+  /* Will be looked up later by get_selected_frame.  */
4a80f0
+  selected_frame = nullptr;
4a80f0
+}
4a80f0
+
4a80f0
 bool
4a80f0
 has_stack_frames ()
4a80f0
 {
4a80f0
@@ -1715,9 +1766,7 @@ has_stack_frames ()
4a80f0
   return true;
4a80f0
 }
4a80f0
 
4a80f0
-/* Return the selected frame.  Always non-NULL (unless there isn't an
4a80f0
-   inferior sufficient for creating a frame) in which case an error is
4a80f0
-   thrown.  */
4a80f0
+/* See frame.h.  */
4a80f0
 
4a80f0
 struct frame_info *
4a80f0
 get_selected_frame (const char *message)
4a80f0
@@ -1726,24 +1775,14 @@ get_selected_frame (const char *message)
4a80f0
     {
4a80f0
       if (message != NULL && !has_stack_frames ())
4a80f0
 	error (("%s"), message);
4a80f0
-      /* Hey!  Don't trust this.  It should really be re-finding the
4a80f0
-	 last selected frame of the currently selected thread.  This,
4a80f0
-	 though, is better than nothing.  */
4a80f0
-      select_frame (get_current_frame ());
4a80f0
+
4a80f0
+      lookup_selected_frame (selected_frame_id, selected_frame_level);
4a80f0
     }
4a80f0
   /* There is always a frame.  */
4a80f0
   gdb_assert (selected_frame != NULL);
4a80f0
   return selected_frame;
4a80f0
 }
4a80f0
 
4a80f0
-/* If there is a selected frame, return it.  Otherwise, return NULL.  */
4a80f0
-
4a80f0
-struct frame_info *
4a80f0
-get_selected_frame_if_set (void)
4a80f0
-{
4a80f0
-  return selected_frame;
4a80f0
-}
4a80f0
-
4a80f0
 /* This is a variant of get_selected_frame() which can be called when
4a80f0
    the inferior does not have a frame; in that case it will return
4a80f0
    NULL instead of calling error().  */
4a80f0
@@ -1756,12 +1795,42 @@ deprecated_safe_get_selected_frame (void)
4a80f0
   return get_selected_frame (NULL);
4a80f0
 }
4a80f0
 
4a80f0
-/* Select frame FI (or NULL - to invalidate the current frame).  */
4a80f0
+/* Select frame FI (or NULL - to invalidate the selected frame).  */
4a80f0
 
4a80f0
 void
4a80f0
 select_frame (struct frame_info *fi)
4a80f0
 {
4a80f0
   selected_frame = fi;
4a80f0
+  selected_frame_level = frame_relative_level (fi);
4a80f0
+  if (selected_frame_level == 0)
4a80f0
+    {
4a80f0
+      /* Treat the current frame especially -- we want to always
4a80f0
+	 save/restore it without warning, even if the frame ID changes
4a80f0
+	 (see lookup_selected_frame).  E.g.:
4a80f0
+
4a80f0
+	  // The current frame is selected, the target had just stopped.
4a80f0
+	  {
4a80f0
+	    scoped_restore_selected_frame restore_frame;
4a80f0
+	    some_operation_that_changes_the_stack ();
4a80f0
+	  }
4a80f0
+	  // scoped_restore_selected_frame's dtor runs, but the
4a80f0
+	  // original frame_id can't be found.  No matter whether it
4a80f0
+	  // is found or not, we still end up with the now-current
4a80f0
+	  // frame selected.  Warning in lookup_selected_frame in this
4a80f0
+	  // case seems pointless.
4a80f0
+
4a80f0
+	 Also get_frame_id may access the target's registers/memory,
4a80f0
+	 and thus skipping get_frame_id optimizes the common case.
4a80f0
+
4a80f0
+	 Saving the selected frame this way makes get_selected_frame
4a80f0
+	 and restore_current_frame return/re-select whatever frame is
4a80f0
+	 the innermost (current) then.  */
4a80f0
+      selected_frame_level = -1;
4a80f0
+      selected_frame_id = null_frame_id;
4a80f0
+    }
4a80f0
+  else
4a80f0
+    selected_frame_id = get_frame_id (fi);
4a80f0
+
4a80f0
   /* NOTE: cagney/2002-05-04: FI can be NULL.  This occurs when the
4a80f0
      frame is being invalidated.  */
4a80f0
 
4a80f0
diff --git a/gdb/frame.h b/gdb/frame.h
4a80f0
--- a/gdb/frame.h
4a80f0
+++ b/gdb/frame.h
4a80f0
@@ -186,8 +186,14 @@ class scoped_restore_selected_frame
4a80f0
 
4a80f0
 private:
4a80f0
 
4a80f0
-  /* The ID of the previously selected frame.  */
4a80f0
+  /* The ID and level of the previously selected frame.  */
4a80f0
   struct frame_id m_fid;
4a80f0
+  int m_level;
4a80f0
+
4a80f0
+  /* Save/restore the language as well, because selecting a frame
4a80f0
+     changes the current language to the frame's language if "set
4a80f0
+     language auto".  */
4a80f0
+  enum language m_lang;
4a80f0
 };
4a80f0
 
4a80f0
 /* Methods for constructing and comparing Frame IDs.  */
4a80f0
@@ -316,24 +322,49 @@ extern bool has_stack_frames ();
4a80f0
    modifies the target invalidating the frame cache).  */
4a80f0
 extern void reinit_frame_cache (void);
4a80f0
 
4a80f0
-/* On demand, create the selected frame and then return it.  If the
4a80f0
-   selected frame can not be created, this function prints then throws
4a80f0
-   an error.  When MESSAGE is non-NULL, use it for the error message,
4a80f0
+/* Return the selected frame.  Always returns non-NULL.  If there
4a80f0
+   isn't an inferior sufficient for creating a frame, an error is
4a80f0
+   thrown.  When MESSAGE is non-NULL, use it for the error message,
4a80f0
    otherwise use a generic error message.  */
4a80f0
 /* FIXME: cagney/2002-11-28: At present, when there is no selected
4a80f0
    frame, this function always returns the current (inner most) frame.
4a80f0
    It should instead, when a thread has previously had its frame
4a80f0
    selected (but not resumed) and the frame cache invalidated, find
4a80f0
    and then return that thread's previously selected frame.  */
4a80f0
-extern struct frame_info *get_selected_frame (const char *message);
4a80f0
-
4a80f0
-/* If there is a selected frame, return it.  Otherwise, return NULL.  */
4a80f0
-extern struct frame_info *get_selected_frame_if_set (void);
4a80f0
+extern struct frame_info *get_selected_frame (const char *message = nullptr);
4a80f0
 
4a80f0
-/* Select a specific frame.  NULL, apparently implies re-select the
4a80f0
-   inner most frame.  */
4a80f0
+/* Select a specific frame.  NULL implies re-select the inner most
4a80f0
+   frame.  */
4a80f0
 extern void select_frame (struct frame_info *);
4a80f0
 
4a80f0
+/* Save the frame ID and frame level of the selected frame in FRAME_ID
4a80f0
+   and FRAME_LEVEL, to be restored later with restore_selected_frame.
4a80f0
+
4a80f0
+   This is preferred over getting the same info out of
4a80f0
+   get_selected_frame directly because this function does not create
4a80f0
+   the selected-frame's frame_info object if it hasn't been created
4a80f0
+   yet, and thus is more efficient and doesn't throw.  */
4a80f0
+extern void save_selected_frame (frame_id *frame_id, int *frame_level)
4a80f0
+  noexcept;
4a80f0
+
4a80f0
+/* Restore selected frame as saved with save_selected_frame.
4a80f0
+
4a80f0
+   Does not try to find the corresponding frame_info object.  Instead
4a80f0
+   the next call to get_selected_frame will look it up and cache the
4a80f0
+   result.
4a80f0
+
4a80f0
+   This function does not throw.  It is designed to be safe to called
4a80f0
+   from the destructors of RAII types.  */
4a80f0
+extern void restore_selected_frame (frame_id frame_id, int frame_level)
4a80f0
+  noexcept;
4a80f0
+
4a80f0
+/* Lookup the frame_info object for the selected frame FRAME_ID /
4a80f0
+   FRAME_LEVEL and cache the result.
4a80f0
+
4a80f0
+   If FRAME_LEVEL > 0 and the originally selected frame isn't found,
4a80f0
+   warn and select the innermost (current) frame.  */
4a80f0
+extern void lookup_selected_frame (frame_id frame_id, int frame_level);
4a80f0
+
4a80f0
 /* Given a FRAME, return the next (more inner, younger) or previous
4a80f0
    (more outer, older) frame.  */
4a80f0
 extern struct frame_info *get_prev_frame (struct frame_info *);
4a80f0
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
4a80f0
--- a/gdb/gdbthread.h
4a80f0
+++ b/gdb/gdbthread.h
4a80f0
@@ -673,6 +673,10 @@ class scoped_restore_current_thread
4a80f0
   frame_id m_selected_frame_id;
4a80f0
   int m_selected_frame_level;
4a80f0
   bool m_was_stopped;
4a80f0
+  /* Save/restore the language as well, because selecting a frame
4a80f0
+     changes the current language to the frame's language if "set
4a80f0
+     language auto".  */
4a80f0
+  enum language m_lang;
4a80f0
 };
4a80f0
 
4a80f0
 /* Returns a pointer into the thread_info corresponding to
4a80f0
diff --git a/gdb/infrun.c b/gdb/infrun.c
4a80f0
--- a/gdb/infrun.c
4a80f0
+++ b/gdb/infrun.c
4a80f0
@@ -9006,8 +9006,10 @@ struct infcall_control_state
4a80f0
   enum stop_stack_kind stop_stack_dummy = STOP_NONE;
4a80f0
   int stopped_by_random_signal = 0;
4a80f0
 
4a80f0
-  /* ID if the selected frame when the inferior function call was made.  */
4a80f0
+  /* ID and level of the selected frame when the inferior function
4a80f0
+     call was made.  */
4a80f0
   struct frame_id selected_frame_id {};
4a80f0
+  int selected_frame_level = -1;
4a80f0
 };
4a80f0
 
4a80f0
 /* Save all of the information associated with the inferior<==>gdb
4a80f0
@@ -9036,27 +9038,12 @@ save_infcall_control_state ()
4a80f0
   inf_status->stop_stack_dummy = stop_stack_dummy;
4a80f0
   inf_status->stopped_by_random_signal = stopped_by_random_signal;
4a80f0
 
4a80f0
-  inf_status->selected_frame_id = get_frame_id (get_selected_frame (NULL));
4a80f0
+  save_selected_frame (&inf_status->selected_frame_id,
4a80f0
+		       &inf_status->selected_frame_level);
4a80f0
 
4a80f0
   return inf_status;
4a80f0
 }
4a80f0
 
4a80f0
-static void
4a80f0
-restore_selected_frame (const frame_id &fid)
4a80f0
-{
4a80f0
-  frame_info *frame = frame_find_by_id (fid);
4a80f0
-
4a80f0
-  /* If inf_status->selected_frame_id is NULL, there was no previously
4a80f0
-     selected frame.  */
4a80f0
-  if (frame == NULL)
4a80f0
-    {
4a80f0
-      warning (_("Unable to restore previously selected frame."));
4a80f0
-      return;
4a80f0
-    }
4a80f0
-
4a80f0
-  select_frame (frame);
4a80f0
-}
4a80f0
-
4a80f0
 /* Restore inferior session state to INF_STATUS.  */
4a80f0
 
4a80f0
 void
4a80f0
@@ -9084,21 +9071,8 @@ restore_infcall_control_state (struct infcall_control_state *inf_status)
4a80f0
 
4a80f0
   if (target_has_stack)
4a80f0
     {
4a80f0
-      /* The point of the try/catch is that if the stack is clobbered,
4a80f0
-         walking the stack might encounter a garbage pointer and
4a80f0
-         error() trying to dereference it.  */
4a80f0
-      try
4a80f0
-	{
4a80f0
-	  restore_selected_frame (inf_status->selected_frame_id);
4a80f0
-	}
4a80f0
-      catch (const gdb_exception_error &ex)
4a80f0
-	{
4a80f0
-	  exception_fprintf (gdb_stderr, ex,
4a80f0
-			     "Unable to restore previously selected frame:\n");
4a80f0
-	  /* Error in restoring the selected frame.  Select the
4a80f0
-	     innermost frame.  */
4a80f0
-	  select_frame (get_current_frame ());
4a80f0
-	}
4a80f0
+      restore_selected_frame (inf_status->selected_frame_id,
4a80f0
+			      inf_status->selected_frame_level);
4a80f0
     }
4a80f0
 
4a80f0
   delete inf_status;
4a80f0
diff --git a/gdb/stack.c b/gdb/stack.c
4a80f0
--- a/gdb/stack.c
4a80f0
+++ b/gdb/stack.c
4a80f0
@@ -1842,9 +1842,9 @@ trailing_outermost_frame (int count)
4a80f0
 static void
4a80f0
 select_frame_command_core (struct frame_info *fi, bool ignored)
4a80f0
 {
4a80f0
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
4a80f0
+  frame_info *prev_frame = get_selected_frame ();
4a80f0
   select_frame (fi);
4a80f0
-  if (get_selected_frame_if_set () != prev_frame)
4a80f0
+  if (get_selected_frame () != prev_frame)
4a80f0
     gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
4a80f0
 }
4a80f0
 
4a80f0
@@ -1863,10 +1863,9 @@ select_frame_for_mi (struct frame_info *fi)
4a80f0
 static void
4a80f0
 frame_command_core (struct frame_info *fi, bool ignored)
4a80f0
 {
4a80f0
-  struct frame_info *prev_frame = get_selected_frame_if_set ();
4a80f0
-
4a80f0
+  frame_info *prev_frame = get_selected_frame ();
4a80f0
   select_frame (fi);
4a80f0
-  if (get_selected_frame_if_set () != prev_frame)
4a80f0
+  if (get_selected_frame () != prev_frame)
4a80f0
     gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
4a80f0
   else
4a80f0
     print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
4a80f0
diff --git a/gdb/thread.c b/gdb/thread.c
4a80f0
--- a/gdb/thread.c
4a80f0
+++ b/gdb/thread.c
4a80f0
@@ -1325,20 +1325,26 @@ switch_to_thread (process_stratum_target *proc_target, ptid_t ptid)
4a80f0
   switch_to_thread (thr);
4a80f0
 }
4a80f0
 
4a80f0
-static void
4a80f0
-restore_selected_frame (struct frame_id a_frame_id, int frame_level)
4a80f0
+/* See frame.h.  */
4a80f0
+
4a80f0
+void
4a80f0
+lookup_selected_frame (struct frame_id a_frame_id, int frame_level)
4a80f0
 {
4a80f0
   struct frame_info *frame = NULL;
4a80f0
   int count;
4a80f0
 
4a80f0
-  /* This means there was no selected frame.  */
4a80f0
+  /* This either means there was no selected frame, or the selected
4a80f0
+     frame was the current frame.  In either case, select the current
4a80f0
+     frame.  */
4a80f0
   if (frame_level == -1)
4a80f0
     {
4a80f0
-      select_frame (NULL);
4a80f0
+      select_frame (get_current_frame ());
4a80f0
       return;
4a80f0
     }
4a80f0
 
4a80f0
-  gdb_assert (frame_level >= 0);
4a80f0
+  /* select_frame never saves 0 in SELECTED_FRAME_LEVEL, so we
4a80f0
+     shouldn't see it here.  */
4a80f0
+  gdb_assert (frame_level > 0);
4a80f0
 
4a80f0
   /* Restore by level first, check if the frame id is the same as
4a80f0
      expected.  If that fails, try restoring by frame id.  If that
4a80f0
@@ -1409,64 +1415,28 @@ scoped_restore_current_thread::restore ()
4a80f0
       && target_has_stack
4a80f0
       && target_has_memory)
4a80f0
     restore_selected_frame (m_selected_frame_id, m_selected_frame_level);
4a80f0
+
4a80f0
+  set_language (m_lang);
4a80f0
 }
4a80f0
 
4a80f0
 scoped_restore_current_thread::~scoped_restore_current_thread ()
4a80f0
 {
4a80f0
   if (!m_dont_restore)
4a80f0
-    {
4a80f0
-      try
4a80f0
-	{
4a80f0
-	  restore ();
4a80f0
-	}
4a80f0
-      catch (const gdb_exception &ex)
4a80f0
-	{
4a80f0
-	  /* We're in a dtor, there's really nothing else we can do
4a80f0
-	     but swallow the exception.  */
4a80f0
-	}
4a80f0
-    }
4a80f0
+    restore ();
4a80f0
 }
4a80f0
 
4a80f0
 scoped_restore_current_thread::scoped_restore_current_thread ()
4a80f0
 {
4a80f0
   m_inf = inferior_ref::new_reference (current_inferior ());
4a80f0
 
4a80f0
+  m_lang = current_language->la_language;
4a80f0
+
4a80f0
   if (inferior_ptid != null_ptid)
4a80f0
     {
4a80f0
       m_thread = thread_info_ref::new_reference (inferior_thread ());
4a80f0
 
4a80f0
-      struct frame_info *frame;
4a80f0
-
4a80f0
       m_was_stopped = m_thread->state == THREAD_STOPPED;
4a80f0
-      if (m_was_stopped
4a80f0
-	  && target_has_registers
4a80f0
-	  && target_has_stack
4a80f0
-	  && target_has_memory)
4a80f0
-	{
4a80f0
-	  /* When processing internal events, there might not be a
4a80f0
-	     selected frame.  If we naively call get_selected_frame
4a80f0
-	     here, then we can end up reading debuginfo for the
4a80f0
-	     current frame, but we don't generally need the debuginfo
4a80f0
-	     at this point.  */
4a80f0
-	  frame = get_selected_frame_if_set ();
4a80f0
-	}
4a80f0
-      else
4a80f0
-	frame = NULL;
4a80f0
-
4a80f0
-      try
4a80f0
-	{
4a80f0
-	  m_selected_frame_id = get_frame_id (frame);
4a80f0
-	  m_selected_frame_level = frame_relative_level (frame);
4a80f0
-	}
4a80f0
-      catch (const gdb_exception_error &ex)
4a80f0
-	{
4a80f0
-	  m_selected_frame_id = null_frame_id;
4a80f0
-	  m_selected_frame_level = -1;
4a80f0
-
4a80f0
-	  /* Better let this propagate.  */
4a80f0
-	  if (ex.error == TARGET_CLOSE_ERROR)
4a80f0
-	    throw;
4a80f0
-	}
4a80f0
+      save_selected_frame (&m_selected_frame_id, &m_selected_frame_level);
4a80f0
     }
4a80f0
 }
4a80f0