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

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