public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/4] Modernize frame unwinders and add disable feature
@ 2024-03-06 12:51 Guinevere Larsen
  2024-03-06 12:51 ` [PATCH 1/4] gdb: make gdbarch store a vector of frame unwinders Guinevere Larsen
                   ` (4 more replies)
  0 siblings, 5 replies; 23+ messages in thread
From: Guinevere Larsen @ 2024-03-06 12:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Guinevere Larsen

This patch series started with me trying to make it easier to test GDB's
ability to unwind using CFI data, to improve a previous patch I sent to
the list. However, once I finished these changes, I realized there was
an unrelated bug I should fix before proposing the CFI test. Since these
changes are significant enough already, and I think would be interesting
on their own, I figured I shoudl submit this patch series as is right
now while I figure out the other bug.

The first patch is just a minor change, storing frame unwinders in a
vector instead of through an unwinder table accessible using the
registry system. This isn't required (like I originally thought it was),
but it does make the whole system more readable in my opinion.

Patch 3 has the real meat of the modernization, making GDB use
polymorphism to handle frame unwinders. This is slightly slower than
using function pointers, but much more readable in my opinion.

As for the unwinder classes, they were chosen somewhat arbitrarily,
mostly based on where I found an unwinder and its name. I almost expect
some unwinders to be mis-categorized, but that should be easy to fix.

The changes up to patch 3 have been tested with a try-branch, no
regressions as far as I could see.

Guinevere Larsen (4):
  gdb: make gdbarch store a vector of frame unwinders
  gdb: add "unwinder class" to frame unwinders
  gdb: Migrate frame unwinders to use C++ classes
  GDB: introduce ability to disable frame unwinders

 gdb/NEWS                                      |   7 +
 gdb/aarch64-tdep.c                            |  12 +-
 gdb/alpha-mdebug-tdep.c                       |   6 +-
 gdb/alpha-tdep.c                              |  12 +-
 gdb/amd64-obsd-tdep.c                         |   6 +-
 gdb/amd64-tdep.c                              |  24 +-
 gdb/amd64-windows-tdep.c                      |   6 +-
 gdb/amdgpu-tdep.c                             |   5 +-
 gdb/arc-tdep.c                                |  10 +-
 gdb/arch-utils.c                              |   8 +
 gdb/arm-tdep.c                                |  29 +-
 gdb/avr-tdep.c                                |   5 +-
 gdb/bfin-tdep.c                               |   6 +-
 gdb/bpf-tdep.c                                |   6 +-
 gdb/cris-tdep.c                               |  12 +-
 gdb/csky-tdep.c                               |  10 +-
 gdb/doc/gdb.texinfo                           |  24 ++
 gdb/dummy-frame.c                             |   8 +-
 gdb/dummy-frame.h                             |   2 +-
 gdb/dwarf2/frame-tailcall.c                   |   6 +-
 gdb/dwarf2/frame-tailcall.h                   |   2 +-
 gdb/dwarf2/frame.c                            |  16 +-
 gdb/frame-unwind.c                            | 359 ++++++++++++++----
 gdb/frame-unwind.h                            | 165 +++++++-
 gdb/frame.c                                   |  28 +-
 gdb/frv-linux-tdep.c                          |   6 +-
 gdb/frv-tdep.c                                |   5 +-
 gdb/ft32-tdep.c                               |   6 +-
 gdb/gdbarch.c                                 |   3 +
 gdb/gdbarch.h                                 |   5 +
 gdb/gdbarch.py                                |   3 +
 gdb/h8300-tdep.c                              |   5 +-
 gdb/hppa-linux-tdep.c                         |   5 +-
 gdb/hppa-tdep.c                               |  17 +-
 gdb/i386-obsd-tdep.c                          |   5 +-
 gdb/i386-tdep.c                               |  30 +-
 gdb/ia64-tdep.c                               |  24 +-
 gdb/inline-frame.c                            |   5 +-
 gdb/inline-frame.h                            |   2 +-
 gdb/iq2000-tdep.c                             |   5 +-
 gdb/jit.c                                     |   6 +-
 gdb/lm32-tdep.c                               |   5 +-
 gdb/loongarch-tdep.c                          |   7 +-
 gdb/m32c-tdep.c                               |   5 +-
 gdb/m32r-linux-tdep.c                         |   5 +-
 gdb/m32r-tdep.c                               |   5 +-
 gdb/m68hc11-tdep.c                            |   5 +-
 gdb/m68k-linux-tdep.c                         |   6 +-
 gdb/m68k-tdep.c                               |   6 +-
 gdb/mep-tdep.c                                |   5 +-
 gdb/microblaze-tdep.c                         |   6 +-
 gdb/mips-sde-tdep.c                           |   6 +-
 gdb/mips-tdep.c                               |  24 +-
 gdb/mn10300-tdep.c                            |   5 +-
 gdb/moxie-tdep.c                              |   5 +-
 gdb/msp430-tdep.c                             |   5 +-
 gdb/nds32-tdep.c                              |  14 +-
 gdb/nios2-tdep.c                              |  12 +-
 gdb/or1k-tdep.c                               |   7 +-
 gdb/ppc-fbsd-tdep.c                           |   5 +-
 gdb/ppc-obsd-tdep.c                           |   5 +-
 gdb/python/py-unwind.c                        |  50 ++-
 gdb/record-btrace.c                           |  12 +-
 gdb/record.h                                  |   4 +-
 gdb/riscv-tdep.c                              |   8 +-
 gdb/rl78-tdep.c                               |   6 +-
 gdb/rs6000-aix-tdep.c                         |   5 +-
 gdb/rs6000-tdep.c                             |  12 +-
 gdb/rx-tdep.c                                 |  10 +-
 gdb/s12z-tdep.c                               |   7 +-
 gdb/s390-linux-tdep.c                         |   5 +-
 gdb/s390-tdep.c                               |  10 +-
 gdb/sentinel-frame.c                          |   8 +-
 gdb/sentinel-frame.h                          |   2 +-
 gdb/sh-tdep.c                                 |  11 +-
 gdb/sparc-netbsd-tdep.c                       |   6 +-
 gdb/sparc-obsd-tdep.c                         |   6 +-
 gdb/sparc-sol2-tdep.c                         |   6 +-
 gdb/sparc-tdep.c                              |   6 +-
 gdb/sparc64-fbsd-tdep.c                       |   6 +-
 gdb/sparc64-netbsd-tdep.c                     |   6 +-
 gdb/sparc64-obsd-tdep.c                       |  12 +-
 gdb/sparc64-sol2-tdep.c                       |   6 +-
 gdb/sparc64-tdep.c                            |   6 +-
 gdb/testsuite/gdb.base/frame-unwind-disable.c |  21 +
 .../gdb.base/frame-unwind-disable.exp         | 114 ++++++
 gdb/tic6x-tdep.c                              |  12 +-
 gdb/tilegx-tdep.c                             |   5 +-
 gdb/tramp-frame.c                             |  50 ++-
 gdb/v850-tdep.c                               |   5 +-
 gdb/vax-tdep.c                                |   6 +-
 gdb/windows-tdep.c                            |  33 +-
 gdb/windows-tdep.h                            |  16 +-
 gdb/xstormy16-tdep.c                          |   5 +-
 gdb/xtensa-tdep.c                             |   7 +-
 gdb/z80-tdep.c                                |   7 +-
 96 files changed, 1098 insertions(+), 442 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/frame-unwind-disable.c
 create mode 100644 gdb/testsuite/gdb.base/frame-unwind-disable.exp

-- 
2.43.2


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 1/4] gdb: make gdbarch store a vector of frame unwinders
  2024-03-06 12:51 [PATCH 0/4] Modernize frame unwinders and add disable feature Guinevere Larsen
@ 2024-03-06 12:51 ` Guinevere Larsen
  2024-03-08 16:34   ` Tom Tromey
  2024-03-06 12:51 ` [PATCH 2/4] gdb: add "unwinder class" to " Guinevere Larsen
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 23+ messages in thread
From: Guinevere Larsen @ 2024-03-06 12:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Guinevere Larsen

Before this commit, all frame unwinders would be stored in the obstack
of a gdbarch and accessed by using the registry system. This made for
unwieldy code, and unnecessarily complex logic in the frame_unwinder
implementation, along with making frame_unwind structs be unable to have
non-trivial constructors.

Seeing as a future patch of this series wants to refactor the
frame_unwind struct to use inheritance, obstack storage would no longer
be viable. In preparation for that change, this commit adds an
std::vector to gdbarch to store the unwinders in.

There should be no user-visible changes.
---
 gdb/arch-utils.c   |   8 ++++
 gdb/frame-unwind.c | 111 +++++++++++++++++----------------------------
 gdb/gdbarch.c      |   3 ++
 gdb/gdbarch.h      |   5 ++
 gdb/gdbarch.py     |   3 ++
 5 files changed, 61 insertions(+), 69 deletions(-)

diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c
index ae3354f6579..30693d84c8d 100644
--- a/gdb/arch-utils.c
+++ b/gdb/arch-utils.c
@@ -1219,6 +1219,14 @@ obstack *gdbarch_obstack (gdbarch *arch)
 
 /* See gdbarch.h.  */
 
+std::vector<const frame_unwind*>&
+gdbarch_get_unwinder_list (struct gdbarch *arch)
+{
+  return arch->unwinders;
+}
+
+/* See gdbarch.h.  */
+
 char *
 gdbarch_obstack_strdup (struct gdbarch *arch, const char *string)
 {
diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
index e9983a9fd74..27d84981d89 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -31,49 +31,16 @@
 #include "cli/cli-cmds.h"
 #include "inferior.h"
 
-struct frame_unwind_table_entry
-{
-  const struct frame_unwind *unwinder;
-  struct frame_unwind_table_entry *next;
-};
-
-struct frame_unwind_table
-{
-  struct frame_unwind_table_entry *list = nullptr;
-  /* The head of the OSABI part of the search list.  */
-  struct frame_unwind_table_entry **osabi_head = nullptr;
-};
+/* If an unwinder should be prepended to the list, this is the
+   index in which it should be inserted.  */
+static int osabi_start = -1;
 
-static const registry<gdbarch>::key<struct frame_unwind_table>
-     frame_unwind_data;
-
-/* A helper function to add an unwinder to a list.  LINK says where to
-   install the new unwinder.  The new link is returned.  */
-
-static struct frame_unwind_table_entry **
-add_unwinder (struct obstack *obstack, const struct frame_unwind *unwinder,
-	      struct frame_unwind_table_entry **link)
-{
-  *link = OBSTACK_ZALLOC (obstack, struct frame_unwind_table_entry);
-  (*link)->unwinder = unwinder;
-  return &(*link)->next;
-}
-
-static struct frame_unwind_table *
-get_frame_unwind_table (struct gdbarch *gdbarch)
+/* Start the table out with a few default sniffers.  OSABI code
+   can't override this.  */
+static void
+initialize_frame_unwind_table (std::vector<const frame_unwind*>& table)
 {
-  struct frame_unwind_table *table = frame_unwind_data.get (gdbarch);
-  if (table != nullptr)
-    return table;
-
-  table = new frame_unwind_table;
-
-  /* Start the table out with a few default sniffers.  OSABI code
-     can't override this.  */
-  struct frame_unwind_table_entry **link = &table->list;
-
-  struct obstack *obstack = gdbarch_obstack (gdbarch);
-  link = add_unwinder (obstack, &dummy_frame_unwind, link);
+  table.push_back (&dummy_frame_unwind);
   /* The DWARF tailcall sniffer must come before the inline sniffer.
      Otherwise, we can end up in a situation where a DWARF frame finds
      tailcall information, but then the inline sniffer claims a frame
@@ -81,12 +48,22 @@ get_frame_unwind_table (struct gdbarch *gdbarch)
      safe to do always because the tailcall sniffer can only ever be
      activated if the newer frame was created using the DWARF
      unwinder, and it also found tailcall information.  */
-  link = add_unwinder (obstack, &dwarf2_tailcall_frame_unwind, link);
-  link = add_unwinder (obstack, &inline_frame_unwind, link);
+  table.push_back (&dwarf2_tailcall_frame_unwind);
+  table.push_back (&inline_frame_unwind);
 
   /* The insertion point for OSABI sniffers.  */
-  table->osabi_head = link;
-  frame_unwind_data.set (gdbarch, table);
+  osabi_start = table.size ();
+}
+
+/* This function call retrieves the list of frame unwinders available in
+   GDBARCH.  If this list is empty, it is initialized before being
+   returned.  */
+static std::vector<const frame_unwind*>&
+get_frame_unwind_table (struct gdbarch *gdbarch)
+{
+  std::vector<const frame_unwind*>& table = gdbarch_get_unwinder_list (gdbarch);
+  if (table.size () == 0)
+    initialize_frame_unwind_table (table);
 
   return table;
 }
@@ -95,27 +72,25 @@ void
 frame_unwind_prepend_unwinder (struct gdbarch *gdbarch,
 				const struct frame_unwind *unwinder)
 {
-  struct frame_unwind_table *table = get_frame_unwind_table (gdbarch);
-  struct frame_unwind_table_entry *entry;
-
-  /* Insert the new entry at the start of the list.  */
-  entry = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_unwind_table_entry);
-  entry->unwinder = unwinder;
-  entry->next = (*table->osabi_head);
-  (*table->osabi_head) = entry;
+  std::vector<const frame_unwind*>& table = get_frame_unwind_table (gdbarch);
+
+  gdb_assert (osabi_start >= 0);
+
+  table.push_back (unwinder);
+
+  /* Bubble the new entry up to where new unwinders are allowed to be
+     inserterd.  */
+  for (int i = table.size() - 1; i > osabi_start; i--)
+    std::swap(table[i], table[i-1]);
 }
 
 void
 frame_unwind_append_unwinder (struct gdbarch *gdbarch,
 			      const struct frame_unwind *unwinder)
 {
-  struct frame_unwind_table *table = get_frame_unwind_table (gdbarch);
-  struct frame_unwind_table_entry **ip;
+  std::vector<const frame_unwind*>& table = get_frame_unwind_table (gdbarch);
 
-  /* Find the end of the list and insert the new entry there.  */
-  for (ip = table->osabi_head; (*ip) != NULL; ip = &(*ip)->next);
-  (*ip) = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_unwind_table_entry);
-  (*ip)->unwinder = unwinder;
+  table.push_back (unwinder);
 }
 
 /* Call SNIFFER from UNWINDER.  If it succeeded set UNWINDER for
@@ -188,9 +163,6 @@ frame_unwind_find_by_frame (const frame_info_ptr &this_frame, void **this_cache)
   FRAME_SCOPED_DEBUG_ENTER_EXIT;
   frame_debug_printf ("this_frame=%d", frame_relative_level (this_frame));
 
-  struct gdbarch *gdbarch = get_frame_arch (this_frame);
-  struct frame_unwind_table *table = get_frame_unwind_table (gdbarch);
-  struct frame_unwind_table_entry *entry;
   const struct frame_unwind *unwinder_from_target;
 
   unwinder_from_target = target_get_unwinder ();
@@ -205,8 +177,10 @@ frame_unwind_find_by_frame (const frame_info_ptr &this_frame, void **this_cache)
 				   unwinder_from_target))
     return;
 
-  for (entry = table->list; entry != NULL; entry = entry->next)
-    if (frame_unwind_try_unwinder (this_frame, this_cache, entry->unwinder))
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  std::vector<const frame_unwind*> table = get_frame_unwind_table (gdbarch);
+  for (auto unwinder: table)
+    if (frame_unwind_try_unwinder (this_frame, this_cache, unwinder))
       return;
 
   internal_error (_("frame_unwind_find_by_frame failed"));
@@ -347,7 +321,7 @@ static void
 maintenance_info_frame_unwinders (const char *args, int from_tty)
 {
   gdbarch *gdbarch = current_inferior ()->arch ();
-  struct frame_unwind_table *table = get_frame_unwind_table (gdbarch);
+  std::vector<const frame_unwind*> table = get_frame_unwind_table (gdbarch);
 
   ui_out *uiout = current_uiout;
   ui_out_emit_table table_emitter (uiout, 2, -1, "FrameUnwinders");
@@ -355,11 +329,10 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
   uiout->table_header (25, ui_left, "type", "Type");
   uiout->table_body ();
 
-  for (struct frame_unwind_table_entry *entry = table->list; entry != NULL;
-       entry = entry->next)
+  for (const struct frame_unwind* unwinder: table)
     {
-      const char *name = entry->unwinder->name;
-      const char *type = frame_type_str (entry->unwinder->type);
+      const char *name = unwinder->name;
+      const char *type = frame_type_str (unwinder->type);
 
       ui_out_emit_list tuple_emitter (uiout, nullptr);
       uiout->field_string ("name", name);
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 80a04bf0caf..fe88f19dc7f 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -27,6 +27,7 @@
 
 /* Maintain the struct gdbarch object.  */
 
+#include <vector>
 struct gdbarch
 {
   /* Has this architecture been fully initialized?  */
@@ -36,6 +37,8 @@ struct gdbarch
   auto_obstack obstack;
   /* Registry.  */
   registry<gdbarch> registry_fields;
+  /* list of frame unwinders.  */
+  std::vector<const frame_unwind *> unwinders;
 
   /* basic architectural information.  */
   const struct bfd_arch_info * bfd_arch_info;
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 77d3406779f..1434d61e600 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -30,6 +30,7 @@
 #include "displaced-stepping.h"
 #include "gdbsupport/gdb-checked-static-cast.h"
 #include "registry.h"
+#include "frame-unwind.h"
 
 struct floatformat;
 struct ui_file;
@@ -310,6 +311,10 @@ extern obstack *gdbarch_obstack (gdbarch *arch);
 
 #define GDBARCH_OBSTACK_ZALLOC(GDBARCH, TYPE)   obstack_zalloc<TYPE> (gdbarch_obstack ((GDBARCH)))
 
+/* Return the vector of unwinders stored in a gdbarch object.  */
+
+std::vector<const frame_unwind*>& gdbarch_get_unwinder_list (struct gdbarch *arch);
+
 /* Duplicate STRING, returning an equivalent string that's allocated on the
    obstack associated with GDBARCH.  The string is freed when the corresponding
    architecture is also freed.  */
diff --git a/gdb/gdbarch.py b/gdb/gdbarch.py
index 4b4db667ca4..bff66cc39d3 100755
--- a/gdb/gdbarch.py
+++ b/gdb/gdbarch.py
@@ -125,6 +125,7 @@ with open("gdbarch.c", "w") as f:
     print(file=f)
     print("/* Maintain the struct gdbarch object.  */", file=f)
     print(file=f)
+    print("#include <vector>", file=f)
     #
     # The struct definition body.
     #
@@ -137,6 +138,8 @@ with open("gdbarch.c", "w") as f:
     print("  auto_obstack obstack;", file=f)
     print("  /* Registry.  */", file=f)
     print("  registry<gdbarch> registry_fields;", file=f)
+    print("  /* list of frame unwinders.  */", file=f)
+    print("  std::vector<const frame_unwind *> unwinders;", file=f)
     print(file=f)
     print("  /* basic architectural information.  */", file=f)
     for c in filter(info, components):
-- 
2.43.2


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 2/4] gdb: add "unwinder class" to frame unwinders
  2024-03-06 12:51 [PATCH 0/4] Modernize frame unwinders and add disable feature Guinevere Larsen
  2024-03-06 12:51 ` [PATCH 1/4] gdb: make gdbarch store a vector of frame unwinders Guinevere Larsen
@ 2024-03-06 12:51 ` Guinevere Larsen
  2024-03-08 16:40   ` Tom Tromey
  2024-03-06 12:51 ` [PATCH 3/4] gdb: Migrate frame unwinders to use C++ classes Guinevere Larsen
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 23+ messages in thread
From: Guinevere Larsen @ 2024-03-06 12:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Guinevere Larsen

A future patch will add a way to disable certain unwinders based on
different characteristics. This patch aims to make it more convenient
to disable related unwinders in bulk, such as architecture specific
ones, by indentifying all unwinders by which part of the code adds it.
The classes, and explanations, are as follows:

* GDB: An internal unwinder, added by GDB core, such as the unwinder
  for dummy frames;
* EXTENSION: Unwinders added by extension languages;
* DEBUGINFO: Unwinders installed by the debug info reader;
* ARCH: Unwinders installed by the architecture specific code.
---
 gdb/aarch64-tdep.c          |  2 ++
 gdb/alpha-mdebug-tdep.c     |  1 +
 gdb/alpha-tdep.c            |  2 ++
 gdb/amd64-obsd-tdep.c       |  1 +
 gdb/amd64-tdep.c            |  4 ++++
 gdb/amd64-windows-tdep.c    |  1 +
 gdb/amdgpu-tdep.c           |  1 +
 gdb/arc-tdep.c              |  2 ++
 gdb/arm-tdep.c              |  5 +++++
 gdb/avr-tdep.c              |  1 +
 gdb/bfin-tdep.c             |  1 +
 gdb/bpf-tdep.c              |  1 +
 gdb/cris-tdep.c             |  2 ++
 gdb/csky-tdep.c             |  2 ++
 gdb/dummy-frame.c           |  1 +
 gdb/dwarf2/frame-tailcall.c |  1 +
 gdb/dwarf2/frame.c          |  2 ++
 gdb/frame-unwind.c          | 25 ++++++++++++++++++++++++-
 gdb/frame-unwind.h          | 11 +++++++++++
 gdb/frv-linux-tdep.c        |  1 +
 gdb/frv-tdep.c              |  1 +
 gdb/ft32-tdep.c             |  1 +
 gdb/h8300-tdep.c            |  1 +
 gdb/hppa-linux-tdep.c       |  1 +
 gdb/hppa-tdep.c             |  3 +++
 gdb/i386-obsd-tdep.c        |  1 +
 gdb/i386-tdep.c             |  5 +++++
 gdb/ia64-tdep.c             |  4 ++++
 gdb/inline-frame.c          |  1 +
 gdb/iq2000-tdep.c           |  1 +
 gdb/jit.c                   |  1 +
 gdb/lm32-tdep.c             |  1 +
 gdb/loongarch-tdep.c        |  1 +
 gdb/m32c-tdep.c             |  1 +
 gdb/m32r-linux-tdep.c       |  1 +
 gdb/m32r-tdep.c             |  1 +
 gdb/m68hc11-tdep.c          |  1 +
 gdb/m68k-linux-tdep.c       |  1 +
 gdb/m68k-tdep.c             |  1 +
 gdb/mep-tdep.c              |  1 +
 gdb/microblaze-tdep.c       |  1 +
 gdb/mips-sde-tdep.c         |  1 +
 gdb/mips-tdep.c             |  4 ++++
 gdb/mn10300-tdep.c          |  1 +
 gdb/moxie-tdep.c            |  1 +
 gdb/msp430-tdep.c           |  1 +
 gdb/nds32-tdep.c            |  2 ++
 gdb/nios2-tdep.c            |  2 ++
 gdb/or1k-tdep.c             |  1 +
 gdb/ppc-fbsd-tdep.c         |  1 +
 gdb/ppc-obsd-tdep.c         |  1 +
 gdb/python/py-unwind.c      |  1 +
 gdb/record-btrace.c         |  2 ++
 gdb/riscv-tdep.c            |  1 +
 gdb/rl78-tdep.c             |  1 +
 gdb/rs6000-aix-tdep.c       |  1 +
 gdb/rs6000-tdep.c           |  2 ++
 gdb/rx-tdep.c               |  2 ++
 gdb/s12z-tdep.c             |  1 +
 gdb/s390-linux-tdep.c       |  1 +
 gdb/s390-tdep.c             |  2 ++
 gdb/sentinel-frame.c        |  1 +
 gdb/sh-tdep.c               |  2 ++
 gdb/sparc-netbsd-tdep.c     |  1 +
 gdb/sparc-obsd-tdep.c       |  1 +
 gdb/sparc-sol2-tdep.c       |  1 +
 gdb/sparc-tdep.c            |  1 +
 gdb/sparc64-fbsd-tdep.c     |  1 +
 gdb/sparc64-netbsd-tdep.c   |  1 +
 gdb/sparc64-obsd-tdep.c     |  2 ++
 gdb/sparc64-sol2-tdep.c     |  1 +
 gdb/sparc64-tdep.c          |  1 +
 gdb/tic6x-tdep.c            |  2 ++
 gdb/tilegx-tdep.c           |  1 +
 gdb/tramp-frame.c           |  1 +
 gdb/v850-tdep.c             |  1 +
 gdb/vax-tdep.c              |  1 +
 gdb/xstormy16-tdep.c        |  1 +
 gdb/xtensa-tdep.c           |  1 +
 gdb/z80-tdep.c              |  1 +
 80 files changed, 147 insertions(+), 1 deletion(-)

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index d1d5486f90d..db0cf28d815 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -1208,6 +1208,7 @@ static frame_unwind aarch64_prologue_unwind =
 {
   "aarch64 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   aarch64_prologue_frame_unwind_stop_reason,
   aarch64_prologue_this_id,
   aarch64_prologue_prev_register,
@@ -1303,6 +1304,7 @@ static frame_unwind aarch64_stub_unwind =
 {
   "aarch64 stub",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   aarch64_stub_frame_unwind_stop_reason,
   aarch64_stub_this_id,
   aarch64_prologue_prev_register,
diff --git a/gdb/alpha-mdebug-tdep.c b/gdb/alpha-mdebug-tdep.c
index 7e9312d2aa7..bc7eb8562e9 100644
--- a/gdb/alpha-mdebug-tdep.c
+++ b/gdb/alpha-mdebug-tdep.c
@@ -335,6 +335,7 @@ static const struct frame_unwind alpha_mdebug_frame_unwind =
 {
   "alpha mdebug",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   alpha_mdebug_frame_this_id,
   alpha_mdebug_frame_prev_register,
diff --git a/gdb/alpha-tdep.c b/gdb/alpha-tdep.c
index e5e0860fe24..f24312b7cbc 100644
--- a/gdb/alpha-tdep.c
+++ b/gdb/alpha-tdep.c
@@ -1011,6 +1011,7 @@ static const struct frame_unwind alpha_sigtramp_frame_unwind =
 {
   "alpha sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   alpha_sigtramp_frame_this_id,
   alpha_sigtramp_frame_prev_register,
@@ -1430,6 +1431,7 @@ static const struct frame_unwind alpha_heuristic_frame_unwind =
 {
   "alpha prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   alpha_heuristic_frame_this_id,
   alpha_heuristic_frame_prev_register,
diff --git a/gdb/amd64-obsd-tdep.c b/gdb/amd64-obsd-tdep.c
index 6b37e8954ee..0159bdb4049 100644
--- a/gdb/amd64-obsd-tdep.c
+++ b/gdb/amd64-obsd-tdep.c
@@ -409,6 +409,7 @@ static const struct frame_unwind amd64obsd_trapframe_unwind =
      which really is not what we want here.  */
   "amd64 openbsd trap",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   amd64obsd_trapframe_this_id,
   amd64obsd_trapframe_prev_register,
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 60d6b7aa198..bbeeb935192 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -2682,6 +2682,7 @@ static const struct frame_unwind amd64_frame_unwind =
 {
   "amd64 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   amd64_frame_unwind_stop_reason,
   amd64_frame_this_id,
   amd64_frame_prev_register,
@@ -2828,6 +2829,7 @@ static const struct frame_unwind amd64_sigtramp_frame_unwind =
 {
   "amd64 sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   amd64_sigtramp_frame_unwind_stop_reason,
   amd64_sigtramp_frame_this_id,
   amd64_sigtramp_frame_prev_register,
@@ -3020,6 +3022,7 @@ static const struct frame_unwind amd64_epilogue_override_frame_unwind =
 {
   "amd64 epilogue override",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   amd64_epilogue_frame_unwind_stop_reason,
   amd64_epilogue_frame_this_id,
   amd64_frame_prev_register,
@@ -3031,6 +3034,7 @@ static const struct frame_unwind amd64_epilogue_frame_unwind =
 {
   "amd64 epilogue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   amd64_epilogue_frame_unwind_stop_reason,
   amd64_epilogue_frame_this_id,
   amd64_frame_prev_register,
diff --git a/gdb/amd64-windows-tdep.c b/gdb/amd64-windows-tdep.c
index af17886c080..e4459b4717b 100644
--- a/gdb/amd64-windows-tdep.c
+++ b/gdb/amd64-windows-tdep.c
@@ -1185,6 +1185,7 @@ static const struct frame_unwind amd64_windows_frame_unwind =
 {
   "amd64 windows",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   &amd64_windows_frame_this_id,
   &amd64_windows_frame_prev_register,
diff --git a/gdb/amdgpu-tdep.c b/gdb/amdgpu-tdep.c
index ac137d9acc4..8189de8e565 100644
--- a/gdb/amdgpu-tdep.c
+++ b/gdb/amdgpu-tdep.c
@@ -895,6 +895,7 @@ amdgpu_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
 static const frame_unwind amdgpu_frame_unwind = {
   "amdgpu",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   amdgpu_frame_this_id,
   amdgpu_frame_prev_register,
diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c
index 7dd43cc239f..9e756ad473e 100644
--- a/gdb/arc-tdep.c
+++ b/gdb/arc-tdep.c
@@ -1913,6 +1913,7 @@ arc_sigtramp_frame_sniffer (const struct frame_unwind *self,
 static const struct frame_unwind arc_frame_unwind = {
   "arc prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   arc_frame_this_id,
   arc_frame_prev_register,
@@ -1929,6 +1930,7 @@ static const struct frame_unwind arc_frame_unwind = {
 static const struct frame_unwind arc_sigtramp_frame_unwind = {
   "arc sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   arc_sigtramp_frame_this_id,
   arc_sigtramp_frame_prev_register,
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 3b4ae15df07..6bec31dead1 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -2472,6 +2472,7 @@ arm_prologue_prev_register (const frame_info_ptr &this_frame,
 static frame_unwind arm_prologue_unwind = {
   "arm prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   arm_prologue_unwind_stop_reason,
   arm_prologue_this_id,
   arm_prologue_prev_register,
@@ -3191,6 +3192,7 @@ arm_exidx_unwind_sniffer (const struct frame_unwind *self,
 struct frame_unwind arm_exidx_unwind = {
   "arm exidx",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   arm_prologue_this_id,
   arm_prologue_prev_register,
@@ -3301,6 +3303,7 @@ static const struct frame_unwind arm_epilogue_frame_unwind =
 {
   "arm epilogue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   arm_epilogue_frame_this_id,
   arm_epilogue_frame_prev_register,
@@ -3430,6 +3433,7 @@ arm_stub_unwind_sniffer (const struct frame_unwind *self,
 struct frame_unwind arm_stub_unwind = {
   "arm stub",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   arm_stub_this_id,
   arm_prologue_prev_register,
@@ -3956,6 +3960,7 @@ struct frame_unwind arm_m_exception_unwind =
 {
   "arm m exception lockup sec_fnc",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   arm_m_exception_frame_unwind_stop_reason,
   arm_m_exception_this_id,
   arm_m_exception_prev_register,
diff --git a/gdb/avr-tdep.c b/gdb/avr-tdep.c
index 2d38f41cd7f..0c12a8a1d65 100644
--- a/gdb/avr-tdep.c
+++ b/gdb/avr-tdep.c
@@ -1158,6 +1158,7 @@ avr_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind avr_frame_unwind = {
   "avr prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   avr_frame_this_id,
   avr_frame_prev_register,
diff --git a/gdb/bfin-tdep.c b/gdb/bfin-tdep.c
index 357866ef953..6c15098fbbc 100644
--- a/gdb/bfin-tdep.c
+++ b/gdb/bfin-tdep.c
@@ -376,6 +376,7 @@ static const struct frame_unwind bfin_frame_unwind =
 {
   "bfin prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   bfin_frame_this_id,
   bfin_frame_prev_register,
diff --git a/gdb/bpf-tdep.c b/gdb/bpf-tdep.c
index 1b3f3e72c5b..15bd08b8f58 100644
--- a/gdb/bpf-tdep.c
+++ b/gdb/bpf-tdep.c
@@ -186,6 +186,7 @@ static const struct frame_unwind bpf_frame_unwind =
 {
   "bpf prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   bpf_frame_unwind_stop_reason,
   bpf_frame_this_id,
   bpf_frame_prev_register,
diff --git a/gdb/cris-tdep.c b/gdb/cris-tdep.c
index 02c35da723d..c1a0e5fe961 100644
--- a/gdb/cris-tdep.c
+++ b/gdb/cris-tdep.c
@@ -439,6 +439,7 @@ static const struct frame_unwind cris_sigtramp_frame_unwind =
 {
   "cris sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   cris_sigtramp_frame_this_id,
   cris_sigtramp_frame_prev_register,
@@ -904,6 +905,7 @@ static const struct frame_unwind cris_frame_unwind =
 {
   "cris prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   cris_frame_this_id,
   cris_frame_prev_register,
diff --git a/gdb/csky-tdep.c b/gdb/csky-tdep.c
index c1ce8a5b02c..d95b0a2f9f2 100644
--- a/gdb/csky-tdep.c
+++ b/gdb/csky-tdep.c
@@ -2162,6 +2162,7 @@ csky_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind csky_unwind_cache = {
   "cski prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   csky_frame_this_id,
   csky_frame_prev_register,
@@ -2296,6 +2297,7 @@ csky_stub_prev_register (const frame_info_ptr &this_frame,
 static frame_unwind csky_stub_unwind = {
   "csky stub",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   csky_stub_this_id,
   csky_stub_prev_register,
diff --git a/gdb/dummy-frame.c b/gdb/dummy-frame.c
index 5969ef2cdad..a208e370aac 100644
--- a/gdb/dummy-frame.c
+++ b/gdb/dummy-frame.c
@@ -380,6 +380,7 @@ const struct frame_unwind dummy_frame_unwind =
 {
   "dummy",
   DUMMY_FRAME,
+  FRAME_UNWIND_GDB,
   default_frame_unwind_stop_reason,
   dummy_frame_this_id,
   dummy_frame_prev_register,
diff --git a/gdb/dwarf2/frame-tailcall.c b/gdb/dwarf2/frame-tailcall.c
index 2f6400d62c3..c4daaf28a01 100644
--- a/gdb/dwarf2/frame-tailcall.c
+++ b/gdb/dwarf2/frame-tailcall.c
@@ -474,6 +474,7 @@ const struct frame_unwind dwarf2_tailcall_frame_unwind =
 {
   "dwarf2 tailcall",
   TAILCALL_FRAME,
+  FRAME_UNWIND_DEBUGINFO,
   default_frame_unwind_stop_reason,
   tailcall_frame_this_id,
   tailcall_frame_prev_register,
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 530513c2684..c1fec57b7c7 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -1344,6 +1344,7 @@ static const struct frame_unwind dwarf2_frame_unwind =
 {
   "dwarf2",
   NORMAL_FRAME,
+  FRAME_UNWIND_DEBUGINFO,
   dwarf2_frame_unwind_stop_reason,
   dwarf2_frame_this_id,
   dwarf2_frame_prev_register,
@@ -1356,6 +1357,7 @@ static const struct frame_unwind dwarf2_signal_frame_unwind =
 {
   "dwarf2 signal",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_DEBUGINFO,
   dwarf2_frame_unwind_stop_reason,
   dwarf2_frame_this_id,
   dwarf2_frame_prev_register,
diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
index 27d84981d89..5af5085b038 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -31,10 +31,22 @@
 #include "cli/cli-cmds.h"
 #include "inferior.h"
 
+#include <map>
+
 /* If an unwinder should be prepended to the list, this is the
    index in which it should be inserted.  */
 static int osabi_start = -1;
 
+/* Conversion list between the enum for frame_unwind_class and
+   string.  */
+static std::map<enum frame_unwind_class, std::string> unwind_class_conversion =
+{
+  {FRAME_UNWIND_GDB, "FRAME_UNWIND_GDB"},
+  {FRAME_UNWIND_ARCH, "FRAME_UNWIND_ARCH"},
+  {FRAME_UNWIND_EXTENSION, "FRAME_UNWIND_EXTENSION"},
+  {FRAME_UNWIND_DEBUGINFO, "FRAME_UNWIND_DEBUGINFO"},
+};
+
 /* Start the table out with a few default sniffers.  OSABI code
    can't override this.  */
 static void
@@ -68,6 +80,14 @@ get_frame_unwind_table (struct gdbarch *gdbarch)
   return table;
 }
 
+static const char *
+frame_unwinder_class_str (frame_unwind_class uclass)
+{
+  auto location = unwind_class_conversion.find (uclass);
+  gdb_assert (location != unwind_class_conversion.end ());
+  return location->second.c_str ();
+}
+
 void
 frame_unwind_prepend_unwinder (struct gdbarch *gdbarch,
 				const struct frame_unwind *unwinder)
@@ -324,19 +344,22 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
   std::vector<const frame_unwind*> table = get_frame_unwind_table (gdbarch);
 
   ui_out *uiout = current_uiout;
-  ui_out_emit_table table_emitter (uiout, 2, -1, "FrameUnwinders");
+  ui_out_emit_table table_emitter (uiout, 3, -1, "FrameUnwinders");
   uiout->table_header (27, ui_left, "name", "Name");
   uiout->table_header (25, ui_left, "type", "Type");
+  uiout->table_header (25, ui_left, "class", "Class");
   uiout->table_body ();
 
   for (const struct frame_unwind* unwinder: table)
     {
       const char *name = unwinder->name;
       const char *type = frame_type_str (unwinder->type);
+      const char *uclass = frame_unwinder_class_str (unwinder->unwinder_class);
 
       ui_out_emit_list tuple_emitter (uiout, nullptr);
       uiout->field_string ("name", name);
       uiout->field_string ("type", type);
+      uiout->field_string ("class", uclass);
       uiout->text ("\n");
     }
 }
diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
index 53fcd6869e9..fd70949df8f 100644
--- a/gdb/frame-unwind.h
+++ b/gdb/frame-unwind.h
@@ -156,12 +156,23 @@ typedef void (frame_dealloc_cache_ftype) (frame_info *self,
 typedef gdbarch *(frame_prev_arch_ftype) (const frame_info_ptr &this_frame,
 					  void **this_prologue_cache);
 
+enum frame_unwind_class {
+  FRAME_UNWIND_GDB,
+  FRAME_UNWIND_EXTENSION,
+  FRAME_UNWIND_DEBUGINFO,
+  FRAME_UNWIND_ARCH,
+};
+
 struct frame_unwind
 {
   const char *name;
   /* The frame's type.  Should this instead be a collection of
      predicates that test the frame for various attributes?  */
   enum frame_type type;
+  /* What kind of unwinder is this.  It generally follows from where
+     the unwinder was added or where it looks for information to do the
+     unwinding.  */
+  enum frame_unwind_class unwinder_class;
   /* Should an attribute indicating the frame's address-in-block go
      here?  */
   frame_unwind_stop_reason_ftype *stop_reason;
diff --git a/gdb/frv-linux-tdep.c b/gdb/frv-linux-tdep.c
index 5160c3b0c1d..f80e5f0d089 100644
--- a/gdb/frv-linux-tdep.c
+++ b/gdb/frv-linux-tdep.c
@@ -336,6 +336,7 @@ static const struct frame_unwind frv_linux_sigtramp_frame_unwind =
 {
   "frv linux sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   frv_linux_sigtramp_frame_this_id,
   frv_linux_sigtramp_frame_prev_register,
diff --git a/gdb/frv-tdep.c b/gdb/frv-tdep.c
index bcb2c621945..216ccde01b0 100644
--- a/gdb/frv-tdep.c
+++ b/gdb/frv-tdep.c
@@ -1408,6 +1408,7 @@ frv_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind frv_frame_unwind = {
   "frv prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   frv_frame_this_id,
   frv_frame_prev_register,
diff --git a/gdb/ft32-tdep.c b/gdb/ft32-tdep.c
index 60cd2c974a1..8ec2668906f 100644
--- a/gdb/ft32-tdep.c
+++ b/gdb/ft32-tdep.c
@@ -528,6 +528,7 @@ static const struct frame_unwind ft32_frame_unwind =
 {
   "ft32 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   ft32_frame_this_id,
   ft32_frame_prev_register,
diff --git a/gdb/h8300-tdep.c b/gdb/h8300-tdep.c
index 36cbd9d4aa6..3a739c24e79 100644
--- a/gdb/h8300-tdep.c
+++ b/gdb/h8300-tdep.c
@@ -503,6 +503,7 @@ h8300_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
 static const struct frame_unwind h8300_frame_unwind = {
   "h8300 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   h8300_frame_this_id,
   h8300_frame_prev_register,
diff --git a/gdb/hppa-linux-tdep.c b/gdb/hppa-linux-tdep.c
index db0c3680131..d44735bc523 100644
--- a/gdb/hppa-linux-tdep.c
+++ b/gdb/hppa-linux-tdep.c
@@ -311,6 +311,7 @@ hppa_linux_sigtramp_frame_sniffer (const struct frame_unwind *self,
 static const struct frame_unwind hppa_linux_sigtramp_frame_unwind = {
   "hppa linux sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   hppa_linux_sigtramp_frame_this_id,
   hppa_linux_sigtramp_frame_prev_register,
diff --git a/gdb/hppa-tdep.c b/gdb/hppa-tdep.c
index 30128bafac4..5a66fe88f45 100644
--- a/gdb/hppa-tdep.c
+++ b/gdb/hppa-tdep.c
@@ -2287,6 +2287,7 @@ static const struct frame_unwind hppa_frame_unwind =
 {
   "hppa unwind table",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   hppa_frame_this_id,
   hppa_frame_prev_register,
@@ -2400,6 +2401,7 @@ static const struct frame_unwind hppa_fallback_frame_unwind =
 {
   "hppa prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   hppa_fallback_frame_this_id,
   hppa_fallback_frame_prev_register,
@@ -2481,6 +2483,7 @@ hppa_stub_unwind_sniffer (const struct frame_unwind *self,
 static const struct frame_unwind hppa_stub_frame_unwind = {
   "hppa stub",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   hppa_stub_frame_this_id,
   hppa_stub_frame_prev_register,
diff --git a/gdb/i386-obsd-tdep.c b/gdb/i386-obsd-tdep.c
index e1480ec6cc4..1f23c2e7d96 100644
--- a/gdb/i386-obsd-tdep.c
+++ b/gdb/i386-obsd-tdep.c
@@ -396,6 +396,7 @@ static const struct frame_unwind i386obsd_trapframe_unwind = {
      frame, but SIGTRAMP_FRAME would print <signal handler called>,
      which really is not what we want here.  */
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   i386obsd_trapframe_this_id,
   i386obsd_trapframe_prev_register,
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index b487a3f7478..ede74a4be70 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -2200,6 +2200,7 @@ static const struct frame_unwind i386_frame_unwind =
 {
   "i386 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   i386_frame_unwind_stop_reason,
   i386_frame_this_id,
   i386_frame_prev_register,
@@ -2355,6 +2356,7 @@ static const struct frame_unwind i386_epilogue_override_frame_unwind =
 {
   "i386 epilogue override",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   i386_epilogue_frame_unwind_stop_reason,
   i386_epilogue_frame_this_id,
   i386_epilogue_frame_prev_register,
@@ -2366,6 +2368,7 @@ static const struct frame_unwind i386_epilogue_frame_unwind =
 {
   "i386 epilogue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   i386_epilogue_frame_unwind_stop_reason,
   i386_epilogue_frame_this_id,
   i386_epilogue_frame_prev_register,
@@ -2448,6 +2451,7 @@ static const struct frame_unwind i386_stack_tramp_frame_unwind =
 {
   "i386 stack tramp",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   i386_epilogue_frame_unwind_stop_reason,
   i386_epilogue_frame_this_id,
   i386_epilogue_frame_prev_register,
@@ -2597,6 +2601,7 @@ static const struct frame_unwind i386_sigtramp_frame_unwind =
 {
   "i386 sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   i386_sigtramp_frame_unwind_stop_reason,
   i386_sigtramp_frame_this_id,
   i386_sigtramp_frame_prev_register,
diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c
index 88f67ccf096..a75ac192a25 100644
--- a/gdb/ia64-tdep.c
+++ b/gdb/ia64-tdep.c
@@ -2166,6 +2166,7 @@ static const struct frame_unwind ia64_frame_unwind =
 {
   "ia64 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   &ia64_frame_this_id,
   &ia64_frame_prev_register,
@@ -2355,6 +2356,7 @@ static const struct frame_unwind ia64_sigtramp_frame_unwind =
 {
   "ia64 sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   ia64_sigtramp_frame_this_id,
   ia64_sigtramp_frame_prev_register,
@@ -3015,6 +3017,7 @@ static const struct frame_unwind ia64_libunwind_frame_unwind =
 {
   "ia64 libunwind",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   ia64_libunwind_frame_this_id,
   ia64_libunwind_frame_prev_register,
@@ -3104,6 +3107,7 @@ static const struct frame_unwind ia64_libunwind_sigtramp_frame_unwind =
 {
   "ia64 libunwind sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   ia64_libunwind_sigtramp_frame_this_id,
   ia64_libunwind_sigtramp_frame_prev_register,
diff --git a/gdb/inline-frame.c b/gdb/inline-frame.c
index 9f3deef8f03..1a0352a35c1 100644
--- a/gdb/inline-frame.c
+++ b/gdb/inline-frame.c
@@ -268,6 +268,7 @@ inline_frame_sniffer (const struct frame_unwind *self,
 const struct frame_unwind inline_frame_unwind = {
   "inline",
   INLINE_FRAME,
+  FRAME_UNWIND_GDB,
   default_frame_unwind_stop_reason,
   inline_frame_this_id,
   inline_frame_prev_register,
diff --git a/gdb/iq2000-tdep.c b/gdb/iq2000-tdep.c
index 4177de059c0..698284597d8 100644
--- a/gdb/iq2000-tdep.c
+++ b/gdb/iq2000-tdep.c
@@ -427,6 +427,7 @@ iq2000_frame_this_id (const frame_info_ptr &this_frame, void **this_cache,
 static const struct frame_unwind iq2000_frame_unwind = {
   "iq2000 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   iq2000_frame_this_id,
   iq2000_frame_prev_register,
diff --git a/gdb/jit.c b/gdb/jit.c
index a7fd5c40f9d..16c3111fbc7 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -1108,6 +1108,7 @@ static const struct frame_unwind jit_frame_unwind =
 {
   "jit",
   NORMAL_FRAME,
+  FRAME_UNWIND_EXTENSION,
   default_frame_unwind_stop_reason,
   jit_frame_this_id,
   jit_frame_prev_register,
diff --git a/gdb/lm32-tdep.c b/gdb/lm32-tdep.c
index b4605e48c6a..9cfc81c429b 100644
--- a/gdb/lm32-tdep.c
+++ b/gdb/lm32-tdep.c
@@ -450,6 +450,7 @@ lm32_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind lm32_frame_unwind = {
   "lm32 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   lm32_frame_this_id,
   lm32_frame_prev_register,
diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c
index 0b38e751d16..e9d3f156125 100644
--- a/gdb/loongarch-tdep.c
+++ b/gdb/loongarch-tdep.c
@@ -453,6 +453,7 @@ loongarch_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind loongarch_frame_unwind = {
   "loongarch prologue",
   /*.type	   =*/NORMAL_FRAME,
+  /*.unwinder_class=*/FRAME_UNWIND_ARCH,
   /*.stop_reason   =*/default_frame_unwind_stop_reason,
   /*.this_id	   =*/loongarch_frame_this_id,
   /*.prev_register =*/loongarch_frame_prev_register,
diff --git a/gdb/m32c-tdep.c b/gdb/m32c-tdep.c
index 0895f7bd0c4..748465256e3 100644
--- a/gdb/m32c-tdep.c
+++ b/gdb/m32c-tdep.c
@@ -1958,6 +1958,7 @@ m32c_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind m32c_unwind = {
   "m32c prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   m32c_this_id,
   m32c_prev_register,
diff --git a/gdb/m32r-linux-tdep.c b/gdb/m32r-linux-tdep.c
index b74d2f3f161..87dce624a4a 100644
--- a/gdb/m32r-linux-tdep.c
+++ b/gdb/m32r-linux-tdep.c
@@ -304,6 +304,7 @@ m32r_linux_sigtramp_frame_sniffer (const struct frame_unwind *self,
 static const struct frame_unwind m32r_linux_sigtramp_frame_unwind = {
   "m32r linux sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   m32r_linux_sigtramp_frame_this_id,
   m32r_linux_sigtramp_frame_prev_register,
diff --git a/gdb/m32r-tdep.c b/gdb/m32r-tdep.c
index 0bd6b499e6f..9b4af6a7248 100644
--- a/gdb/m32r-tdep.c
+++ b/gdb/m32r-tdep.c
@@ -834,6 +834,7 @@ m32r_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind m32r_frame_unwind = {
   "m32r prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   m32r_frame_this_id,
   m32r_frame_prev_register,
diff --git a/gdb/m68hc11-tdep.c b/gdb/m68hc11-tdep.c
index d80b190f2c0..bcf9861ccd5 100644
--- a/gdb/m68hc11-tdep.c
+++ b/gdb/m68hc11-tdep.c
@@ -939,6 +939,7 @@ m68hc11_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind m68hc11_frame_unwind = {
   "m68hc11 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   m68hc11_frame_this_id,
   m68hc11_frame_prev_register,
diff --git a/gdb/m68k-linux-tdep.c b/gdb/m68k-linux-tdep.c
index eca4ede099f..86c700dafe6 100644
--- a/gdb/m68k-linux-tdep.c
+++ b/gdb/m68k-linux-tdep.c
@@ -318,6 +318,7 @@ static const struct frame_unwind m68k_linux_sigtramp_frame_unwind =
 {
   "m68k linux sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   m68k_linux_sigtramp_frame_this_id,
   m68k_linux_sigtramp_frame_prev_register,
diff --git a/gdb/m68k-tdep.c b/gdb/m68k-tdep.c
index abfc605aabb..23254dcfac0 100644
--- a/gdb/m68k-tdep.c
+++ b/gdb/m68k-tdep.c
@@ -1011,6 +1011,7 @@ static const struct frame_unwind m68k_frame_unwind =
 {
   "m68k prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   m68k_frame_this_id,
   m68k_frame_prev_register,
diff --git a/gdb/mep-tdep.c b/gdb/mep-tdep.c
index f455d3eb51d..392c7231535 100644
--- a/gdb/mep-tdep.c
+++ b/gdb/mep-tdep.c
@@ -2064,6 +2064,7 @@ mep_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind mep_frame_unwind = {
   "mep prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   mep_frame_this_id,
   mep_frame_prev_register,
diff --git a/gdb/microblaze-tdep.c b/gdb/microblaze-tdep.c
index 07261bd4d22..7dc209c680d 100644
--- a/gdb/microblaze-tdep.c
+++ b/gdb/microblaze-tdep.c
@@ -482,6 +482,7 @@ static const struct frame_unwind microblaze_frame_unwind =
 {
   "microblaze prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   microblaze_frame_this_id,
   microblaze_frame_prev_register,
diff --git a/gdb/mips-sde-tdep.c b/gdb/mips-sde-tdep.c
index 7b367898f04..9f16d2d8593 100644
--- a/gdb/mips-sde-tdep.c
+++ b/gdb/mips-sde-tdep.c
@@ -165,6 +165,7 @@ static const struct frame_unwind mips_sde_frame_unwind =
 {
   "mips sde sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   mips_sde_frame_this_id,
   mips_sde_frame_prev_register,
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index 8339d2f1d4c..a8d2cea8cb7 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -2933,6 +2933,7 @@ static const struct frame_unwind mips_insn16_frame_unwind =
 {
   "mips insn16 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   mips_insn16_frame_this_id,
   mips_insn16_frame_prev_register,
@@ -3369,6 +3370,7 @@ static const struct frame_unwind mips_micro_frame_unwind =
 {
   "mips micro prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   mips_micro_frame_this_id,
   mips_micro_frame_prev_register,
@@ -3748,6 +3750,7 @@ static const struct frame_unwind mips_insn32_frame_unwind =
 {
   "mips insn32 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   mips_insn32_frame_this_id,
   mips_insn32_frame_prev_register,
@@ -3865,6 +3868,7 @@ static const struct frame_unwind mips_stub_frame_unwind =
 {
   "mips stub",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   mips_stub_frame_this_id,
   mips_stub_frame_prev_register,
diff --git a/gdb/mn10300-tdep.c b/gdb/mn10300-tdep.c
index f8c5a0906ef..b8b290d1b87 100644
--- a/gdb/mn10300-tdep.c
+++ b/gdb/mn10300-tdep.c
@@ -1130,6 +1130,7 @@ mn10300_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind mn10300_frame_unwind = {
   "mn10300 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   mn10300_frame_this_id, 
   mn10300_frame_prev_register,
diff --git a/gdb/moxie-tdep.c b/gdb/moxie-tdep.c
index 812c92d1e0a..124275de8e2 100644
--- a/gdb/moxie-tdep.c
+++ b/gdb/moxie-tdep.c
@@ -588,6 +588,7 @@ moxie_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind moxie_frame_unwind = {
   "moxie prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   moxie_frame_this_id,
   moxie_frame_prev_register,
diff --git a/gdb/msp430-tdep.c b/gdb/msp430-tdep.c
index e07e3cc4be2..05830de63b0 100644
--- a/gdb/msp430-tdep.c
+++ b/gdb/msp430-tdep.c
@@ -545,6 +545,7 @@ msp430_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind msp430_unwind = {
   "msp430 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   msp430_this_id,
   msp430_prev_register,
diff --git a/gdb/nds32-tdep.c b/gdb/nds32-tdep.c
index e453cd0f6d9..f2653400ab3 100644
--- a/gdb/nds32-tdep.c
+++ b/gdb/nds32-tdep.c
@@ -992,6 +992,7 @@ static const struct frame_unwind nds32_frame_unwind =
 {
   "nds32 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   nds32_frame_this_id,
   nds32_frame_prev_register,
@@ -1376,6 +1377,7 @@ static const struct frame_unwind nds32_epilogue_frame_unwind =
 {
   "nds32 epilogue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   nds32_epilogue_frame_this_id,
   nds32_epilogue_frame_prev_register,
diff --git a/gdb/nios2-tdep.c b/gdb/nios2-tdep.c
index 0a8eeb88bb5..4b5b7239366 100644
--- a/gdb/nios2-tdep.c
+++ b/gdb/nios2-tdep.c
@@ -1981,6 +1981,7 @@ static const struct frame_unwind nios2_frame_unwind =
 {
   "nios2 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   nios2_frame_this_id,
   nios2_frame_prev_register,
@@ -2082,6 +2083,7 @@ static const struct frame_unwind nios2_stub_frame_unwind =
 {
   "nios2 stub",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   nios2_stub_frame_this_id,
   nios2_stub_frame_prev_register,
diff --git a/gdb/or1k-tdep.c b/gdb/or1k-tdep.c
index ffcb50f4bcd..681877fdb18 100644
--- a/gdb/or1k-tdep.c
+++ b/gdb/or1k-tdep.c
@@ -1128,6 +1128,7 @@ or1k_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind or1k_frame_unwind = {
   "or1k prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   or1k_frame_this_id,
   or1k_frame_prev_register,
diff --git a/gdb/ppc-fbsd-tdep.c b/gdb/ppc-fbsd-tdep.c
index 719ddc71d13..a9576231c66 100644
--- a/gdb/ppc-fbsd-tdep.c
+++ b/gdb/ppc-fbsd-tdep.c
@@ -265,6 +265,7 @@ ppcfbsd_sigtramp_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind ppcfbsd_sigtramp_frame_unwind = {
   "ppc freebsd sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   ppcfbsd_sigtramp_frame_this_id,
   ppcfbsd_sigtramp_frame_prev_register,
diff --git a/gdb/ppc-obsd-tdep.c b/gdb/ppc-obsd-tdep.c
index b845e67bcaa..c5e08091547 100644
--- a/gdb/ppc-obsd-tdep.c
+++ b/gdb/ppc-obsd-tdep.c
@@ -234,6 +234,7 @@ ppcobsd_sigtramp_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind ppcobsd_sigtramp_frame_unwind = {
   "ppc openbsd sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   ppcobsd_sigtramp_frame_this_id,
   ppcobsd_sigtramp_frame_prev_register,
diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c
index 56f925bc57f..6268c475777 100644
--- a/gdb/python/py-unwind.c
+++ b/gdb/python/py-unwind.c
@@ -965,6 +965,7 @@ pyuw_on_new_gdbarch (gdbarch *newarch)
 
       unwinder->name = "python";
       unwinder->type = NORMAL_FRAME;
+      unwinder->unwinder_class = FRAME_UNWIND_EXTENSION;
       unwinder->stop_reason = default_frame_unwind_stop_reason;
       unwinder->this_id = pyuw_this_id;
       unwinder->prev_register = pyuw_prev_register;
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 6350400c318..b6011c86c85 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -1898,6 +1898,7 @@ const struct frame_unwind record_btrace_frame_unwind =
 {
   "record-btrace",
   NORMAL_FRAME,
+  FRAME_UNWIND_GDB,
   record_btrace_frame_unwind_stop_reason,
   record_btrace_frame_this_id,
   record_btrace_frame_prev_register,
@@ -1910,6 +1911,7 @@ const struct frame_unwind record_btrace_tailcall_frame_unwind =
 {
   "record-btrace tailcall",
   TAILCALL_FRAME,
+  FRAME_UNWIND_GDB,
   record_btrace_frame_unwind_stop_reason,
   record_btrace_frame_this_id,
   record_btrace_frame_prev_register,
diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
index 68ef3233e8d..7af747a5161 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -3905,6 +3905,7 @@ static const struct frame_unwind riscv_frame_unwind =
 {
   /*.name          =*/ "riscv prologue",
   /*.type          =*/ NORMAL_FRAME,
+  /*.unwinder_class=*/FRAME_UNWIND_ARCH,
   /*.stop_reason   =*/ default_frame_unwind_stop_reason,
   /*.this_id       =*/ riscv_frame_this_id,
   /*.prev_register =*/ riscv_frame_prev_register,
diff --git a/gdb/rl78-tdep.c b/gdb/rl78-tdep.c
index 1e439f36356..00fa3ad301e 100644
--- a/gdb/rl78-tdep.c
+++ b/gdb/rl78-tdep.c
@@ -1187,6 +1187,7 @@ static const struct frame_unwind rl78_unwind =
 {
   "rl78 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   rl78_this_id,
   rl78_prev_register,
diff --git a/gdb/rs6000-aix-tdep.c b/gdb/rs6000-aix-tdep.c
index db76af6984b..78d6f5e8a19 100644
--- a/gdb/rs6000-aix-tdep.c
+++ b/gdb/rs6000-aix-tdep.c
@@ -331,6 +331,7 @@ aix_sighandle_frame_sniffer (const struct frame_unwind *self,
 static const struct frame_unwind aix_sighandle_frame_unwind = {
   "rs6000 aix sighandle",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   aix_sighandle_frame_this_id,
   aix_sighandle_frame_prev_register,
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index c8450345be2..c3551be5248 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -3842,6 +3842,7 @@ static const struct frame_unwind rs6000_frame_unwind =
 {
   "rs6000 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   rs6000_frame_this_id,
   rs6000_frame_prev_register,
@@ -3983,6 +3984,7 @@ static const struct frame_unwind rs6000_epilogue_frame_unwind =
 {
   "rs6000 epilogue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   rs6000_epilogue_frame_this_id, rs6000_epilogue_frame_prev_register,
   NULL,
diff --git a/gdb/rx-tdep.c b/gdb/rx-tdep.c
index 53ad5d5c92b..d959dd1e327 100644
--- a/gdb/rx-tdep.c
+++ b/gdb/rx-tdep.c
@@ -634,6 +634,7 @@ rx_exception_sniffer (const struct frame_unwind *self,
 static const struct frame_unwind rx_frame_unwind = {
   "rx prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   rx_frame_this_id,
   rx_frame_prev_register,
@@ -648,6 +649,7 @@ static const struct frame_unwind rx_exception_unwind = {
   "rx exception",
   /* SIGTRAMP_FRAME could be used here, but backtraces are less informative.  */
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   rx_frame_this_id,
   rx_frame_prev_register,
diff --git a/gdb/s12z-tdep.c b/gdb/s12z-tdep.c
index 60cbc26afff..7f7865a8022 100644
--- a/gdb/s12z-tdep.c
+++ b/gdb/s12z-tdep.c
@@ -445,6 +445,7 @@ s12z_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind s12z_frame_unwind = {
   "s12z prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   s12z_frame_this_id,
   s12z_frame_prev_register,
diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
index 945b3c7cee7..8b3df082063 100644
--- a/gdb/s390-linux-tdep.c
+++ b/gdb/s390-linux-tdep.c
@@ -545,6 +545,7 @@ s390_sigtramp_frame_sniffer (const struct frame_unwind *self,
 static const struct frame_unwind s390_sigtramp_frame_unwind = {
   "s390 linux sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   s390_sigtramp_frame_this_id,
   s390_sigtramp_frame_prev_register,
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index 75983550a5c..d7fa9e11fad 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -2649,6 +2649,7 @@ s390_frame_prev_register (const frame_info_ptr &this_frame,
 static const struct frame_unwind s390_frame_unwind = {
   "s390 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   s390_frame_this_id,
   s390_frame_prev_register,
@@ -2743,6 +2744,7 @@ s390_stub_frame_sniffer (const struct frame_unwind *self,
 static const struct frame_unwind s390_stub_frame_unwind = {
   "s390 stub",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   s390_stub_frame_this_id,
   s390_stub_frame_prev_register,
diff --git a/gdb/sentinel-frame.c b/gdb/sentinel-frame.c
index ca7c28952f3..42e5f66bf2a 100644
--- a/gdb/sentinel-frame.c
+++ b/gdb/sentinel-frame.c
@@ -83,6 +83,7 @@ const struct frame_unwind sentinel_frame_unwind =
 {
   "sentinel",
   SENTINEL_FRAME,
+  FRAME_UNWIND_GDB,
   default_frame_unwind_stop_reason,
   sentinel_frame_this_id,
   sentinel_frame_prev_register,
diff --git a/gdb/sh-tdep.c b/gdb/sh-tdep.c
index 16e513eb405..60e717c73c4 100644
--- a/gdb/sh-tdep.c
+++ b/gdb/sh-tdep.c
@@ -1932,6 +1932,7 @@ sh_frame_this_id (const frame_info_ptr &this_frame, void **this_cache,
 static const struct frame_unwind sh_frame_unwind = {
   "sh prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   sh_frame_this_id,
   sh_frame_prev_register,
@@ -1999,6 +2000,7 @@ static const struct frame_unwind sh_stub_unwind =
 {
   "sh stub",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   sh_stub_this_id,
   sh_frame_prev_register,
diff --git a/gdb/sparc-netbsd-tdep.c b/gdb/sparc-netbsd-tdep.c
index d2b1a4bfd14..5861bf7b836 100644
--- a/gdb/sparc-netbsd-tdep.c
+++ b/gdb/sparc-netbsd-tdep.c
@@ -253,6 +253,7 @@ static const struct frame_unwind sparc32nbsd_sigcontext_frame_unwind =
 {
   "sparc32 netbsd sigcontext",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   sparc32nbsd_sigcontext_frame_this_id,
   sparc32nbsd_sigcontext_frame_prev_register,
diff --git a/gdb/sparc-obsd-tdep.c b/gdb/sparc-obsd-tdep.c
index 8cdbc8aae6e..9ffb86fe3dc 100644
--- a/gdb/sparc-obsd-tdep.c
+++ b/gdb/sparc-obsd-tdep.c
@@ -138,6 +138,7 @@ static const struct frame_unwind sparc32obsd_sigtramp_frame_unwind =
 {
   "sparc32 openbsd sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   sparc32obsd_sigtramp_frame_this_id,
   sparc32obsd_sigtramp_frame_prev_register,
diff --git a/gdb/sparc-sol2-tdep.c b/gdb/sparc-sol2-tdep.c
index 52578440103..2c2f3fb74ee 100644
--- a/gdb/sparc-sol2-tdep.c
+++ b/gdb/sparc-sol2-tdep.c
@@ -184,6 +184,7 @@ static const struct frame_unwind sparc32_sol2_sigtramp_frame_unwind =
 {
   "sparc32 solaris sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   sparc32_sol2_sigtramp_frame_this_id,
   sparc32_sol2_sigtramp_frame_prev_register,
diff --git a/gdb/sparc-tdep.c b/gdb/sparc-tdep.c
index 5eec3615f53..d26e630b274 100644
--- a/gdb/sparc-tdep.c
+++ b/gdb/sparc-tdep.c
@@ -1350,6 +1350,7 @@ static const struct frame_unwind sparc32_frame_unwind =
 {
   "sparc32 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   sparc32_frame_this_id,
   sparc32_frame_prev_register,
diff --git a/gdb/sparc64-fbsd-tdep.c b/gdb/sparc64-fbsd-tdep.c
index c992c8c8f6f..aa5eaf7563e 100644
--- a/gdb/sparc64-fbsd-tdep.c
+++ b/gdb/sparc64-fbsd-tdep.c
@@ -201,6 +201,7 @@ static const struct frame_unwind sparc64fbsd_sigtramp_frame_unwind =
 {
   "sparc64 freebsd sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   sparc64fbsd_sigtramp_frame_this_id,
   sparc64fbsd_sigtramp_frame_prev_register,
diff --git a/gdb/sparc64-netbsd-tdep.c b/gdb/sparc64-netbsd-tdep.c
index fe97689ae15..16c5b07fbbb 100644
--- a/gdb/sparc64-netbsd-tdep.c
+++ b/gdb/sparc64-netbsd-tdep.c
@@ -227,6 +227,7 @@ static const struct frame_unwind sparc64nbsd_sigcontext_frame_unwind =
 {
   "sparc64 netbsd sigcontext",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   sparc64nbsd_sigcontext_frame_this_id,
   sparc64nbsd_sigcontext_frame_prev_register,
diff --git a/gdb/sparc64-obsd-tdep.c b/gdb/sparc64-obsd-tdep.c
index 3837074a9fb..50611742726 100644
--- a/gdb/sparc64-obsd-tdep.c
+++ b/gdb/sparc64-obsd-tdep.c
@@ -224,6 +224,7 @@ static const struct frame_unwind sparc64obsd_frame_unwind =
 {
   "sparc64 openbsd sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   sparc64obsd_frame_this_id,
   sparc64obsd_frame_prev_register,
@@ -308,6 +309,7 @@ static const struct frame_unwind sparc64obsd_trapframe_unwind =
 {
   "sparc64 openbsd trap",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   sparc64obsd_trapframe_this_id,
   sparc64obsd_trapframe_prev_register,
diff --git a/gdb/sparc64-sol2-tdep.c b/gdb/sparc64-sol2-tdep.c
index 7cef98bb681..e2cd7548355 100644
--- a/gdb/sparc64-sol2-tdep.c
+++ b/gdb/sparc64-sol2-tdep.c
@@ -187,6 +187,7 @@ static const struct frame_unwind sparc64_sol2_sigtramp_frame_unwind =
 {
   "sparc64 solaris sigtramp",
   SIGTRAMP_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   sparc64_sol2_sigtramp_frame_this_id,
   sparc64_sol2_sigtramp_frame_prev_register,
diff --git a/gdb/sparc64-tdep.c b/gdb/sparc64-tdep.c
index 8a90dd00d16..b7036464191 100644
--- a/gdb/sparc64-tdep.c
+++ b/gdb/sparc64-tdep.c
@@ -1138,6 +1138,7 @@ static const struct frame_unwind sparc64_frame_unwind =
 {
   "sparc64 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   sparc64_frame_this_id,
   sparc64_frame_prev_register,
diff --git a/gdb/tic6x-tdep.c b/gdb/tic6x-tdep.c
index aa115095df7..7fc58d4fc61 100644
--- a/gdb/tic6x-tdep.c
+++ b/gdb/tic6x-tdep.c
@@ -456,6 +456,7 @@ static const struct frame_unwind tic6x_frame_unwind =
 {
   "tic6x prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   tic6x_frame_this_id,
   tic6x_frame_prev_register,
@@ -519,6 +520,7 @@ static const struct frame_unwind tic6x_stub_unwind =
 {
   "tic6x stub",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   tic6x_stub_this_id,
   tic6x_frame_prev_register,
diff --git a/gdb/tilegx-tdep.c b/gdb/tilegx-tdep.c
index c25933be558..d0d0de79909 100644
--- a/gdb/tilegx-tdep.c
+++ b/gdb/tilegx-tdep.c
@@ -903,6 +903,7 @@ tilegx_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
 static const struct frame_unwind tilegx_frame_unwind = {
   "tilegx prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   tilegx_frame_this_id,
   tilegx_frame_prev_register,
diff --git a/gdb/tramp-frame.c b/gdb/tramp-frame.c
index 0e730e66b58..fba0c669489 100644
--- a/gdb/tramp-frame.c
+++ b/gdb/tramp-frame.c
@@ -166,6 +166,7 @@ tramp_frame_prepend_unwinder (struct gdbarch *gdbarch,
   data->tramp_frame = tramp_frame;
   unwinder->type = tramp_frame->frame_type;
   unwinder->unwind_data = data;
+  unwinder->unwinder_class = FRAME_UNWIND_GDB;
   unwinder->sniffer = tramp_frame_sniffer;
   unwinder->stop_reason = default_frame_unwind_stop_reason;
   unwinder->this_id = tramp_frame_this_id;
diff --git a/gdb/v850-tdep.c b/gdb/v850-tdep.c
index 4c1fd383a84..66cb5004058 100644
--- a/gdb/v850-tdep.c
+++ b/gdb/v850-tdep.c
@@ -1323,6 +1323,7 @@ v850_frame_this_id (const frame_info_ptr &this_frame, void **this_cache,
 static const struct frame_unwind v850_frame_unwind = {
   "v850 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   v850_frame_this_id,
   v850_frame_prev_register,
diff --git a/gdb/vax-tdep.c b/gdb/vax-tdep.c
index 125f3423be5..17d36a545fb 100644
--- a/gdb/vax-tdep.c
+++ b/gdb/vax-tdep.c
@@ -390,6 +390,7 @@ static const struct frame_unwind vax_frame_unwind =
 {
   "vax prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   vax_frame_this_id,
   vax_frame_prev_register,
diff --git a/gdb/xstormy16-tdep.c b/gdb/xstormy16-tdep.c
index 12b16c2d3d3..46412737039 100644
--- a/gdb/xstormy16-tdep.c
+++ b/gdb/xstormy16-tdep.c
@@ -731,6 +731,7 @@ xstormy16_frame_base_address (const frame_info_ptr &this_frame, void **this_cach
 static const struct frame_unwind xstormy16_frame_unwind = {
   "xstormy16 prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   xstormy16_frame_this_id,
   xstormy16_frame_prev_register,
diff --git a/gdb/xtensa-tdep.c b/gdb/xtensa-tdep.c
index d116b34fd81..728774f955a 100644
--- a/gdb/xtensa-tdep.c
+++ b/gdb/xtensa-tdep.c
@@ -1501,6 +1501,7 @@ xtensa_unwind =
 {
   "xtensa prologue",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   xtensa_frame_this_id,
   xtensa_frame_prev_register,
diff --git a/gdb/z80-tdep.c b/gdb/z80-tdep.c
index f2d000f5303..4404d4bbd59 100644
--- a/gdb/z80-tdep.c
+++ b/gdb/z80-tdep.c
@@ -1068,6 +1068,7 @@ z80_frame_unwind =
 {
   "z80",
   NORMAL_FRAME,
+  FRAME_UNWIND_ARCH,
   default_frame_unwind_stop_reason,
   z80_frame_this_id,
   z80_frame_prev_register,
-- 
2.43.2


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 3/4] gdb: Migrate frame unwinders to use C++ classes
  2024-03-06 12:51 [PATCH 0/4] Modernize frame unwinders and add disable feature Guinevere Larsen
  2024-03-06 12:51 ` [PATCH 1/4] gdb: make gdbarch store a vector of frame unwinders Guinevere Larsen
  2024-03-06 12:51 ` [PATCH 2/4] gdb: add "unwinder class" to " Guinevere Larsen
@ 2024-03-06 12:51 ` Guinevere Larsen
  2024-03-07 11:01   ` Lancelot SIX
  2024-03-08 17:07   ` Tom Tromey
  2024-03-06 12:51 ` [PATCH 4/4] GDB: introduce ability to disable frame unwinders Guinevere Larsen
  2024-03-11 14:56 ` [PATCH 0/4] Modernize frame unwinders and add disable feature Luis Machado
  4 siblings, 2 replies; 23+ messages in thread
From: Guinevere Larsen @ 2024-03-06 12:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Guinevere Larsen

Frame unwinders have historically been a structure populated with
callback pointers, so that architectures (or other specific unwinders)
could install their own way to handle the inferior. However, since
moving to c++, we could use polymorphism to get the same functionality
in a more readable way. Polymorphism also makes it simpler to add new
functionality to all frame unwinders, since all that's required is
adding it to the base class.

As part of the changes to add support to disabling frame unwinders,
this commit makes the first baby step in  using polymorphism for the
frame unwinders, by making frame_unwind a virtual class, and adds a
couple of new classes. The main class added is frame_unwind_legacy,
which works the same as the previous structs, using function pointers
as callbacks. This class was added to allow the transition to happen
piecemeal. New unwinders should instead follow the lead of the other
classes implemented.

2 of the others, frame_unwind_python and frame_unwind_trampoline, were added
because it seemed simpler at the moment to do that instead of reworking
the dynamic allocation to work with the legacy class, and can be used as
an example to future implementations.

Finally, the cygwin unwinder was converted to a class since it was most
of the way there already.
---
 gdb/aarch64-tdep.c          |  10 +--
 gdb/alpha-mdebug-tdep.c     |   5 +-
 gdb/alpha-tdep.c            |  10 +--
 gdb/amd64-obsd-tdep.c       |   5 +-
 gdb/amd64-tdep.c            |  20 ++---
 gdb/amd64-windows-tdep.c    |   5 +-
 gdb/amdgpu-tdep.c           |   4 +-
 gdb/arc-tdep.c              |   8 +-
 gdb/arm-tdep.c              |  24 +++---
 gdb/avr-tdep.c              |   4 +-
 gdb/bfin-tdep.c             |   5 +-
 gdb/bpf-tdep.c              |   5 +-
 gdb/cris-tdep.c             |  10 +--
 gdb/csky-tdep.c             |   8 +-
 gdb/dummy-frame.c           |   7 +-
 gdb/dummy-frame.h           |   2 +-
 gdb/dwarf2/frame-tailcall.c |   5 +-
 gdb/dwarf2/frame-tailcall.h |   2 +-
 gdb/dwarf2/frame.c          |  14 ++--
 gdb/frame-unwind.c          |  80 +++++++++++++++++++-
 gdb/frame-unwind.h          | 143 +++++++++++++++++++++++++++++++++---
 gdb/frame.c                 |  28 +++----
 gdb/frv-linux-tdep.c        |   5 +-
 gdb/frv-tdep.c              |   4 +-
 gdb/ft32-tdep.c             |   5 +-
 gdb/h8300-tdep.c            |   4 +-
 gdb/hppa-linux-tdep.c       |   4 +-
 gdb/hppa-tdep.c             |  14 ++--
 gdb/i386-obsd-tdep.c        |   4 +-
 gdb/i386-tdep.c             |  25 +++----
 gdb/ia64-tdep.c             |  20 ++---
 gdb/inline-frame.c          |   4 +-
 gdb/inline-frame.h          |   2 +-
 gdb/iq2000-tdep.c           |   4 +-
 gdb/jit.c                   |   5 +-
 gdb/lm32-tdep.c             |   4 +-
 gdb/loongarch-tdep.c        |   6 +-
 gdb/m32c-tdep.c             |   4 +-
 gdb/m32r-linux-tdep.c       |   4 +-
 gdb/m32r-tdep.c             |   4 +-
 gdb/m68hc11-tdep.c          |   4 +-
 gdb/m68k-linux-tdep.c       |   5 +-
 gdb/m68k-tdep.c             |   5 +-
 gdb/mep-tdep.c              |   4 +-
 gdb/microblaze-tdep.c       |   5 +-
 gdb/mips-sde-tdep.c         |   5 +-
 gdb/mips-tdep.c             |  20 ++---
 gdb/mn10300-tdep.c          |   4 +-
 gdb/moxie-tdep.c            |   4 +-
 gdb/msp430-tdep.c           |   4 +-
 gdb/nds32-tdep.c            |  12 ++-
 gdb/nios2-tdep.c            |  10 +--
 gdb/or1k-tdep.c             |   6 +-
 gdb/ppc-fbsd-tdep.c         |   4 +-
 gdb/ppc-obsd-tdep.c         |   4 +-
 gdb/python/py-unwind.c      |  51 ++++++++++---
 gdb/record-btrace.c         |  10 +--
 gdb/record.h                |   4 +-
 gdb/riscv-tdep.c            |   7 +-
 gdb/rl78-tdep.c             |   5 +-
 gdb/rs6000-aix-tdep.c       |   4 +-
 gdb/rs6000-tdep.c           |  10 +--
 gdb/rx-tdep.c               |   8 +-
 gdb/s12z-tdep.c             |   6 +-
 gdb/s390-linux-tdep.c       |   4 +-
 gdb/s390-tdep.c             |   8 +-
 gdb/sentinel-frame.c        |   7 +-
 gdb/sentinel-frame.h        |   2 +-
 gdb/sh-tdep.c               |   9 +--
 gdb/sparc-netbsd-tdep.c     |   5 +-
 gdb/sparc-obsd-tdep.c       |   5 +-
 gdb/sparc-sol2-tdep.c       |   5 +-
 gdb/sparc-tdep.c            |   5 +-
 gdb/sparc64-fbsd-tdep.c     |   5 +-
 gdb/sparc64-netbsd-tdep.c   |   5 +-
 gdb/sparc64-obsd-tdep.c     |  10 +--
 gdb/sparc64-sol2-tdep.c     |   5 +-
 gdb/sparc64-tdep.c          |   5 +-
 gdb/tic6x-tdep.c            |  10 +--
 gdb/tilegx-tdep.c           |   4 +-
 gdb/tramp-frame.c           |  51 ++++++++++---
 gdb/v850-tdep.c             |   4 +-
 gdb/vax-tdep.c              |   5 +-
 gdb/windows-tdep.c          |  33 ++++-----
 gdb/windows-tdep.h          |  16 +++-
 gdb/xstormy16-tdep.c        |   4 +-
 gdb/xtensa-tdep.c           |   6 +-
 gdb/z80-tdep.c              |   6 +-
 88 files changed, 566 insertions(+), 380 deletions(-)

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index db0cf28d815..7248ca32c52 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -1204,8 +1204,7 @@ aarch64_prologue_prev_register (const frame_info_ptr &this_frame,
 }
 
 /* AArch64 prologue unwinder.  */
-static frame_unwind aarch64_prologue_unwind =
-{
+static frame_unwind_legacy aarch64_prologue_unwind (
   "aarch64 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1214,7 +1213,7 @@ static frame_unwind aarch64_prologue_unwind =
   aarch64_prologue_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Allocate and fill in *THIS_CACHE with information about the prologue of
    *THIS_FRAME.  Do not do this is if *THIS_CACHE was already allocated.
@@ -1300,8 +1299,7 @@ aarch64_stub_unwind_sniffer (const struct frame_unwind *self,
 }
 
 /* AArch64 stub unwinder.  */
-static frame_unwind aarch64_stub_unwind =
-{
+static frame_unwind_legacy aarch64_stub_unwind (
   "aarch64 stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1310,7 +1308,7 @@ static frame_unwind aarch64_stub_unwind =
   aarch64_prologue_prev_register,
   NULL,
   aarch64_stub_unwind_sniffer
-};
+);
 
 /* Return the frame base address of *THIS_FRAME.  */
 
diff --git a/gdb/alpha-mdebug-tdep.c b/gdb/alpha-mdebug-tdep.c
index bc7eb8562e9..2615260816b 100644
--- a/gdb/alpha-mdebug-tdep.c
+++ b/gdb/alpha-mdebug-tdep.c
@@ -331,8 +331,7 @@ alpha_mdebug_frame_sniffer (const struct frame_unwind *self,
   return 1;
 }
 
-static const struct frame_unwind alpha_mdebug_frame_unwind =
-{
+static const struct frame_unwind_legacy alpha_mdebug_frame_unwind (
   "alpha mdebug",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -341,7 +340,7 @@ static const struct frame_unwind alpha_mdebug_frame_unwind =
   alpha_mdebug_frame_prev_register,
   NULL,
   alpha_mdebug_frame_sniffer
-};
+);
 
 static CORE_ADDR
 alpha_mdebug_frame_base_address (const frame_info_ptr &this_frame,
diff --git a/gdb/alpha-tdep.c b/gdb/alpha-tdep.c
index f24312b7cbc..57b36c1f7e7 100644
--- a/gdb/alpha-tdep.c
+++ b/gdb/alpha-tdep.c
@@ -1007,8 +1007,7 @@ alpha_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind alpha_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy alpha_sigtramp_frame_unwind (
   "alpha sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1017,7 +1016,7 @@ static const struct frame_unwind alpha_sigtramp_frame_unwind =
   alpha_sigtramp_frame_prev_register,
   NULL,
   alpha_sigtramp_frame_sniffer
-};
+);
 
 \f
 
@@ -1427,8 +1426,7 @@ alpha_heuristic_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
 }
 
-static const struct frame_unwind alpha_heuristic_frame_unwind =
-{
+static const struct frame_unwind_legacy alpha_heuristic_frame_unwind (
   "alpha prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1437,7 +1435,7 @@ static const struct frame_unwind alpha_heuristic_frame_unwind =
   alpha_heuristic_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 alpha_heuristic_frame_base_address (const frame_info_ptr &this_frame,
diff --git a/gdb/amd64-obsd-tdep.c b/gdb/amd64-obsd-tdep.c
index 0159bdb4049..e328ae87b7c 100644
--- a/gdb/amd64-obsd-tdep.c
+++ b/gdb/amd64-obsd-tdep.c
@@ -402,8 +402,7 @@ amd64obsd_trapframe_sniffer (const struct frame_unwind *self,
 		   || (startswith (name, "Xintr"))));
 }
 
-static const struct frame_unwind amd64obsd_trapframe_unwind =
-{
+static const struct frame_unwind_legacy amd64obsd_trapframe_unwind (
   /* FIXME: kettenis/20051219: This really is more like an interrupt
      frame, but SIGTRAMP_FRAME would print <signal handler called>,
      which really is not what we want here.  */
@@ -415,7 +414,7 @@ static const struct frame_unwind amd64obsd_trapframe_unwind =
   amd64obsd_trapframe_prev_register,
   NULL,
   amd64obsd_trapframe_sniffer
-};
+);
 \f
 
 static void
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index bbeeb935192..6af48deb362 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -2678,8 +2678,7 @@ amd64_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind amd64_frame_unwind =
-{
+static const struct frame_unwind_legacy amd64_frame_unwind (
   "amd64 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2688,7 +2687,7 @@ static const struct frame_unwind amd64_frame_unwind =
   amd64_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 \f
 /* Generate a bytecode expression to get the value of the saved PC.  */
 
@@ -2825,8 +2824,7 @@ amd64_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind amd64_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy amd64_sigtramp_frame_unwind (
   "amd64 sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2835,7 +2833,7 @@ static const struct frame_unwind amd64_sigtramp_frame_unwind =
   amd64_sigtramp_frame_prev_register,
   NULL,
   amd64_sigtramp_frame_sniffer
-};
+);
 \f
 
 static CORE_ADDR
@@ -3018,8 +3016,7 @@ amd64_epilogue_frame_this_id (const frame_info_ptr &this_frame,
     (*this_id) = frame_id_build (cache->base + 16, cache->pc);
 }
 
-static const struct frame_unwind amd64_epilogue_override_frame_unwind =
-{
+static const struct frame_unwind_legacy amd64_epilogue_override_frame_unwind (
   "amd64 epilogue override",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3028,10 +3025,9 @@ static const struct frame_unwind amd64_epilogue_override_frame_unwind =
   amd64_frame_prev_register,
   NULL,
   amd64_epilogue_override_frame_sniffer
-};
+);
 
-static const struct frame_unwind amd64_epilogue_frame_unwind =
-{
+static const struct frame_unwind_legacy amd64_epilogue_frame_unwind (
   "amd64 epilogue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3040,7 +3036,7 @@ static const struct frame_unwind amd64_epilogue_frame_unwind =
   amd64_frame_prev_register,
   NULL, 
   amd64_epilogue_frame_sniffer
-};
+);
 
 static struct frame_id
 amd64_dummy_id (struct gdbarch *gdbarch, const frame_info_ptr &this_frame)
diff --git a/gdb/amd64-windows-tdep.c b/gdb/amd64-windows-tdep.c
index e4459b4717b..75bc39c01e6 100644
--- a/gdb/amd64-windows-tdep.c
+++ b/gdb/amd64-windows-tdep.c
@@ -1181,8 +1181,7 @@ amd64_windows_frame_this_id (const frame_info_ptr &this_frame, void **this_cache
 
 /* Windows x64 SEH unwinder.  */
 
-static const struct frame_unwind amd64_windows_frame_unwind =
-{
+static const struct frame_unwind_legacy amd64_windows_frame_unwind (
   "amd64 windows",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1191,7 +1190,7 @@ static const struct frame_unwind amd64_windows_frame_unwind =
   &amd64_windows_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Implement the "skip_prologue" gdbarch method.  */
 
diff --git a/gdb/amdgpu-tdep.c b/gdb/amdgpu-tdep.c
index 8189de8e565..45c83a562e8 100644
--- a/gdb/amdgpu-tdep.c
+++ b/gdb/amdgpu-tdep.c
@@ -892,7 +892,7 @@ amdgpu_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const frame_unwind amdgpu_frame_unwind = {
+static const frame_unwind_legacy amdgpu_frame_unwind (
   "amdgpu",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -903,7 +903,7 @@ static const frame_unwind amdgpu_frame_unwind = {
   default_frame_sniffer,
   nullptr,
   nullptr,
-};
+);
 
 static int
 print_insn_amdgpu (bfd_vma memaddr, struct disassemble_info *info)
diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c
index 9e756ad473e..d9b881c9b92 100644
--- a/gdb/arc-tdep.c
+++ b/gdb/arc-tdep.c
@@ -1910,7 +1910,7 @@ arc_sigtramp_frame_sniffer (const struct frame_unwind *self,
    the fallback unwinder, we use the default frame sniffer, which always
    accepts the frame.  */
 
-static const struct frame_unwind arc_frame_unwind = {
+static const struct frame_unwind_legacy arc_frame_unwind (
   "arc prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1921,13 +1921,13 @@ static const struct frame_unwind arc_frame_unwind = {
   default_frame_sniffer,
   NULL,
   NULL
-};
+);
 
 /* Structure defining the ARC signal frame unwind functions.  Custom
    sniffer is used, because this frame must be accepted only in the right
    context.  */
 
-static const struct frame_unwind arc_sigtramp_frame_unwind = {
+static const struct frame_unwind_legacy arc_sigtramp_frame_unwind (
   "arc sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1938,7 +1938,7 @@ static const struct frame_unwind arc_sigtramp_frame_unwind = {
   arc_sigtramp_frame_sniffer,
   NULL,
   NULL
-};
+);
 
 
 static const struct frame_base arc_normal_base = {
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 6bec31dead1..5198a3dddd8 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -2469,7 +2469,7 @@ arm_prologue_prev_register (const frame_info_ptr &this_frame,
 				       prev_regnum);
 }
 
-static frame_unwind arm_prologue_unwind = {
+static frame_unwind_legacy arm_prologue_unwind (
   "arm prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2478,7 +2478,7 @@ static frame_unwind arm_prologue_unwind = {
   arm_prologue_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Maintain a list of ARM exception table entries per objfile, similar to the
    list of mapping symbols.  We only cache entries for standard ARM-defined
@@ -3189,7 +3189,7 @@ arm_exidx_unwind_sniffer (const struct frame_unwind *self,
   return 1;
 }
 
-struct frame_unwind arm_exidx_unwind = {
+struct frame_unwind_legacy arm_exidx_unwind (
   "arm exidx",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3198,7 +3198,7 @@ struct frame_unwind arm_exidx_unwind = {
   arm_prologue_prev_register,
   NULL,
   arm_exidx_unwind_sniffer
-};
+);
 
 static struct arm_prologue_cache *
 arm_make_epilogue_frame_cache (const frame_info_ptr &this_frame)
@@ -3299,8 +3299,7 @@ arm_epilogue_frame_sniffer (const struct frame_unwind *self,
 
 /* Frame unwinder from epilogue.  */
 
-static const struct frame_unwind arm_epilogue_frame_unwind =
-{
+static const struct frame_unwind_legacy arm_epilogue_frame_unwind (
   "arm epilogue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3308,8 +3307,8 @@ static const struct frame_unwind arm_epilogue_frame_unwind =
   arm_epilogue_frame_this_id,
   arm_epilogue_frame_prev_register,
   NULL,
-  arm_epilogue_frame_sniffer,
-};
+  arm_epilogue_frame_sniffer
+);
 
 /* Recognize GCC's trampoline for thumb call-indirect.  If we are in a
    trampoline, return the target PC.  Otherwise return 0.
@@ -3430,7 +3429,7 @@ arm_stub_unwind_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-struct frame_unwind arm_stub_unwind = {
+struct frame_unwind_legacy arm_stub_unwind (
   "arm stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3439,7 +3438,7 @@ struct frame_unwind arm_stub_unwind = {
   arm_prologue_prev_register,
   NULL,
   arm_stub_unwind_sniffer
-};
+);
 
 /* Put here the code to store, into CACHE->saved_regs, the addresses
    of the saved registers of frame described by THIS_FRAME.  CACHE is
@@ -3956,8 +3955,7 @@ arm_m_exception_unwind_sniffer (const struct frame_unwind *self,
 /* Frame unwinder for M-profile exceptions (EXC_RETURN on stack),
    lockup and secure/nonsecure interstate function calls (FNC_RETURN).  */
 
-struct frame_unwind arm_m_exception_unwind =
-{
+struct frame_unwind_legacy arm_m_exception_unwind (
   "arm m exception lockup sec_fnc",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3966,7 +3964,7 @@ struct frame_unwind arm_m_exception_unwind =
   arm_m_exception_prev_register,
   NULL,
   arm_m_exception_unwind_sniffer
-};
+);
 
 static CORE_ADDR
 arm_normal_frame_base (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/avr-tdep.c b/gdb/avr-tdep.c
index 0c12a8a1d65..4305c3834cf 100644
--- a/gdb/avr-tdep.c
+++ b/gdb/avr-tdep.c
@@ -1155,7 +1155,7 @@ avr_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
 }
 
-static const struct frame_unwind avr_frame_unwind = {
+static const struct frame_unwind_legacy avr_frame_unwind (
   "avr prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1164,7 +1164,7 @@ static const struct frame_unwind avr_frame_unwind = {
   avr_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 avr_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/bfin-tdep.c b/gdb/bfin-tdep.c
index 6c15098fbbc..b55c7c26c66 100644
--- a/gdb/bfin-tdep.c
+++ b/gdb/bfin-tdep.c
@@ -372,8 +372,7 @@ bfin_frame_prev_register (const frame_info_ptr &this_frame,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind bfin_frame_unwind =
-{
+static const struct frame_unwind_legacy bfin_frame_unwind (
   "bfin prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -382,7 +381,7 @@ static const struct frame_unwind bfin_frame_unwind =
   bfin_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Check for "[--SP] = <reg>;" insns.  These are appear in function
    prologues to save misc registers onto the stack.  */
diff --git a/gdb/bpf-tdep.c b/gdb/bpf-tdep.c
index 15bd08b8f58..0e4b9dce90a 100644
--- a/gdb/bpf-tdep.c
+++ b/gdb/bpf-tdep.c
@@ -182,8 +182,7 @@ bpf_frame_prev_register (const frame_info_ptr &this_frame,
 
 /* Frame unwinder machinery for BPF.  */
 
-static const struct frame_unwind bpf_frame_unwind =
-{
+static const struct frame_unwind_legacy bpf_frame_unwind (
   "bpf prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -192,7 +191,7 @@ static const struct frame_unwind bpf_frame_unwind =
   bpf_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 \f
 /* Breakpoints.  */
diff --git a/gdb/cris-tdep.c b/gdb/cris-tdep.c
index c1a0e5fe961..24a4bd84baa 100644
--- a/gdb/cris-tdep.c
+++ b/gdb/cris-tdep.c
@@ -435,8 +435,7 @@ cris_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind cris_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy cris_sigtramp_frame_unwind (
   "cris sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -445,7 +444,7 @@ static const struct frame_unwind cris_sigtramp_frame_unwind =
   cris_sigtramp_frame_prev_register,
   NULL,
   cris_sigtramp_frame_sniffer
-};
+);
 
 static int
 crisv32_single_step_through_delay (struct gdbarch *gdbarch,
@@ -901,8 +900,7 @@ cris_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
   return sp;
 }
 
-static const struct frame_unwind cris_frame_unwind = 
-{
+static const struct frame_unwind_legacy cris_frame_unwind (
   "cris prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -911,7 +909,7 @@ static const struct frame_unwind cris_frame_unwind =
   cris_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 cris_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/csky-tdep.c b/gdb/csky-tdep.c
index d95b0a2f9f2..447e3cc3461 100644
--- a/gdb/csky-tdep.c
+++ b/gdb/csky-tdep.c
@@ -2159,7 +2159,7 @@ csky_frame_prev_register (const frame_info_ptr &this_frame,
 /* Data structures for the normal prologue-analysis-based
    unwinder.  */
 
-static const struct frame_unwind csky_unwind_cache = {
+static const struct frame_unwind_legacy csky_unwind_cache (
   "cski prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2170,7 +2170,7 @@ static const struct frame_unwind csky_unwind_cache = {
   default_frame_sniffer,
   NULL,
   NULL
-};
+);
 
 static CORE_ADDR
 csky_check_long_branch (const frame_info_ptr &frame, CORE_ADDR pc)
@@ -2294,7 +2294,7 @@ csky_stub_prev_register (const frame_info_ptr &this_frame,
 				       prev_regnum);
 }
 
-static frame_unwind csky_stub_unwind = {
+static frame_unwind_legacy csky_stub_unwind (
   "csky stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2303,7 +2303,7 @@ static frame_unwind csky_stub_unwind = {
   csky_stub_prev_register,
   NULL,
   csky_stub_unwind_sniffer
-};
+);
 
 /* Implement the this_base, this_locals, and this_args hooks
    for the normal unwinder.  */
diff --git a/gdb/dummy-frame.c b/gdb/dummy-frame.c
index a208e370aac..ed067323c46 100644
--- a/gdb/dummy-frame.c
+++ b/gdb/dummy-frame.c
@@ -376,8 +376,7 @@ dummy_frame_this_id (const frame_info_ptr &this_frame,
   (*this_id) = cache->this_id;
 }
 
-const struct frame_unwind dummy_frame_unwind =
-{
+const struct frame_unwind_legacy dummy_frame_unwind (
   "dummy",
   DUMMY_FRAME,
   FRAME_UNWIND_GDB,
@@ -385,8 +384,8 @@ const struct frame_unwind dummy_frame_unwind =
   dummy_frame_this_id,
   dummy_frame_prev_register,
   NULL,
-  dummy_frame_sniffer,
-};
+  dummy_frame_sniffer
+);
 
 /* See dummy-frame.h.  */
 
diff --git a/gdb/dummy-frame.h b/gdb/dummy-frame.h
index 7d963ad9beb..a1341797acd 100644
--- a/gdb/dummy-frame.h
+++ b/gdb/dummy-frame.h
@@ -54,7 +54,7 @@ extern void dummy_frame_discard (frame_id dummy_id, thread_info *thread);
 /* If the PC falls in a dummy frame, return a dummy frame
    unwinder.  */
 
-extern const struct frame_unwind dummy_frame_unwind;
+extern const struct frame_unwind_legacy dummy_frame_unwind;
 
 /* Destructor for dummy_frame.  DATA is supplied by registrant.
    REGISTERS_VALID is 1 for dummy_frame_pop, 0 for dummy_frame_discard.  */
diff --git a/gdb/dwarf2/frame-tailcall.c b/gdb/dwarf2/frame-tailcall.c
index c4daaf28a01..ec96d8be6a5 100644
--- a/gdb/dwarf2/frame-tailcall.c
+++ b/gdb/dwarf2/frame-tailcall.c
@@ -470,8 +470,7 @@ tailcall_frame_prev_arch (const frame_info_ptr &this_frame,
 /* Virtual tail call frame unwinder if dwarf2_tailcall_sniffer_first finds
    a chain to create.  */
 
-const struct frame_unwind dwarf2_tailcall_frame_unwind =
-{
+const struct frame_unwind_legacy dwarf2_tailcall_frame_unwind (
   "dwarf2 tailcall",
   TAILCALL_FRAME,
   FRAME_UNWIND_DEBUGINFO,
@@ -482,7 +481,7 @@ const struct frame_unwind dwarf2_tailcall_frame_unwind =
   tailcall_frame_sniffer,
   tailcall_frame_dealloc_cache,
   tailcall_frame_prev_arch
-};
+);
 
 void _initialize_tailcall_frame ();
 void
diff --git a/gdb/dwarf2/frame-tailcall.h b/gdb/dwarf2/frame-tailcall.h
index 3f49487ac2a..3569c7dd3e2 100644
--- a/gdb/dwarf2/frame-tailcall.h
+++ b/gdb/dwarf2/frame-tailcall.h
@@ -34,6 +34,6 @@ extern struct value *
   dwarf2_tailcall_prev_register_first (const frame_info_ptr &this_frame,
 				       void **tailcall_cachep, int regnum);
 
-extern const struct frame_unwind dwarf2_tailcall_frame_unwind;
+extern const struct frame_unwind_legacy dwarf2_tailcall_frame_unwind;
 
 #endif /* !DWARF2_FRAME_TAILCALL_H */
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index c1fec57b7c7..699db8831a5 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -1332,16 +1332,15 @@ dwarf2_frame_sniffer (const struct frame_unwind *self,
   if (fde->cie->signal_frame
       || dwarf2_frame_signal_frame_p (get_frame_arch (this_frame),
 				      this_frame))
-    return self->type == SIGTRAMP_FRAME;
+    return self->type () == SIGTRAMP_FRAME;
 
-  if (self->type != NORMAL_FRAME)
+  if (self->type () != NORMAL_FRAME)
     return 0;
 
   return 1;
 }
 
-static const struct frame_unwind dwarf2_frame_unwind =
-{
+static const struct frame_unwind_legacy dwarf2_frame_unwind (
   "dwarf2",
   NORMAL_FRAME,
   FRAME_UNWIND_DEBUGINFO,
@@ -1351,10 +1350,9 @@ static const struct frame_unwind dwarf2_frame_unwind =
   NULL,
   dwarf2_frame_sniffer,
   dwarf2_frame_dealloc_cache
-};
+);
 
-static const struct frame_unwind dwarf2_signal_frame_unwind =
-{
+static const struct frame_unwind_legacy dwarf2_signal_frame_unwind (
   "dwarf2 signal",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_DEBUGINFO,
@@ -1366,7 +1364,7 @@ static const struct frame_unwind dwarf2_signal_frame_unwind =
 
   /* TAILCALL_CACHE can never be in such frame to need dealloc_cache.  */
   NULL
-};
+);
 
 /* Append the DWARF-2 frame unwinders to GDBARCH's list.  */
 
diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
index 5af5085b038..2e35732d763 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -129,7 +129,7 @@ frame_unwind_try_unwinder (const frame_info_ptr &this_frame, void **this_cache,
 
   try
     {
-      frame_debug_printf ("trying unwinder \"%s\"", unwinder->name);
+      frame_debug_printf ("trying unwinder \"%s\"", unwinder->name ());
       res = unwinder->sniffer (unwinder, this_frame, this_cache);
     }
   catch (const gdb_exception &ex)
@@ -335,6 +335,78 @@ frame_unwind_got_address (const frame_info_ptr &frame, int regnum,
   return reg_val;
 }
 
+/* See frame-unwind.h.  */
+
+enum unwind_stop_reason
+frame_unwind::stop_reason (const frame_info_ptr &this_frame,
+			   void **this_prologue_cache) const
+{
+  return default_frame_unwind_stop_reason (this_frame, this_prologue_cache);
+}
+
+/* See frame-unwind.h.  */
+
+int
+frame_unwind::sniffer (const struct frame_unwind *self,
+		       const frame_info_ptr &this_frame,
+		       void **this_prologue_cache) const
+{
+  return 1;
+}
+
+/* This method just passes the parameters to the callback pointer.  */
+enum unwind_stop_reason
+frame_unwind_legacy::stop_reason (const frame_info_ptr &this_frame,
+				  void **this_prologue_cache) const
+{
+  return stop_reason_p (this_frame, this_prologue_cache);
+}
+
+/* This method just passes the parameters to the callback pointer.  */
+void
+frame_unwind_legacy::this_id (const frame_info_ptr &this_frame,
+			      void **this_prologue_cache,
+			      struct frame_id *id) const
+{
+  return this_id_p (this_frame, this_prologue_cache, id);
+}
+
+/* This method just passes the parameters to the callback pointer.  */
+struct value *
+frame_unwind_legacy::prev_register (const frame_info_ptr &this_frame,
+				    void **this_prologue_cache,
+				    int regnum) const
+{
+  return prev_register_p (this_frame, this_prologue_cache, regnum);
+}
+
+/* This method just passes the parameters to the callback pointer.  */
+int
+frame_unwind_legacy::sniffer (const struct frame_unwind *self,
+			      const frame_info_ptr &this_frame,
+			      void **this_prologue_cache) const
+{
+  return sniffer_p (self, this_frame, this_prologue_cache);
+}
+
+/* This method just passes the parameters to the callback pointer.  */
+void
+frame_unwind_legacy::dealloc_cache (frame_info *self, void *this_cache) const
+{
+  if (dealloc_cache_p != nullptr)
+    dealloc_cache_p (self, this_cache);
+}
+
+/* This method just passes the parameters to the callback pointer.  */
+struct gdbarch *
+frame_unwind_legacy::prev_arch (const frame_info_ptr &this_frame,
+				void **this_prologue_cache) const
+{
+  if (prev_arch_p == nullptr)
+    error (_("No prev_arch callback installed"));
+  return prev_arch_p (this_frame, this_prologue_cache);
+}
+
 /* Implement "maintenance info frame-unwinders" command.  */
 
 static void
@@ -352,9 +424,9 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
 
   for (const struct frame_unwind* unwinder: table)
     {
-      const char *name = unwinder->name;
-      const char *type = frame_type_str (unwinder->type);
-      const char *uclass = frame_unwinder_class_str (unwinder->unwinder_class);
+      const char *name = unwinder->name ();
+      const char *type = frame_type_str (unwinder->type ());
+      const char *uclass = frame_unwinder_class_str (unwinder->unwinder_class ());
 
       ui_out_emit_list tuple_emitter (uiout, nullptr);
       uiout->field_string ("name", name);
diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
index fd70949df8f..eff36f7eab5 100644
--- a/gdb/frame-unwind.h
+++ b/gdb/frame-unwind.h
@@ -163,25 +163,146 @@ enum frame_unwind_class {
   FRAME_UNWIND_ARCH,
 };
 
-struct frame_unwind
+class frame_unwind
 {
-  const char *name;
+private:
+  const char *m_name;
   /* The frame's type.  Should this instead be a collection of
      predicates that test the frame for various attributes?  */
-  enum frame_type type;
+  enum frame_type m_type;
   /* What kind of unwinder is this.  It generally follows from where
      the unwinder was added or where it looks for information to do the
      unwinding.  */
-  enum frame_unwind_class unwinder_class;
+  enum frame_unwind_class m_unwinder_class;
+  const struct frame_data *m_unwind_data;
+public:
+  frame_unwind (const char *n, frame_type t, frame_unwind_class c,
+		       const struct frame_data *d)
+    : m_name (n), m_type (t), m_unwinder_class (c), m_unwind_data (d) { }
+
+  const char *name () const
+  {
+    return m_name;
+  }
+
+  enum frame_type type () const
+  {
+    return m_type;
+  }
+
+  enum frame_unwind_class unwinder_class () const
+  {
+    return m_unwinder_class;
+  }
+
+  const struct frame_data *unwind_data () const
+  {
+    return m_unwind_data;
+  }
+
+  /* Default stop_reason function.  It reports NO_REASON, unless the
+     frame is the outermost.  */
+  virtual enum unwind_stop_reason stop_reason (const frame_info_ptr &this_frame,
+					       void **this_prologue_cache) const;
+
+  /* Default frame sniffer.  Will always return that the unwinder
+     is able to unwind the frame.  */
+  virtual int sniffer (const frame_unwind *self,
+		       const frame_info_ptr &this_frame,
+		       void **this_prologue_cache) const;
+
+  /* The following methods are here mostly for interface functionality.  They
+     all throw an error when called, as a safe way to check if an unwinder has
+     implemented the desired functionality.  */
+
+  /* Calculate the ID of the given frame using this unwinder.  */
+  virtual void this_id (const frame_info_ptr &this_frame,
+			void **this_prologue_cache,
+			struct frame_id *id) const
+  {
+    error (_("No method this_id implemented for unwinder %s"), m_name);
+  }
+
+  /* Get the value of a register in a previous frame.  */
+  virtual struct value *prev_register (const frame_info_ptr &this_frame,
+				       void **this_prologue_cache,
+				       int regnum) const
+  {
+    error (_("No method prev_register implemented for unwinder %s"), m_name);
+  }
+
+  /* Properly deallocate the cache.  */
+  virtual void dealloc_cache (frame_info *self, void *this_cache) const
+  {
+    error (_("No method dealloc_cache implemented for unwinder %s"), m_name);
+  }
+
+  /* Get the previous architecture.  */
+  virtual struct gdbarch *prev_arch (const frame_info_ptr &this_frame,
+				     void **this_prologue_cache) const
+  {
+    error (_("No method prev_arch implemented for unwinder %s"), m_name);
+  }
+};
+
+/* This is a legacy version of the frame unwinder.  The original struct
+   used function pointers for callbacks, this updated version has a
+   method that just passes the parameters along to the callback
+   pointer.
+   Do not use this class for new unwinders.  Instead, see other classes
+   that inherit from frame_unwind, such as the python unwinder.  */
+class frame_unwind_legacy : public frame_unwind
+{
+private:
   /* Should an attribute indicating the frame's address-in-block go
      here?  */
-  frame_unwind_stop_reason_ftype *stop_reason;
-  frame_this_id_ftype *this_id;
-  frame_prev_register_ftype *prev_register;
-  const struct frame_data *unwind_data;
-  frame_sniffer_ftype *sniffer;
-  frame_dealloc_cache_ftype *dealloc_cache;
-  frame_prev_arch_ftype *prev_arch;
+  frame_unwind_stop_reason_ftype *stop_reason_p;
+  frame_this_id_ftype *this_id_p;
+  frame_prev_register_ftype *prev_register_p;
+  frame_sniffer_ftype *sniffer_p;
+  frame_dealloc_cache_ftype *dealloc_cache_p;
+  frame_prev_arch_ftype *prev_arch_p;
+public:
+  frame_unwind_legacy (const char *n, frame_type t, frame_unwind_class c,
+		       frame_unwind_stop_reason_ftype *sr,
+		       frame_this_id_ftype *ti,
+		       frame_prev_register_ftype *pr,
+		       const struct frame_data *ud,
+		       frame_sniffer_ftype *s,
+		       frame_dealloc_cache_ftype *dc = nullptr,
+		       frame_prev_arch_ftype *pa = nullptr)
+  : frame_unwind (n, t, c, ud), stop_reason_p (sr),
+    this_id_p (ti), prev_register_p (pr), sniffer_p (s),
+    dealloc_cache_p (dc), prev_arch_p (pa) { }
+
+  /* This method just passes the parameters to the callback pointer.  */
+  enum unwind_stop_reason stop_reason (const frame_info_ptr &this_frame,
+					void **this_prologue_cache) const override;
+
+  /* This method just passes the parameters to the callback pointer.  */
+  void this_id (const frame_info_ptr &this_frame, void **this_prologue_cache,
+		 struct frame_id *id) const override;
+
+  /* This method just passes the parameters to the callback pointer.  */
+  struct value *prev_register (const frame_info_ptr &this_frame,
+				void **this_prologue_cache,
+				int regnum) const override;
+
+  /* This method just passes the parameters to the callback pointer.  */
+  int sniffer (const frame_unwind *self,
+		const frame_info_ptr &this_frame,
+		void **this_prologue_cache) const override;
+
+  /* This method just passes the parameters to the callback pointer.
+     It is safe to call this method if no callback is installed, it
+     just turns into a noop.  */
+  void dealloc_cache (frame_info *self, void *this_cache) const override;
+
+  /* This method just passes the parameters to the callback pointer.
+     If this function is called with no installed callback, this method
+     will error out.  Wrap calls in try-catch blocks.  */
+  struct gdbarch *prev_arch (const frame_info_ptr &this_frame,
+			      void **this_prologue_cache) const override;
 };
 
 /* Register a frame unwinder, _prepending_ it to the front of the
diff --git a/gdb/frame.c b/gdb/frame.c
index 5c7aae9edf4..60b7586468a 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -268,12 +268,10 @@ frame_addr_hash_eq (const void *a, const void *b)
 static void
 frame_info_del (frame_info *frame)
 {
-  if (frame->prologue_cache != nullptr
-      && frame->unwind->dealloc_cache != nullptr)
+  if (frame->prologue_cache != nullptr)
     frame->unwind->dealloc_cache (frame, frame->prologue_cache);
 
-  if (frame->base_cache != nullptr
-      && frame->base->unwind->dealloc_cache != nullptr)
+  if (frame->base_cache != nullptr)
     frame->base->unwind->dealloc_cache (frame, frame->base_cache);
 }
 
@@ -486,12 +484,12 @@ frame_info::to_string () const
   res += string_printf ("{level=%d,", fi->level);
 
   if (fi->unwind != NULL)
-    res += string_printf ("type=%s,", frame_type_str (fi->unwind->type));
+    res += string_printf ("type=%s,", frame_type_str (fi->unwind->type ()));
   else
     res += "type=<unknown>,";
 
   if (fi->unwind != NULL)
-    res += string_printf ("unwinder=\"%s\",", fi->unwind->name);
+    res += string_printf ("unwinder=\"%s\",", fi->unwind->name ());
   else
     res += "unwinder=<unknown>,";
 
@@ -2360,7 +2358,7 @@ get_prev_frame_always_1 (const frame_info_ptr &this_frame)
      This check is valid only if this frame and the next frame are NORMAL.
      See the comment at frame_id_inner for details.  */
   if (get_frame_type (this_frame) == NORMAL_FRAME
-      && this_frame->next->unwind->type == NORMAL_FRAME
+      && this_frame->next->unwind->type () == NORMAL_FRAME
       && frame_id_inner (get_frame_arch (frame_info_ptr (this_frame->next)),
 			 get_frame_id (this_frame),
 			 get_frame_id (frame_info_ptr (this_frame->next))))
@@ -2956,7 +2954,7 @@ get_frame_type (const frame_info_ptr &frame)
     /* Initialize the frame's unwinder because that's what
        provides the frame's type.  */
     frame_unwind_find_by_frame (frame, &frame->prologue_cache);
-  return frame->unwind->type;
+  return frame->unwind->type ();
 }
 
 struct program_space *
@@ -3037,11 +3035,15 @@ frame_unwind_arch (const frame_info_ptr &next_frame)
       if (next_frame->unwind == NULL)
 	frame_unwind_find_by_frame (next_frame, &next_frame->prologue_cache);
 
-      if (next_frame->unwind->prev_arch != NULL)
-	arch = next_frame->unwind->prev_arch (next_frame,
-					      &next_frame->prologue_cache);
-      else
-	arch = get_frame_arch (next_frame);
+      try
+	{
+	  arch = next_frame->unwind->prev_arch (next_frame,
+						&next_frame->prologue_cache);
+	}
+      catch (gdb_exception &e)
+	{
+	  arch = get_frame_arch (next_frame);
+	}
 
       next_frame->prev_arch.arch = arch;
       next_frame->prev_arch.p = true;
diff --git a/gdb/frv-linux-tdep.c b/gdb/frv-linux-tdep.c
index f80e5f0d089..31a8a84edb1 100644
--- a/gdb/frv-linux-tdep.c
+++ b/gdb/frv-linux-tdep.c
@@ -332,8 +332,7 @@ frv_linux_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind frv_linux_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy frv_linux_sigtramp_frame_unwind (
   "frv linux sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -342,7 +341,7 @@ static const struct frame_unwind frv_linux_sigtramp_frame_unwind =
   frv_linux_sigtramp_frame_prev_register,
   NULL,
   frv_linux_sigtramp_frame_sniffer
-};
+);
 \f
 /* The FRV kernel defines ELF_NGREG as 46.  We add 2 in order to include
    the loadmap addresses in the register set.  (See below for more info.)  */
diff --git a/gdb/frv-tdep.c b/gdb/frv-tdep.c
index 216ccde01b0..1eb35acee83 100644
--- a/gdb/frv-tdep.c
+++ b/gdb/frv-tdep.c
@@ -1405,7 +1405,7 @@ frv_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
 }
 
-static const struct frame_unwind frv_frame_unwind = {
+static const struct frame_unwind_legacy frv_frame_unwind (
   "frv prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1414,7 +1414,7 @@ static const struct frame_unwind frv_frame_unwind = {
   frv_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 frv_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/ft32-tdep.c b/gdb/ft32-tdep.c
index 8ec2668906f..cb2bd60d29f 100644
--- a/gdb/ft32-tdep.c
+++ b/gdb/ft32-tdep.c
@@ -524,8 +524,7 @@ ft32_frame_prev_register (const frame_info_ptr &this_frame,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind ft32_frame_unwind =
-{
+static const struct frame_unwind_legacy ft32_frame_unwind (
   "ft32 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -534,7 +533,7 @@ static const struct frame_unwind ft32_frame_unwind =
   ft32_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Return the base address of this_frame.  */
 
diff --git a/gdb/h8300-tdep.c b/gdb/h8300-tdep.c
index 3a739c24e79..d0f50ee4db1 100644
--- a/gdb/h8300-tdep.c
+++ b/gdb/h8300-tdep.c
@@ -500,7 +500,7 @@ h8300_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind h8300_frame_unwind = {
+static const struct frame_unwind_legacy h8300_frame_unwind (
   "h8300 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -509,7 +509,7 @@ static const struct frame_unwind h8300_frame_unwind = {
   h8300_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 h8300_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/hppa-linux-tdep.c b/gdb/hppa-linux-tdep.c
index d44735bc523..dcc33f9f6bd 100644
--- a/gdb/hppa-linux-tdep.c
+++ b/gdb/hppa-linux-tdep.c
@@ -308,7 +308,7 @@ hppa_linux_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind hppa_linux_sigtramp_frame_unwind = {
+static const struct frame_unwind_legacy hppa_linux_sigtramp_frame_unwind (
   "hppa linux sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -317,7 +317,7 @@ static const struct frame_unwind hppa_linux_sigtramp_frame_unwind = {
   hppa_linux_sigtramp_frame_prev_register,
   NULL,
   hppa_linux_sigtramp_frame_sniffer
-};
+);
 
 /* Attempt to find (and return) the global pointer for the given
    function.
diff --git a/gdb/hppa-tdep.c b/gdb/hppa-tdep.c
index 5a66fe88f45..027342f6456 100644
--- a/gdb/hppa-tdep.c
+++ b/gdb/hppa-tdep.c
@@ -2283,8 +2283,7 @@ hppa_frame_unwind_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind hppa_frame_unwind =
-{
+static const struct frame_unwind_legacy hppa_frame_unwind (
   "hppa unwind table",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2293,7 +2292,7 @@ static const struct frame_unwind hppa_frame_unwind =
   hppa_frame_prev_register,
   NULL,
   hppa_frame_unwind_sniffer
-};
+);
 
 /* This is a generic fallback frame unwinder that kicks in if we fail all
    the other ones.  Normally we would expect the stub and regular unwinder
@@ -2397,8 +2396,7 @@ hppa_fallback_frame_prev_register (const frame_info_ptr &this_frame,
 					  info->saved_regs, regnum);
 }
 
-static const struct frame_unwind hppa_fallback_frame_unwind =
-{
+static const struct frame_unwind_legacy hppa_fallback_frame_unwind (
   "hppa prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2407,7 +2405,7 @@ static const struct frame_unwind hppa_fallback_frame_unwind =
   hppa_fallback_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Stub frames, used for all kinds of call stubs.  */
 struct hppa_stub_unwind_cache
@@ -2480,7 +2478,7 @@ hppa_stub_unwind_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind hppa_stub_frame_unwind = {
+static const struct frame_unwind_legacy hppa_stub_frame_unwind (
   "hppa stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2489,7 +2487,7 @@ static const struct frame_unwind hppa_stub_frame_unwind = {
   hppa_stub_frame_prev_register,
   NULL,
   hppa_stub_unwind_sniffer
-};
+);
 
 CORE_ADDR
 hppa_unwind_pc (struct gdbarch *gdbarch, const frame_info_ptr &next_frame)
diff --git a/gdb/i386-obsd-tdep.c b/gdb/i386-obsd-tdep.c
index 1f23c2e7d96..e0735243951 100644
--- a/gdb/i386-obsd-tdep.c
+++ b/gdb/i386-obsd-tdep.c
@@ -390,7 +390,7 @@ i386obsd_trapframe_sniffer (const struct frame_unwind *self,
 		   || startswith (name, "Xsoft")));
 }
 
-static const struct frame_unwind i386obsd_trapframe_unwind = {
+static const struct frame_unwind_legacy i386obsd_trapframe_unwind (
   "i386 openbsd trap",
   /* FIXME: kettenis/20051219: This really is more like an interrupt
      frame, but SIGTRAMP_FRAME would print <signal handler called>,
@@ -402,7 +402,7 @@ static const struct frame_unwind i386obsd_trapframe_unwind = {
   i386obsd_trapframe_prev_register,
   NULL,
   i386obsd_trapframe_sniffer
-};
+);
 \f
 
 static void 
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index ede74a4be70..e89667650fc 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -2196,8 +2196,7 @@ i386_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind i386_frame_unwind =
-{
+static const struct frame_unwind_legacy i386_frame_unwind (
   "i386 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2206,7 +2205,7 @@ static const struct frame_unwind i386_frame_unwind =
   i386_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Normal frames, but in a function epilogue.  */
 
@@ -2352,8 +2351,7 @@ i386_epilogue_frame_prev_register (const frame_info_ptr &this_frame,
   return i386_frame_prev_register (this_frame, this_cache, regnum);
 }
 
-static const struct frame_unwind i386_epilogue_override_frame_unwind =
-{
+static const struct frame_unwind_legacy i386_epilogue_override_frame_unwind (
   "i386 epilogue override",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2362,10 +2360,9 @@ static const struct frame_unwind i386_epilogue_override_frame_unwind =
   i386_epilogue_frame_prev_register,
   NULL,
   i386_epilogue_override_frame_sniffer
-};
+);
 
-static const struct frame_unwind i386_epilogue_frame_unwind =
-{
+static const struct frame_unwind_legacy i386_epilogue_frame_unwind (
   "i386 epilogue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2374,7 +2371,7 @@ static const struct frame_unwind i386_epilogue_frame_unwind =
   i386_epilogue_frame_prev_register,
   NULL,
   i386_epilogue_frame_sniffer
-};
+);
 \f
 
 /* Stack-based trampolines.  */
@@ -2447,8 +2444,7 @@ i386_stack_tramp_frame_sniffer (const struct frame_unwind *self,
     return 0;
 }
 
-static const struct frame_unwind i386_stack_tramp_frame_unwind =
-{
+static const struct frame_unwind_legacy i386_stack_tramp_frame_unwind (
   "i386 stack tramp",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2457,7 +2453,7 @@ static const struct frame_unwind i386_stack_tramp_frame_unwind =
   i386_epilogue_frame_prev_register,
   NULL,
   i386_stack_tramp_frame_sniffer
-};
+);
 \f
 /* Generate a bytecode expression to get the value of the saved PC.  */
 
@@ -2597,8 +2593,7 @@ i386_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind i386_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy i386_sigtramp_frame_unwind (
   "i386 sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2607,7 +2602,7 @@ static const struct frame_unwind i386_sigtramp_frame_unwind =
   i386_sigtramp_frame_prev_register,
   NULL,
   i386_sigtramp_frame_sniffer
-};
+);
 \f
 
 static CORE_ADDR
diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c
index a75ac192a25..f643c99ac3a 100644
--- a/gdb/ia64-tdep.c
+++ b/gdb/ia64-tdep.c
@@ -2162,8 +2162,7 @@ ia64_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
     }
 }
  
-static const struct frame_unwind ia64_frame_unwind =
-{
+static const struct frame_unwind_legacy ia64_frame_unwind (
   "ia64 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2172,7 +2171,7 @@ static const struct frame_unwind ia64_frame_unwind =
   &ia64_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Signal trampolines.  */
 
@@ -2352,8 +2351,7 @@ ia64_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind ia64_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy ia64_sigtramp_frame_unwind (
   "ia64 sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2362,7 +2360,7 @@ static const struct frame_unwind ia64_sigtramp_frame_unwind =
   ia64_sigtramp_frame_prev_register,
   NULL,
   ia64_sigtramp_frame_sniffer
-};
+);
 
 \f
 
@@ -3013,8 +3011,7 @@ ia64_libunwind_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind ia64_libunwind_frame_unwind =
-{
+static const struct frame_unwind_legacy ia64_libunwind_frame_unwind (
   "ia64 libunwind",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3024,7 +3021,7 @@ static const struct frame_unwind ia64_libunwind_frame_unwind =
   NULL,
   ia64_libunwind_frame_sniffer,
   libunwind_frame_dealloc_cache
-};
+);
 
 static void
 ia64_libunwind_sigtramp_frame_this_id (const frame_info_ptr &this_frame,
@@ -3103,8 +3100,7 @@ ia64_libunwind_sigtramp_frame_sniffer (const struct frame_unwind *self,
     return ia64_sigtramp_frame_sniffer (self, this_frame, this_cache);
 }
 
-static const struct frame_unwind ia64_libunwind_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy ia64_libunwind_sigtramp_frame_unwind (
   "ia64 libunwind sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3113,7 +3109,7 @@ static const struct frame_unwind ia64_libunwind_sigtramp_frame_unwind =
   ia64_libunwind_sigtramp_frame_prev_register,
   NULL,
   ia64_libunwind_sigtramp_frame_sniffer
-};
+);
 
 /* Set of libunwind callback acccessor functions.  */
 unw_accessors_t ia64_unw_accessors =
diff --git a/gdb/inline-frame.c b/gdb/inline-frame.c
index 1a0352a35c1..7d95dac719d 100644
--- a/gdb/inline-frame.c
+++ b/gdb/inline-frame.c
@@ -265,7 +265,7 @@ inline_frame_sniffer (const struct frame_unwind *self,
   return 1;
 }
 
-const struct frame_unwind inline_frame_unwind = {
+const struct frame_unwind_legacy inline_frame_unwind (
   "inline",
   INLINE_FRAME,
   FRAME_UNWIND_GDB,
@@ -274,7 +274,7 @@ const struct frame_unwind inline_frame_unwind = {
   inline_frame_prev_register,
   NULL,
   inline_frame_sniffer
-};
+);
 
 /* Return non-zero if BLOCK, an inlined function block containing PC,
    has a group of contiguous instructions starting at PC (but not
diff --git a/gdb/inline-frame.h b/gdb/inline-frame.h
index bbe617c7c5b..a8bd9b07414 100644
--- a/gdb/inline-frame.h
+++ b/gdb/inline-frame.h
@@ -27,7 +27,7 @@ struct process_stratum_target;
 
 /* The inline frame unwinder.  */
 
-extern const struct frame_unwind inline_frame_unwind;
+extern const struct frame_unwind_legacy inline_frame_unwind;
 
 /* Skip all inlined functions whose call sites are at the current PC.
 
diff --git a/gdb/iq2000-tdep.c b/gdb/iq2000-tdep.c
index 698284597d8..51a95fc48d4 100644
--- a/gdb/iq2000-tdep.c
+++ b/gdb/iq2000-tdep.c
@@ -424,7 +424,7 @@ iq2000_frame_this_id (const frame_info_ptr &this_frame, void **this_cache,
   *this_id = frame_id_build (cache->saved_sp, cache->pc);
 }
 
-static const struct frame_unwind iq2000_frame_unwind = {
+static const struct frame_unwind_legacy iq2000_frame_unwind (
   "iq2000 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -433,7 +433,7 @@ static const struct frame_unwind iq2000_frame_unwind = {
   iq2000_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 iq2000_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/jit.c b/gdb/jit.c
index 16c3111fbc7..c5649e1c0af 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -1104,8 +1104,7 @@ jit_frame_prev_register (const frame_info_ptr &this_frame, void **cache, int reg
 /* Relay everything back to the unwinder registered by the JIT debug
    info reader.*/
 
-static const struct frame_unwind jit_frame_unwind =
-{
+static const struct frame_unwind_legacy jit_frame_unwind (
   "jit",
   NORMAL_FRAME,
   FRAME_UNWIND_EXTENSION,
@@ -1115,7 +1114,7 @@ static const struct frame_unwind jit_frame_unwind =
   NULL,
   jit_frame_sniffer,
   jit_dealloc_cache
-};
+);
 
 
 /* This is the information that is stored at jit_gdbarch_data for each
diff --git a/gdb/lm32-tdep.c b/gdb/lm32-tdep.c
index 9cfc81c429b..e8e0325f19e 100644
--- a/gdb/lm32-tdep.c
+++ b/gdb/lm32-tdep.c
@@ -447,7 +447,7 @@ lm32_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
 }
 
-static const struct frame_unwind lm32_frame_unwind = {
+static const struct frame_unwind_legacy lm32_frame_unwind (
   "lm32 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -456,7 +456,7 @@ static const struct frame_unwind lm32_frame_unwind = {
   lm32_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 lm32_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c
index e9d3f156125..0a6cd0aba3a 100644
--- a/gdb/loongarch-tdep.c
+++ b/gdb/loongarch-tdep.c
@@ -450,7 +450,7 @@ loongarch_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_register (info, this_frame, regnum);
 }
 
-static const struct frame_unwind loongarch_frame_unwind = {
+static const struct frame_unwind_legacy loongarch_frame_unwind (
   "loongarch prologue",
   /*.type	   =*/NORMAL_FRAME,
   /*.unwinder_class=*/FRAME_UNWIND_ARCH,
@@ -460,8 +460,8 @@ static const struct frame_unwind loongarch_frame_unwind = {
   /*.unwind_data   =*/nullptr,
   /*.sniffer	   =*/default_frame_sniffer,
   /*.dealloc_cache =*/nullptr,
-  /*.prev_arch	   =*/nullptr,
-};
+  /*.prev_arch	   =*/nullptr
+);
 
 /* Write the contents of buffer VAL into the general-purpose argument
    register defined by GAR in REGCACHE.  GAR indicates the available
diff --git a/gdb/m32c-tdep.c b/gdb/m32c-tdep.c
index 748465256e3..c3bb46b9ae2 100644
--- a/gdb/m32c-tdep.c
+++ b/gdb/m32c-tdep.c
@@ -1955,7 +1955,7 @@ m32c_prev_register (const frame_info_ptr &this_frame,
 }
 
 
-static const struct frame_unwind m32c_unwind = {
+static const struct frame_unwind_legacy m32c_unwind (
   "m32c prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1964,7 +1964,7 @@ static const struct frame_unwind m32c_unwind = {
   m32c_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 \f
 /* Inferior calls.  */
diff --git a/gdb/m32r-linux-tdep.c b/gdb/m32r-linux-tdep.c
index 87dce624a4a..a2b2cea3884 100644
--- a/gdb/m32r-linux-tdep.c
+++ b/gdb/m32r-linux-tdep.c
@@ -301,7 +301,7 @@ m32r_linux_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind m32r_linux_sigtramp_frame_unwind = {
+static const struct frame_unwind_legacy m32r_linux_sigtramp_frame_unwind (
   "m32r linux sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -310,7 +310,7 @@ static const struct frame_unwind m32r_linux_sigtramp_frame_unwind = {
   m32r_linux_sigtramp_frame_prev_register,
   NULL,
   m32r_linux_sigtramp_frame_sniffer
-};
+);
 
 /* Mapping between the registers in `struct pt_regs'
    format and GDB's register array layout.  */
diff --git a/gdb/m32r-tdep.c b/gdb/m32r-tdep.c
index 9b4af6a7248..f35b4a5a824 100644
--- a/gdb/m32r-tdep.c
+++ b/gdb/m32r-tdep.c
@@ -831,7 +831,7 @@ m32r_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
 }
 
-static const struct frame_unwind m32r_frame_unwind = {
+static const struct frame_unwind_legacy m32r_frame_unwind (
   "m32r prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -840,7 +840,7 @@ static const struct frame_unwind m32r_frame_unwind = {
   m32r_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 m32r_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/m68hc11-tdep.c b/gdb/m68hc11-tdep.c
index bcf9861ccd5..8c535728c0c 100644
--- a/gdb/m68hc11-tdep.c
+++ b/gdb/m68hc11-tdep.c
@@ -936,7 +936,7 @@ m68hc11_frame_prev_register (const frame_info_ptr &this_frame,
   return value;
 }
 
-static const struct frame_unwind m68hc11_frame_unwind = {
+static const struct frame_unwind_legacy m68hc11_frame_unwind (
   "m68hc11 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -945,7 +945,7 @@ static const struct frame_unwind m68hc11_frame_unwind = {
   m68hc11_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 m68hc11_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/m68k-linux-tdep.c b/gdb/m68k-linux-tdep.c
index 86c700dafe6..7fd6ddca25b 100644
--- a/gdb/m68k-linux-tdep.c
+++ b/gdb/m68k-linux-tdep.c
@@ -314,8 +314,7 @@ m68k_linux_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return m68k_linux_pc_in_sigtramp (this_frame);
 }
 
-static const struct frame_unwind m68k_linux_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy m68k_linux_sigtramp_frame_unwind (
   "m68k linux sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -324,7 +323,7 @@ static const struct frame_unwind m68k_linux_sigtramp_frame_unwind =
   m68k_linux_sigtramp_frame_prev_register,
   NULL,
   m68k_linux_sigtramp_frame_sniffer
-};
+);
 
 /* Register maps for supply/collect regset functions.  */
 
diff --git a/gdb/m68k-tdep.c b/gdb/m68k-tdep.c
index 23254dcfac0..78198846c3a 100644
--- a/gdb/m68k-tdep.c
+++ b/gdb/m68k-tdep.c
@@ -1007,8 +1007,7 @@ m68k_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind m68k_frame_unwind =
-{
+static const struct frame_unwind_legacy m68k_frame_unwind (
   "m68k prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1017,7 +1016,7 @@ static const struct frame_unwind m68k_frame_unwind =
   m68k_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 \f
 static CORE_ADDR
 m68k_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/mep-tdep.c b/gdb/mep-tdep.c
index 392c7231535..6349b1ac4dc 100644
--- a/gdb/mep-tdep.c
+++ b/gdb/mep-tdep.c
@@ -2061,7 +2061,7 @@ mep_frame_prev_register (const frame_info_ptr &this_frame,
 }
 
 
-static const struct frame_unwind mep_frame_unwind = {
+static const struct frame_unwind_legacy mep_frame_unwind (
   "mep prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2070,7 +2070,7 @@ static const struct frame_unwind mep_frame_unwind = {
   mep_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 \f
 /* Return values.  */
diff --git a/gdb/microblaze-tdep.c b/gdb/microblaze-tdep.c
index 7dc209c680d..7856b974c40 100644
--- a/gdb/microblaze-tdep.c
+++ b/gdb/microblaze-tdep.c
@@ -478,8 +478,7 @@ microblaze_frame_prev_register (const frame_info_ptr &this_frame,
 
 }
 
-static const struct frame_unwind microblaze_frame_unwind =
-{
+static const struct frame_unwind_legacy microblaze_frame_unwind (
   "microblaze prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -488,7 +487,7 @@ static const struct frame_unwind microblaze_frame_unwind =
   microblaze_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 \f
 static CORE_ADDR
 microblaze_frame_base_address (const frame_info_ptr &next_frame,
diff --git a/gdb/mips-sde-tdep.c b/gdb/mips-sde-tdep.c
index 9f16d2d8593..be414800881 100644
--- a/gdb/mips-sde-tdep.c
+++ b/gdb/mips-sde-tdep.c
@@ -161,8 +161,7 @@ mips_sde_frame_sniffer (const struct frame_unwind *self,
 
 /* Data structure for the SDE frame unwinder.  */
 
-static const struct frame_unwind mips_sde_frame_unwind =
-{
+static const struct frame_unwind_legacy mips_sde_frame_unwind (
   "mips sde sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -171,7 +170,7 @@ static const struct frame_unwind mips_sde_frame_unwind =
   mips_sde_frame_prev_register,
   NULL,
   mips_sde_frame_sniffer
-};
+);
 
 /* Implement the this_base, this_locals, and this_args hooks
    for the normal unwinder.  */
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index a8d2cea8cb7..c01942c6dd2 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -2929,8 +2929,7 @@ mips_insn16_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind mips_insn16_frame_unwind =
-{
+static const struct frame_unwind_legacy mips_insn16_frame_unwind (
   "mips insn16 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2939,7 +2938,7 @@ static const struct frame_unwind mips_insn16_frame_unwind =
   mips_insn16_frame_prev_register,
   NULL,
   mips_insn16_frame_sniffer
-};
+);
 
 static CORE_ADDR
 mips_insn16_frame_base_address (const frame_info_ptr &this_frame,
@@ -3366,8 +3365,7 @@ mips_micro_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind mips_micro_frame_unwind =
-{
+static const struct frame_unwind_legacy mips_micro_frame_unwind (
   "mips micro prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3376,7 +3374,7 @@ static const struct frame_unwind mips_micro_frame_unwind =
   mips_micro_frame_prev_register,
   NULL,
   mips_micro_frame_sniffer
-};
+);
 
 static CORE_ADDR
 mips_micro_frame_base_address (const frame_info_ptr &this_frame,
@@ -3746,8 +3744,7 @@ mips_insn32_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind mips_insn32_frame_unwind =
-{
+static const struct frame_unwind_legacy mips_insn32_frame_unwind (
   "mips insn32 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3756,7 +3753,7 @@ static const struct frame_unwind mips_insn32_frame_unwind =
   mips_insn32_frame_prev_register,
   NULL,
   mips_insn32_frame_sniffer
-};
+);
 
 static CORE_ADDR
 mips_insn32_frame_base_address (const frame_info_ptr &this_frame,
@@ -3864,8 +3861,7 @@ mips_stub_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind mips_stub_frame_unwind =
-{
+static const struct frame_unwind_legacy mips_stub_frame_unwind (
   "mips stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3874,7 +3870,7 @@ static const struct frame_unwind mips_stub_frame_unwind =
   mips_stub_frame_prev_register,
   NULL,
   mips_stub_frame_sniffer
-};
+);
 
 static CORE_ADDR
 mips_stub_frame_base_address (const frame_info_ptr &this_frame,
diff --git a/gdb/mn10300-tdep.c b/gdb/mn10300-tdep.c
index b8b290d1b87..1de37a1608f 100644
--- a/gdb/mn10300-tdep.c
+++ b/gdb/mn10300-tdep.c
@@ -1127,7 +1127,7 @@ mn10300_frame_prev_register (const frame_info_ptr &this_frame,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind mn10300_frame_unwind = {
+static const struct frame_unwind_legacy mn10300_frame_unwind (
   "mn10300 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1136,7 +1136,7 @@ static const struct frame_unwind mn10300_frame_unwind = {
   mn10300_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static void
 mn10300_frame_unwind_init (struct gdbarch *gdbarch)
diff --git a/gdb/moxie-tdep.c b/gdb/moxie-tdep.c
index 124275de8e2..c6ae3260be9 100644
--- a/gdb/moxie-tdep.c
+++ b/gdb/moxie-tdep.c
@@ -585,7 +585,7 @@ moxie_frame_prev_register (const frame_info_ptr &this_frame,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind moxie_frame_unwind = {
+static const struct frame_unwind_legacy moxie_frame_unwind (
   "moxie prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -594,7 +594,7 @@ static const struct frame_unwind moxie_frame_unwind = {
   moxie_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Return the base address of this_frame.  */
 
diff --git a/gdb/msp430-tdep.c b/gdb/msp430-tdep.c
index 05830de63b0..cc840cbf688 100644
--- a/gdb/msp430-tdep.c
+++ b/gdb/msp430-tdep.c
@@ -542,7 +542,7 @@ msp430_prev_register (const frame_info_ptr &this_frame,
     return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind msp430_unwind = {
+static const struct frame_unwind_legacy msp430_unwind (
   "msp430 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -551,7 +551,7 @@ static const struct frame_unwind msp430_unwind = {
   msp430_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Implement the "dwarf2_reg_to_regnum" gdbarch method.  */
 
diff --git a/gdb/nds32-tdep.c b/gdb/nds32-tdep.c
index f2653400ab3..c59719f28a2 100644
--- a/gdb/nds32-tdep.c
+++ b/gdb/nds32-tdep.c
@@ -988,8 +988,7 @@ nds32_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind nds32_frame_unwind =
-{
+static const struct frame_unwind_legacy nds32_frame_unwind (
   "nds32 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -997,8 +996,8 @@ static const struct frame_unwind nds32_frame_unwind =
   nds32_frame_this_id,
   nds32_frame_prev_register,
   NULL,
-  default_frame_sniffer,
-};
+  default_frame_sniffer
+);
 
 /* Return the frame base address of *THIS_FRAME.  */
 
@@ -1373,8 +1372,7 @@ nds32_epilogue_frame_prev_register (const frame_info_ptr &this_frame,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind nds32_epilogue_frame_unwind =
-{
+static const struct frame_unwind_legacy nds32_epilogue_frame_unwind (
   "nds32 epilogue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1383,7 +1381,7 @@ static const struct frame_unwind nds32_epilogue_frame_unwind =
   nds32_epilogue_frame_prev_register,
   NULL,
   nds32_epilogue_frame_sniffer
-};
+);
 
 \f
 /* Floating type and struct type that has only one floating type member
diff --git a/gdb/nios2-tdep.c b/gdb/nios2-tdep.c
index 4b5b7239366..b158b97bab4 100644
--- a/gdb/nios2-tdep.c
+++ b/gdb/nios2-tdep.c
@@ -1977,8 +1977,7 @@ nios2_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
 /* Data structures for the normal prologue-analysis-based
    unwinder.  */
 
-static const struct frame_unwind nios2_frame_unwind =
-{
+static const struct frame_unwind_legacy nios2_frame_unwind (
   "nios2 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1987,7 +1986,7 @@ static const struct frame_unwind nios2_frame_unwind =
   nios2_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static const struct frame_base nios2_frame_base =
 {
@@ -2079,8 +2078,7 @@ nios2_stub_frame_sniffer (const struct frame_unwind *self,
 
 /* Define the data structures for the stub unwinder.  */
 
-static const struct frame_unwind nios2_stub_frame_unwind =
-{
+static const struct frame_unwind_legacy nios2_stub_frame_unwind (
   "nios2 stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2089,7 +2087,7 @@ static const struct frame_unwind nios2_stub_frame_unwind =
   nios2_stub_frame_prev_register,
   NULL,
   nios2_stub_frame_sniffer
-};
+);
 
 
 
diff --git a/gdb/or1k-tdep.c b/gdb/or1k-tdep.c
index 681877fdb18..9a9bfa18697 100644
--- a/gdb/or1k-tdep.c
+++ b/gdb/or1k-tdep.c
@@ -1125,7 +1125,7 @@ or1k_frame_prev_register (const frame_info_ptr &this_frame,
 
 /* Data structures for the normal prologue-analysis-based unwinder.  */
 
-static const struct frame_unwind or1k_frame_unwind = {
+static const struct frame_unwind_legacy or1k_frame_unwind (
   "or1k prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1134,8 +1134,8 @@ static const struct frame_unwind or1k_frame_unwind = {
   or1k_frame_prev_register,
   NULL,
   default_frame_sniffer,
-  NULL,
-};
+  NULL
+);
 
 /* Architecture initialization for OpenRISC 1000.  */
 
diff --git a/gdb/ppc-fbsd-tdep.c b/gdb/ppc-fbsd-tdep.c
index a9576231c66..41a5103519f 100644
--- a/gdb/ppc-fbsd-tdep.c
+++ b/gdb/ppc-fbsd-tdep.c
@@ -262,7 +262,7 @@ ppcfbsd_sigtramp_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_register (cache, this_frame, regnum);
 }
 
-static const struct frame_unwind ppcfbsd_sigtramp_frame_unwind = {
+static const struct frame_unwind_legacy ppcfbsd_sigtramp_frame_unwind (
   "ppc freebsd sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -271,7 +271,7 @@ static const struct frame_unwind ppcfbsd_sigtramp_frame_unwind = {
   ppcfbsd_sigtramp_frame_prev_register,
   NULL,
   ppcfbsd_sigtramp_frame_sniffer
-};
+);
 
 static enum return_value_convention
 ppcfbsd_return_value (struct gdbarch *gdbarch, struct value *function,
diff --git a/gdb/ppc-obsd-tdep.c b/gdb/ppc-obsd-tdep.c
index c5e08091547..1c77b592b57 100644
--- a/gdb/ppc-obsd-tdep.c
+++ b/gdb/ppc-obsd-tdep.c
@@ -231,7 +231,7 @@ ppcobsd_sigtramp_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_register (cache, this_frame, regnum);
 }
 
-static const struct frame_unwind ppcobsd_sigtramp_frame_unwind = {
+static const struct frame_unwind_legacy ppcobsd_sigtramp_frame_unwind (
   "ppc openbsd sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -240,7 +240,7 @@ static const struct frame_unwind ppcobsd_sigtramp_frame_unwind = {
   ppcobsd_sigtramp_frame_prev_register,
   NULL,
   ppcobsd_sigtramp_frame_sniffer
-};
+);
 \f
 
 static void
diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c
index 6268c475777..298957d34ae 100644
--- a/gdb/python/py-unwind.c
+++ b/gdb/python/py-unwind.c
@@ -807,7 +807,7 @@ pyuw_sniffer (const struct frame_unwind *self, const frame_info_ptr &this_frame,
 {
   PYUW_SCOPED_DEBUG_ENTER_EXIT;
 
-  struct gdbarch *gdbarch = (struct gdbarch *) (self->unwind_data);
+  struct gdbarch *gdbarch = (struct gdbarch *) (self->unwind_data ());
   cached_frame_info *cached_frame;
 
   gdbpy_enter enter_py (gdbarch);
@@ -948,6 +948,41 @@ struct pyuw_gdbarch_data_type
 
 static const registry<gdbarch>::key<pyuw_gdbarch_data_type> pyuw_gdbarch_data;
 
+/* Class for frame unwinders registered by the Python architecture callback.  */
+class frame_unwind_python : public frame_unwind
+{
+public:
+  frame_unwind_python (const struct frame_data *newarch)
+    : frame_unwind ("python", NORMAL_FRAME, FRAME_UNWIND_EXTENSION, newarch)
+  { }
+
+  /* No need to override stop_reason, we want the default.  */
+
+  int sniffer (const frame_unwind *self, const frame_info_ptr &this_frame,
+		void **this_prologue_cache) const override
+  {
+    return pyuw_sniffer (self, this_frame, this_prologue_cache);
+  }
+
+  void this_id (const frame_info_ptr &this_frame, void **this_prologue_cache,
+		struct frame_id *id) const override
+  {
+    pyuw_this_id (this_frame, this_prologue_cache, id);
+  }
+
+  struct value *prev_register (const frame_info_ptr &this_frame,
+			       void **this_prologue_cache,
+			       int regnum) const override
+  {
+      return pyuw_prev_register (this_frame, this_prologue_cache, regnum);
+  }
+
+  void dealloc_cache (frame_info *self, void *this_cache) const override
+  {
+      pyuw_dealloc_cache (self, this_cache);
+  }
+};
+
 /* New inferior architecture callback: register the Python unwinders
    intermediary.  */
 
@@ -961,17 +996,9 @@ pyuw_on_new_gdbarch (gdbarch *newarch)
   if (!data->unwinder_registered)
     {
       struct frame_unwind *unwinder
-	  = GDBARCH_OBSTACK_ZALLOC (newarch, struct frame_unwind);
-
-      unwinder->name = "python";
-      unwinder->type = NORMAL_FRAME;
-      unwinder->unwinder_class = FRAME_UNWIND_EXTENSION;
-      unwinder->stop_reason = default_frame_unwind_stop_reason;
-      unwinder->this_id = pyuw_this_id;
-      unwinder->prev_register = pyuw_prev_register;
-      unwinder->unwind_data = (const struct frame_data *) newarch;
-      unwinder->sniffer = pyuw_sniffer;
-      unwinder->dealloc_cache = pyuw_dealloc_cache;
+	  = obstack_new<frame_unwind_python>
+	    (gdbarch_obstack(newarch), (const struct frame_data *) newarch);
+
       frame_unwind_prepend_unwinder (newarch, unwinder);
       data->unwinder_registered = 1;
     }
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index b6011c86c85..f2db246fd25 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -1894,8 +1894,7 @@ record_btrace_frame_dealloc_cache (frame_info *self, void *this_cache)
    Therefore this unwinder reports any possibly unwound registers as
    <unavailable>.  */
 
-const struct frame_unwind record_btrace_frame_unwind =
-{
+const struct frame_unwind_legacy record_btrace_frame_unwind (
   "record-btrace",
   NORMAL_FRAME,
   FRAME_UNWIND_GDB,
@@ -1905,10 +1904,9 @@ const struct frame_unwind record_btrace_frame_unwind =
   NULL,
   record_btrace_frame_sniffer,
   record_btrace_frame_dealloc_cache
-};
+);
 
-const struct frame_unwind record_btrace_tailcall_frame_unwind =
-{
+const struct frame_unwind_legacy record_btrace_tailcall_frame_unwind (
   "record-btrace tailcall",
   TAILCALL_FRAME,
   FRAME_UNWIND_GDB,
@@ -1918,7 +1916,7 @@ const struct frame_unwind record_btrace_tailcall_frame_unwind =
   NULL,
   record_btrace_tailcall_frame_sniffer,
   record_btrace_frame_dealloc_cache
-};
+);
 
 /* Implement the get_unwinder method.  */
 
diff --git a/gdb/record.h b/gdb/record.h
index f44b3952943..44918ef4242 100644
--- a/gdb/record.h
+++ b/gdb/record.h
@@ -36,8 +36,8 @@ extern struct cmd_list_element *show_record_cmdlist;
 extern struct cmd_list_element *info_record_cmdlist;
 
 /* Unwinders for some record targets.  */
-extern const struct frame_unwind record_btrace_frame_unwind;
-extern const struct frame_unwind record_btrace_tailcall_frame_unwind;
+extern const struct frame_unwind_legacy record_btrace_frame_unwind;
+extern const struct frame_unwind_legacy record_btrace_tailcall_frame_unwind;
 
 /* A list of different recording methods.  */
 enum record_method
diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
index 7af747a5161..6fe7116ce54 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -3901,8 +3901,7 @@ riscv_frame_prev_register (const frame_info_ptr &this_frame,
    are the fallback unwinder (DWARF unwinder is used first), we use the
    default frame sniffer, which always accepts the frame.  */
 
-static const struct frame_unwind riscv_frame_unwind =
-{
+static const struct frame_unwind_legacy riscv_frame_unwind (
   /*.name          =*/ "riscv prologue",
   /*.type          =*/ NORMAL_FRAME,
   /*.unwinder_class=*/FRAME_UNWIND_ARCH,
@@ -3912,8 +3911,8 @@ static const struct frame_unwind riscv_frame_unwind =
   /*.unwind_data   =*/ NULL,
   /*.sniffer       =*/ default_frame_sniffer,
   /*.dealloc_cache =*/ NULL,
-  /*.prev_arch     =*/ NULL,
-};
+  /*.prev_arch     =*/ NULL
+);
 
 /* Extract a set of required target features out of ABFD.  If ABFD is
    nullptr then a RISCV_GDBARCH_FEATURES is returned in its default state.  */
diff --git a/gdb/rl78-tdep.c b/gdb/rl78-tdep.c
index 00fa3ad301e..da660808f4b 100644
--- a/gdb/rl78-tdep.c
+++ b/gdb/rl78-tdep.c
@@ -1183,8 +1183,7 @@ rl78_prev_register (const frame_info_ptr &this_frame,
     return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind rl78_unwind =
-{
+static const struct frame_unwind_legacy rl78_unwind (
   "rl78 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1193,7 +1192,7 @@ static const struct frame_unwind rl78_unwind =
   rl78_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Implement the "dwarf_reg_to_regnum" gdbarch method.  */
 
diff --git a/gdb/rs6000-aix-tdep.c b/gdb/rs6000-aix-tdep.c
index 78d6f5e8a19..318616be0eb 100644
--- a/gdb/rs6000-aix-tdep.c
+++ b/gdb/rs6000-aix-tdep.c
@@ -328,7 +328,7 @@ aix_sighandle_frame_sniffer (const struct frame_unwind *self,
 
 /* AIX signal handler frame unwinder */
 
-static const struct frame_unwind aix_sighandle_frame_unwind = {
+static const struct frame_unwind_legacy aix_sighandle_frame_unwind (
   "rs6000 aix sighandle",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -337,7 +337,7 @@ static const struct frame_unwind aix_sighandle_frame_unwind = {
   aix_sighandle_frame_prev_register,
   NULL,
   aix_sighandle_frame_sniffer
-};
+);
 
 /* Core file support.  */
 
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index c3551be5248..b94cf75c4b0 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -3838,8 +3838,7 @@ rs6000_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
 }
 
-static const struct frame_unwind rs6000_frame_unwind =
-{
+static const struct frame_unwind_legacy rs6000_frame_unwind (
   "rs6000 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3848,7 +3847,7 @@ static const struct frame_unwind rs6000_frame_unwind =
   rs6000_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Allocate and initialize a frame cache for an epilogue frame.
    SP is restored and prev-PC is stored in LR.  */
@@ -3980,8 +3979,7 @@ rs6000_epilogue_frame_sniffer (const struct frame_unwind *self,
 /* Frame unwinder for epilogue frame.  This is required for reverse step-over
    a function without debug information.  */
 
-static const struct frame_unwind rs6000_epilogue_frame_unwind =
-{
+static const struct frame_unwind_legacy rs6000_epilogue_frame_unwind (
   "rs6000 epilogue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -3989,7 +3987,7 @@ static const struct frame_unwind rs6000_epilogue_frame_unwind =
   rs6000_epilogue_frame_this_id, rs6000_epilogue_frame_prev_register,
   NULL,
   rs6000_epilogue_frame_sniffer
-};
+);
 \f
 
 static CORE_ADDR
diff --git a/gdb/rx-tdep.c b/gdb/rx-tdep.c
index d959dd1e327..35bdbd31513 100644
--- a/gdb/rx-tdep.c
+++ b/gdb/rx-tdep.c
@@ -631,7 +631,7 @@ rx_exception_sniffer (const struct frame_unwind *self,
 /* Data structure for normal code using instruction-based prologue
    analyzer.  */
 
-static const struct frame_unwind rx_frame_unwind = {
+static const struct frame_unwind_legacy rx_frame_unwind (
   "rx prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -640,12 +640,12 @@ static const struct frame_unwind rx_frame_unwind = {
   rx_frame_prev_register,
   NULL,
   rx_frame_sniffer
-};
+);
 
 /* Data structure for exception code using instruction-based prologue
    analyzer.  */
 
-static const struct frame_unwind rx_exception_unwind = {
+static const struct frame_unwind_legacy rx_exception_unwind (
   "rx exception",
   /* SIGTRAMP_FRAME could be used here, but backtraces are less informative.  */
   NORMAL_FRAME,
@@ -655,7 +655,7 @@ static const struct frame_unwind rx_exception_unwind = {
   rx_frame_prev_register,
   NULL,
   rx_exception_sniffer
-};
+);
 
 /* Implement the "push_dummy_call" gdbarch method.  */
 static CORE_ADDR
diff --git a/gdb/s12z-tdep.c b/gdb/s12z-tdep.c
index 7f7865a8022..125bb287a3c 100644
--- a/gdb/s12z-tdep.c
+++ b/gdb/s12z-tdep.c
@@ -442,7 +442,7 @@ s12z_frame_prev_register (const frame_info_ptr &this_frame,
 }
 
 /* Data structures for the normal prologue-analysis-based unwinder.  */
-static const struct frame_unwind s12z_frame_unwind = {
+static const struct frame_unwind_legacy s12z_frame_unwind (
   "s12z prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -451,8 +451,8 @@ static const struct frame_unwind s12z_frame_unwind = {
   s12z_frame_prev_register,
   NULL,
   default_frame_sniffer,
-  NULL,
-};
+  NULL
+);
 
 
 constexpr gdb_byte s12z_break_insn[] = {0x00};
diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
index 8b3df082063..2b3473af3f0 100644
--- a/gdb/s390-linux-tdep.c
+++ b/gdb/s390-linux-tdep.c
@@ -542,7 +542,7 @@ s390_sigtramp_frame_sniffer (const struct frame_unwind *self,
 
 /* S390 sigtramp frame unwinder.  */
 
-static const struct frame_unwind s390_sigtramp_frame_unwind = {
+static const struct frame_unwind_legacy s390_sigtramp_frame_unwind (
   "s390 linux sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -551,7 +551,7 @@ static const struct frame_unwind s390_sigtramp_frame_unwind = {
   s390_sigtramp_frame_prev_register,
   NULL,
   s390_sigtramp_frame_sniffer
-};
+);
 
 /* Syscall handling.  */
 
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index d7fa9e11fad..d08f72c9023 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -2646,7 +2646,7 @@ s390_frame_prev_register (const frame_info_ptr &this_frame,
 
 /* Default S390 frame unwinder.  */
 
-static const struct frame_unwind s390_frame_unwind = {
+static const struct frame_unwind_legacy s390_frame_unwind (
   "s390 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2655,7 +2655,7 @@ static const struct frame_unwind s390_frame_unwind = {
   s390_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Code stubs and their stack frames.  For things like PLTs and NULL
    function calls (where there is no true frame and the return address
@@ -2741,7 +2741,7 @@ s390_stub_frame_sniffer (const struct frame_unwind *self,
 
 /* S390 stub frame unwinder.  */
 
-static const struct frame_unwind s390_stub_frame_unwind = {
+static const struct frame_unwind_legacy s390_stub_frame_unwind (
   "s390 stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2750,7 +2750,7 @@ static const struct frame_unwind s390_stub_frame_unwind = {
   s390_stub_frame_prev_register,
   NULL,
   s390_stub_frame_sniffer
-};
+);
 
 /* Frame base handling.  */
 
diff --git a/gdb/sentinel-frame.c b/gdb/sentinel-frame.c
index 42e5f66bf2a..c5a0dbadf91 100644
--- a/gdb/sentinel-frame.c
+++ b/gdb/sentinel-frame.c
@@ -79,8 +79,7 @@ sentinel_frame_prev_arch (const frame_info_ptr &this_frame,
   return cache->regcache->arch ();
 }
 
-const struct frame_unwind sentinel_frame_unwind =
-{
+const struct frame_unwind_legacy sentinel_frame_unwind (
   "sentinel",
   SENTINEL_FRAME,
   FRAME_UNWIND_GDB,
@@ -90,5 +89,5 @@ const struct frame_unwind sentinel_frame_unwind =
   NULL,
   NULL,
   NULL,
-  sentinel_frame_prev_arch,
-};
+  sentinel_frame_prev_arch
+);
diff --git a/gdb/sentinel-frame.h b/gdb/sentinel-frame.h
index 1a37ff1f49a..4b498f18940 100644
--- a/gdb/sentinel-frame.h
+++ b/gdb/sentinel-frame.h
@@ -34,6 +34,6 @@ extern void *sentinel_frame_cache (struct regcache *regcache);
 
 /* At present there is only one type of sentinel frame.  */
 
-extern const struct frame_unwind sentinel_frame_unwind;
+extern const struct frame_unwind_legacy sentinel_frame_unwind;
 
 #endif /* !defined (SENTINEL_FRAME_H)  */
diff --git a/gdb/sh-tdep.c b/gdb/sh-tdep.c
index 60e717c73c4..e326014391f 100644
--- a/gdb/sh-tdep.c
+++ b/gdb/sh-tdep.c
@@ -1929,7 +1929,7 @@ sh_frame_this_id (const frame_info_ptr &this_frame, void **this_cache,
   *this_id = frame_id_build (cache->saved_sp, cache->pc);
 }
 
-static const struct frame_unwind sh_frame_unwind = {
+static const struct frame_unwind_legacy sh_frame_unwind (
   "sh prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1938,7 +1938,7 @@ static const struct frame_unwind sh_frame_unwind = {
   sh_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 sh_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
@@ -1996,8 +1996,7 @@ sh_stub_unwind_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind sh_stub_unwind =
-{
+static const struct frame_unwind_legacy sh_stub_unwind (
   "sh stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2006,7 +2005,7 @@ static const struct frame_unwind sh_stub_unwind =
   sh_frame_prev_register,
   NULL,
   sh_stub_unwind_sniffer
-};
+);
 
 /* Implement the stack_frame_destroyed_p gdbarch method.
 
diff --git a/gdb/sparc-netbsd-tdep.c b/gdb/sparc-netbsd-tdep.c
index 5861bf7b836..625792507d1 100644
--- a/gdb/sparc-netbsd-tdep.c
+++ b/gdb/sparc-netbsd-tdep.c
@@ -249,8 +249,7 @@ sparc32nbsd_sigcontext_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind sparc32nbsd_sigcontext_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc32nbsd_sigcontext_frame_unwind (
   "sparc32 netbsd sigcontext",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -259,7 +258,7 @@ static const struct frame_unwind sparc32nbsd_sigcontext_frame_unwind =
   sparc32nbsd_sigcontext_frame_prev_register,
   NULL,
   sparc32nbsd_sigcontext_frame_sniffer
-};
+);
 \f
 /* Return the address of a system call's alternative return
    address.  */
diff --git a/gdb/sparc-obsd-tdep.c b/gdb/sparc-obsd-tdep.c
index 9ffb86fe3dc..6aa17fcc8e5 100644
--- a/gdb/sparc-obsd-tdep.c
+++ b/gdb/sparc-obsd-tdep.c
@@ -134,8 +134,7 @@ sparc32obsd_sigtramp_frame_sniffer (const struct frame_unwind *self,
 
   return 0;
 }
-static const struct frame_unwind sparc32obsd_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc32obsd_sigtramp_frame_unwind (
   "sparc32 openbsd sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -144,7 +143,7 @@ static const struct frame_unwind sparc32obsd_sigtramp_frame_unwind =
   sparc32obsd_sigtramp_frame_prev_register,
   NULL,
   sparc32obsd_sigtramp_frame_sniffer
-};
+);
 
 \f
 
diff --git a/gdb/sparc-sol2-tdep.c b/gdb/sparc-sol2-tdep.c
index 2c2f3fb74ee..63027ebef13 100644
--- a/gdb/sparc-sol2-tdep.c
+++ b/gdb/sparc-sol2-tdep.c
@@ -180,8 +180,7 @@ sparc32_sol2_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return sol2_sigtramp_p (this_frame);
 }
 
-static const struct frame_unwind sparc32_sol2_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc32_sol2_sigtramp_frame_unwind (
   "sparc32 solaris sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -190,7 +189,7 @@ static const struct frame_unwind sparc32_sol2_sigtramp_frame_unwind =
   sparc32_sol2_sigtramp_frame_prev_register,
   NULL,
   sparc32_sol2_sigtramp_frame_sniffer
-};
+);
 
 \f
 
diff --git a/gdb/sparc-tdep.c b/gdb/sparc-tdep.c
index d26e630b274..cf9fea53248 100644
--- a/gdb/sparc-tdep.c
+++ b/gdb/sparc-tdep.c
@@ -1346,8 +1346,7 @@ sparc32_frame_prev_register (const frame_info_ptr &this_frame,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind sparc32_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc32_frame_unwind (
   "sparc32 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1356,7 +1355,7 @@ static const struct frame_unwind sparc32_frame_unwind =
   sparc32_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 \f
 
 static CORE_ADDR
diff --git a/gdb/sparc64-fbsd-tdep.c b/gdb/sparc64-fbsd-tdep.c
index aa5eaf7563e..93574c9aa70 100644
--- a/gdb/sparc64-fbsd-tdep.c
+++ b/gdb/sparc64-fbsd-tdep.c
@@ -197,8 +197,7 @@ sparc64fbsd_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind sparc64fbsd_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc64fbsd_sigtramp_frame_unwind (
   "sparc64 freebsd sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -207,7 +206,7 @@ static const struct frame_unwind sparc64fbsd_sigtramp_frame_unwind =
   sparc64fbsd_sigtramp_frame_prev_register,
   NULL,
   sparc64fbsd_sigtramp_frame_sniffer
-};
+);
 \f
 
 static const struct regset sparc64fbsd_gregset =
diff --git a/gdb/sparc64-netbsd-tdep.c b/gdb/sparc64-netbsd-tdep.c
index 16c5b07fbbb..2fb1b19fbe0 100644
--- a/gdb/sparc64-netbsd-tdep.c
+++ b/gdb/sparc64-netbsd-tdep.c
@@ -223,8 +223,7 @@ sparc64nbsd_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind sparc64nbsd_sigcontext_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc64nbsd_sigcontext_frame_unwind (
   "sparc64 netbsd sigcontext",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -233,7 +232,7 @@ static const struct frame_unwind sparc64nbsd_sigcontext_frame_unwind =
   sparc64nbsd_sigcontext_frame_prev_register,
   NULL,
   sparc64nbsd_sigtramp_frame_sniffer
-};
+);
 \f
 
 static const struct regset sparc64nbsd_gregset =
diff --git a/gdb/sparc64-obsd-tdep.c b/gdb/sparc64-obsd-tdep.c
index 50611742726..3ff5a8ffbe7 100644
--- a/gdb/sparc64-obsd-tdep.c
+++ b/gdb/sparc64-obsd-tdep.c
@@ -220,8 +220,7 @@ sparc64obsd_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind sparc64obsd_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc64obsd_frame_unwind (
   "sparc64 openbsd sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -230,7 +229,7 @@ static const struct frame_unwind sparc64obsd_frame_unwind =
   sparc64obsd_frame_prev_register,
   NULL,
   sparc64obsd_sigtramp_frame_sniffer
-};
+);
 \f
 /* Kernel debugging support.  */
 
@@ -305,8 +304,7 @@ sparc64obsd_trapframe_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind sparc64obsd_trapframe_unwind =
-{
+static const struct frame_unwind_legacy sparc64obsd_trapframe_unwind (
   "sparc64 openbsd trap",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -315,7 +313,7 @@ static const struct frame_unwind sparc64obsd_trapframe_unwind =
   sparc64obsd_trapframe_prev_register,
   NULL,
   sparc64obsd_trapframe_sniffer
-};
+);
 \f
 
 /* Threads support.  */
diff --git a/gdb/sparc64-sol2-tdep.c b/gdb/sparc64-sol2-tdep.c
index e2cd7548355..8403e00b446 100644
--- a/gdb/sparc64-sol2-tdep.c
+++ b/gdb/sparc64-sol2-tdep.c
@@ -183,8 +183,7 @@ sparc64_sol2_sigtramp_frame_sniffer (const struct frame_unwind *self,
   return sol2_sigtramp_p (this_frame);
 }
 
-static const struct frame_unwind sparc64_sol2_sigtramp_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc64_sol2_sigtramp_frame_unwind (
   "sparc64 solaris sigtramp",
   SIGTRAMP_FRAME,
   FRAME_UNWIND_ARCH,
@@ -193,7 +192,7 @@ static const struct frame_unwind sparc64_sol2_sigtramp_frame_unwind =
   sparc64_sol2_sigtramp_frame_prev_register,
   NULL,
   sparc64_sol2_sigtramp_frame_sniffer
-};
+);
 
 \f
 
diff --git a/gdb/sparc64-tdep.c b/gdb/sparc64-tdep.c
index b7036464191..57156263628 100644
--- a/gdb/sparc64-tdep.c
+++ b/gdb/sparc64-tdep.c
@@ -1134,8 +1134,7 @@ sparc64_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static const struct frame_unwind sparc64_frame_unwind =
-{
+static const struct frame_unwind_legacy sparc64_frame_unwind (
   "sparc64 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1144,7 +1143,7 @@ static const struct frame_unwind sparc64_frame_unwind =
   sparc64_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 \f
 
 static CORE_ADDR
diff --git a/gdb/tic6x-tdep.c b/gdb/tic6x-tdep.c
index 7fc58d4fc61..43f8a6a58bb 100644
--- a/gdb/tic6x-tdep.c
+++ b/gdb/tic6x-tdep.c
@@ -452,8 +452,7 @@ tic6x_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
   return info->base;
 }
 
-static const struct frame_unwind tic6x_frame_unwind =
-{
+static const struct frame_unwind_legacy tic6x_frame_unwind (
   "tic6x prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -462,7 +461,7 @@ static const struct frame_unwind tic6x_frame_unwind =
   tic6x_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static const struct frame_base tic6x_frame_base =
 {
@@ -516,8 +515,7 @@ tic6x_stub_unwind_sniffer (const struct frame_unwind *self,
   return 0;
 }
 
-static const struct frame_unwind tic6x_stub_unwind =
-{
+static const struct frame_unwind_legacy tic6x_stub_unwind (
   "tic6x stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -526,7 +524,7 @@ static const struct frame_unwind tic6x_stub_unwind =
   tic6x_frame_prev_register,
   NULL,
   tic6x_stub_unwind_sniffer
-};
+);
 
 /* Return the instruction on address PC.  */
 
diff --git a/gdb/tilegx-tdep.c b/gdb/tilegx-tdep.c
index d0d0de79909..a7f1b120f33 100644
--- a/gdb/tilegx-tdep.c
+++ b/gdb/tilegx-tdep.c
@@ -900,7 +900,7 @@ tilegx_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
   return cache->base;
 }
 
-static const struct frame_unwind tilegx_frame_unwind = {
+static const struct frame_unwind_legacy tilegx_frame_unwind (
   "tilegx prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -910,7 +910,7 @@ static const struct frame_unwind tilegx_frame_unwind = {
   NULL,                        /* const struct frame_data *unwind_data  */
   default_frame_sniffer,       /* frame_sniffer_ftype *sniffer  */
   NULL                         /* frame_prev_pc_ftype *prev_pc  */
-};
+);
 
 static const struct frame_base tilegx_frame_base = {
   &tilegx_frame_unwind,
diff --git a/gdb/tramp-frame.c b/gdb/tramp-frame.c
index fba0c669489..fa5ea6c2194 100644
--- a/gdb/tramp-frame.c
+++ b/gdb/tramp-frame.c
@@ -124,7 +124,7 @@ tramp_frame_sniffer (const struct frame_unwind *self,
 		     const frame_info_ptr &this_frame,
 		     void **this_cache)
 {
-  const struct tramp_frame *tramp = self->unwind_data->tramp_frame;
+  const struct tramp_frame *tramp = self->unwind_data ()->tramp_frame;
   CORE_ADDR pc = get_frame_pc (this_frame);
   CORE_ADDR func;
   struct tramp_frame_cache *tramp_cache;
@@ -143,6 +143,40 @@ tramp_frame_sniffer (const struct frame_unwind *self,
   return 1;
 }
 
+class frame_unwind_trampoline : public frame_unwind
+{
+private:
+  frame_prev_arch_ftype *prev_arch_p;
+public:
+  frame_unwind_trampoline (enum frame_type t, const struct frame_data *d,
+			   frame_prev_arch_ftype *pa)
+    : frame_unwind ("trampoline", t, FRAME_UNWIND_GDB, d), prev_arch_p (pa)
+  { }
+
+  int sniffer(const frame_unwind *self, const frame_info_ptr &this_frame,
+	      void **this_prologue_cache) const override
+  {
+    return tramp_frame_sniffer (self, this_frame, this_prologue_cache);
+  }
+  void this_id (const frame_info_ptr &this_frame, void **this_prologue_cache,
+		struct frame_id *id) const override
+  {
+    tramp_frame_this_id (this_frame, this_prologue_cache, id);
+  }
+  struct value *prev_register (const frame_info_ptr &this_frame,
+			       void **this_prologue_cache,
+			       int regnum) const override
+  {
+    return tramp_frame_prev_register (this_frame, this_prologue_cache, regnum);
+  }
+
+  struct gdbarch *prev_arch (const frame_info_ptr &this_frame,
+			     void **this_prologue_cache) const override
+  {
+    return prev_arch_p (this_frame, this_prologue_cache);
+  }
+};
+
 void
 tramp_frame_prepend_unwinder (struct gdbarch *gdbarch,
 			      const struct tramp_frame *tramp_frame)
@@ -161,16 +195,11 @@ tramp_frame_prepend_unwinder (struct gdbarch *gdbarch,
   gdb_assert (tramp_frame->insn_size <= sizeof (tramp_frame->insn[0].bytes));
 
   data = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_data);
-  unwinder = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_unwind);
-
   data->tramp_frame = tramp_frame;
-  unwinder->type = tramp_frame->frame_type;
-  unwinder->unwind_data = data;
-  unwinder->unwinder_class = FRAME_UNWIND_GDB;
-  unwinder->sniffer = tramp_frame_sniffer;
-  unwinder->stop_reason = default_frame_unwind_stop_reason;
-  unwinder->this_id = tramp_frame_this_id;
-  unwinder->prev_register = tramp_frame_prev_register;
-  unwinder->prev_arch = tramp_frame->prev_arch;
+
+  unwinder = obstack_new <frame_unwind_trampoline> (gdbarch_obstack (gdbarch),
+						    tramp_frame->frame_type,
+						    data,
+						    tramp_frame->prev_arch);
   frame_unwind_prepend_unwinder (gdbarch, unwinder);
 }
diff --git a/gdb/v850-tdep.c b/gdb/v850-tdep.c
index 66cb5004058..c88f4ab83de 100644
--- a/gdb/v850-tdep.c
+++ b/gdb/v850-tdep.c
@@ -1320,7 +1320,7 @@ v850_frame_this_id (const frame_info_ptr &this_frame, void **this_cache,
   *this_id = frame_id_build (cache->saved_regs[E_SP_REGNUM].addr (), cache->pc);
 }
 
-static const struct frame_unwind v850_frame_unwind = {
+static const struct frame_unwind_legacy v850_frame_unwind (
   "v850 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1329,7 +1329,7 @@ static const struct frame_unwind v850_frame_unwind = {
   v850_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 v850_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/vax-tdep.c b/gdb/vax-tdep.c
index 17d36a545fb..ced24a92cc4 100644
--- a/gdb/vax-tdep.c
+++ b/gdb/vax-tdep.c
@@ -386,8 +386,7 @@ vax_frame_prev_register (const frame_info_ptr &this_frame,
   return trad_frame_get_prev_register (this_frame, cache->saved_regs, regnum);
 }
 
-static const struct frame_unwind vax_frame_unwind =
-{
+static const struct frame_unwind_legacy vax_frame_unwind (
   "vax prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -396,7 +395,7 @@ static const struct frame_unwind vax_frame_unwind =
   vax_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 \f
 
 static CORE_ADDR
diff --git a/gdb/windows-tdep.c b/gdb/windows-tdep.c
index 361ccdd0dcb..8e232a5bd64 100644
--- a/gdb/windows-tdep.c
+++ b/gdb/windows-tdep.c
@@ -1321,10 +1321,9 @@ cygwin_sigwrapper_frame_cache (frame_info_ptr this_frame, void **this_cache)
   return cache;
 }
 
-static struct value *
-cygwin_sigwrapper_frame_prev_register (const frame_info_ptr &this_frame,
-				       void **this_cache,
-				       int regnum)
+struct value *
+cygwin_sigwrapper_frame_unwind::prev_register
+    (const frame_info_ptr &this_frame, void **this_cache, int regnum) const
 {
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
   struct cygwin_sigwrapper_frame_cache *cache
@@ -1340,18 +1339,18 @@ cygwin_sigwrapper_frame_prev_register (const frame_info_ptr &this_frame,
   return frame_unwind_got_register (this_frame, regnum, regnum);
 }
 
-static void
-cygwin_sigwrapper_frame_this_id (const frame_info_ptr &this_frame,
-				 void **this_cache,
-				 struct frame_id *this_id)
+void
+cygwin_sigwrapper_frame_unwind::this_id (const frame_info_ptr &this_frame,
+					 void **this_cache,
+					 struct frame_id *this_id) const
 {
   *this_id = frame_id_build_unavailable_stack (get_frame_func (this_frame));
 }
 
-static int
-cygwin_sigwrapper_frame_sniffer (const struct frame_unwind *self_,
-				 const frame_info_ptr &this_frame,
-				 void **this_cache)
+int
+cygwin_sigwrapper_frame_unwind::sniffer (const struct frame_unwind *self_,
+					 const frame_info_ptr &this_frame,
+					 void **this_cache) const
 {
   const auto *self = (const struct cygwin_sigwrapper_frame_unwind *) self_;
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
@@ -1396,13 +1395,7 @@ cygwin_sigwrapper_frame_sniffer (const struct frame_unwind *self_,
 
 cygwin_sigwrapper_frame_unwind::cygwin_sigwrapper_frame_unwind
   (gdb::array_view<const gdb::array_view<const gdb_byte>> patterns_list)
-    : frame_unwind (),
-      patterns_list (patterns_list)
+    : frame_unwind ("cygwin sigwrapper", NORMAL_FRAME, FRAME_UNWIND_GDB,
+		    nullptr), patterns_list (patterns_list)
 {
-  name = "cygwin sigwrapper";
-  type = NORMAL_FRAME;
-  stop_reason = default_frame_unwind_stop_reason;
-  this_id = cygwin_sigwrapper_frame_this_id;
-  prev_register = cygwin_sigwrapper_frame_prev_register;
-  sniffer = cygwin_sigwrapper_frame_sniffer;
 }
diff --git a/gdb/windows-tdep.h b/gdb/windows-tdep.h
index f122f7aaa61..bdab0962ede 100644
--- a/gdb/windows-tdep.h
+++ b/gdb/windows-tdep.h
@@ -60,8 +60,9 @@ extern bool is_linked_with_cygwin_dll (bfd *abfd);
 /* Cygwin sigwapper unwinder.  Unwinds signal frames over
    sigbe/sigdelayed.  */
 
-struct cygwin_sigwrapper_frame_unwind : public frame_unwind
+class cygwin_sigwrapper_frame_unwind : public frame_unwind
 {
+public:
   explicit cygwin_sigwrapper_frame_unwind
     (gdb::array_view<const gdb::array_view<const gdb_byte>> patterns_list);
 
@@ -73,6 +74,19 @@ struct cygwin_sigwrapper_frame_unwind : public frame_unwind
      If any pattern in the list matches, then the frame is assumed to
      be a sigwrapper frame.  */
   gdb::array_view<const gdb::array_view<const gdb_byte>> patterns_list;
+
+  /* Calculate the frame ID of a cygwin wrapper.  */
+  void this_id (const frame_info_ptr &this_frame, void **this_prologue_cache,
+		struct frame_id *id) const override;
+
+  /* Sniff the frame to tell if this unwinder should be used.  */
+  int sniffer (const frame_unwind *self, const frame_info_ptr &this_frame,
+	       void **this_prologue_cache) const override;
+
+  /* Calculate the value of a given register in the previous frame.  */
+  struct value *prev_register (const frame_info_ptr &this_frame,
+			       void **this_cache,
+			       int regnum) const override;
 };
 
 #endif
diff --git a/gdb/xstormy16-tdep.c b/gdb/xstormy16-tdep.c
index 46412737039..519e059439a 100644
--- a/gdb/xstormy16-tdep.c
+++ b/gdb/xstormy16-tdep.c
@@ -728,7 +728,7 @@ xstormy16_frame_base_address (const frame_info_ptr &this_frame, void **this_cach
   return cache->base;
 }
 
-static const struct frame_unwind xstormy16_frame_unwind = {
+static const struct frame_unwind_legacy xstormy16_frame_unwind (
   "xstormy16 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -737,7 +737,7 @@ static const struct frame_unwind xstormy16_frame_unwind = {
   xstormy16_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static const struct frame_base xstormy16_frame_base = {
   &xstormy16_frame_unwind,
diff --git a/gdb/xtensa-tdep.c b/gdb/xtensa-tdep.c
index 728774f955a..869414c57d8 100644
--- a/gdb/xtensa-tdep.c
+++ b/gdb/xtensa-tdep.c
@@ -1496,9 +1496,7 @@ xtensa_frame_prev_register (const frame_info_ptr &this_frame,
 }
 
 
-static const struct frame_unwind
-xtensa_unwind =
-{
+static const struct frame_unwind_legacy xtensa_unwind (
   "xtensa prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1507,7 +1505,7 @@ xtensa_unwind =
   xtensa_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 static CORE_ADDR
 xtensa_frame_base_address (const frame_info_ptr &this_frame, void **this_cache)
diff --git a/gdb/z80-tdep.c b/gdb/z80-tdep.c
index 4404d4bbd59..160e8b503fd 100644
--- a/gdb/z80-tdep.c
+++ b/gdb/z80-tdep.c
@@ -1063,9 +1063,7 @@ z80_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
   return 0;
 }
 
-static const struct frame_unwind
-z80_frame_unwind =
-{
+static const struct frame_unwind_legacy z80_frame_unwind (
   "z80",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1076,7 +1074,7 @@ z80_frame_unwind =
   default_frame_sniffer
   /*dealloc_cache*/
   /*prev_arch*/
-};
+);
 
 /* Initialize the gdbarch struct for the Z80 arch */
 static struct gdbarch *
-- 
2.43.2


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 4/4] GDB: introduce ability to disable frame unwinders
  2024-03-06 12:51 [PATCH 0/4] Modernize frame unwinders and add disable feature Guinevere Larsen
                   ` (2 preceding siblings ...)
  2024-03-06 12:51 ` [PATCH 3/4] gdb: Migrate frame unwinders to use C++ classes Guinevere Larsen
@ 2024-03-06 12:51 ` Guinevere Larsen
  2024-03-06 13:47   ` Eli Zaretskii
  2024-03-08 17:22   ` Tom Tromey
  2024-03-11 14:56 ` [PATCH 0/4] Modernize frame unwinders and add disable feature Luis Machado
  4 siblings, 2 replies; 23+ messages in thread
From: Guinevere Larsen @ 2024-03-06 12:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Guinevere Larsen

Sometimes, in the GDB testsuite, we want to test the ability of specific
unwinders to handle some piece of code. Usually this is done by trying
to outsmart GDB, or by coercing the compiler to remove information that
GDB would rely on.  Both approaches have problems as GDB gets smarter
with time, and that compilers might differ in version and behavior, or
simply introduce new useful information.

To improve our ability to thoroughly test GDB, this patch introduces a
new maintenance command that allows a user to disable some unwinders,
based on either the name of the unwinder or on its class. With this
change, it will now be possible for GDB to not find any frame unwinders
for a given frame, which would previously cause GDB to assert. GDB will
now check if any frame unwinder has been disabled, and if some has, it
will just error out instead of asserting.

Unwinders can be disabled or re-enabled in 3 different ways:
* Disabling/enabling all at once (using '-all').
* By specifying an unwinder class to be disabled (option '-class').
* By specifying the name of an unwinder (option '-name').

If you give no options to the command, GDB assumes the input is an
unwinder class. '-class' would make no difference if used, is just here
for completeness.

This command is meant to be used once the inferior is already at the
desired location for the test. An example session would be:

(gdb) start
Temporary breakpoint 1, main () at omp.c:17
17          func();
(gdb) maint frame-unwinder disable ARCH
(gdb) bt
\#0  main () at omp.c:17
(gdb) maint frame-unwinder enable ARCH
(gdb) cont
Continuing.
---
 gdb/NEWS                                      |   7 +
 gdb/doc/gdb.texinfo                           |  24 +++
 gdb/frame-unwind.c                            | 161 +++++++++++++++++-
 gdb/frame-unwind.h                            |  13 ++
 gdb/testsuite/gdb.base/frame-unwind-disable.c |  21 +++
 .../gdb.base/frame-unwind-disable.exp         | 114 +++++++++++++
 6 files changed, 336 insertions(+), 4 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/frame-unwind-disable.c
 create mode 100644 gdb/testsuite/gdb.base/frame-unwind-disable.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 2638b3e0d9c..5598a8d5a09 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -43,6 +43,13 @@ disable missing-debug-handler LOCUS HANDLER
 maintenance info linux-lwps
   List all LWPs under control of the linux-nat target.
 
+maintenance frame-unwinder disable [-name]
+maintenance frame-unwinder enable [-name]
+  Enable or disable frame unwinders.  This is only meant to be used when
+  testing unwinders themselves, and you want to ensure that a fallback
+  algorithm won't obscure a regression. GDB is not expected to behave well
+  if you try to execute the inferior with unwinders disabled.
+
 set remote thread-options-packet
 show remote thread-options-packet
   Set/show the use of the thread options packet.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 34cd567f811..79290576654 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -41921,6 +41921,30 @@ architecture, then enabling this flag does not cause them to be used.
 @item maint info frame-unwinders
 List the frame unwinders currently in effect, starting with the highest priority.
 
+@kindex maint frame-unwinder disable
+@kindex maint frame-unwinder enable
+@item maint frame-unwinder disable [OPTION] UNWINDER
+@item maint frame-unwinder enable [OPTION] UNWINDER
+Disable or enable frame unwinders. This is meant only as a testing tool, and
+@value{GDBN} is not guaranteed to work properly if it is unable to find
+valid frame unwinders.
+
+The meaning of the argument @samp{unwinder} depends on the @samp{option}
+provided. The following options are accepted:
+
+@table @samp
+@item @code{-all}
+ignore @samp{unwinder} and disable/enable all unwinders
+@item @code{-class}
+@samp{unwinder} is the class on frame unwinders to be disabled or enabled.
+The class may include the prefix @code{FRAME_UNWINDER_}, but it is not
+required.  This is the default option.
+@item @code{-name}
+@samp{unwinder} is the exact name of the unwinder to be disabled or enabled.
+@end table
+
+@samp{Unwinder} is always case insensitive
+
 @kindex maint set worker-threads
 @kindex maint show worker-threads
 @item maint set worker-threads
diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
index 2e35732d763..945276ef1e7 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -88,6 +88,23 @@ frame_unwinder_class_str (frame_unwind_class uclass)
   return location->second.c_str ();
 }
 
+static enum frame_unwind_class
+str_to_frame_unwind_class (const char **c_str)
+{
+  std::string full_name = "FRAME_UNWIND_";
+  const int start_length = full_name.length ();
+  if (strncasecmp (*c_str, full_name.c_str (), start_length) == 0)
+    full_name = *c_str;
+  else
+    full_name += *c_str;
+  for (const auto& it : unwind_class_conversion)
+    {
+      if (strcasecmp(it.second.c_str (), full_name.c_str ()) == 0)
+	return it.first;
+    }
+  error (_("Unknown frame unwind class: %s"), *c_str);
+}
+
 void
 frame_unwind_prepend_unwinder (struct gdbarch *gdbarch,
 				const struct frame_unwind *unwinder)
@@ -185,25 +202,47 @@ frame_unwind_find_by_frame (const frame_info_ptr &this_frame, void **this_cache)
 
   const struct frame_unwind *unwinder_from_target;
 
+  /* If we see a disabled unwinder, we assume some test is being run on
+     GDB, and we don't want to assert at the end of this function.  */
+  bool seen_disabled_unwinder = false;
+
   unwinder_from_target = target_get_unwinder ();
   if (unwinder_from_target != NULL
+      && unwinder_from_target->enabled ()
       && frame_unwind_try_unwinder (this_frame, this_cache,
 				   unwinder_from_target))
     return;
+  else if (unwinder_from_target != nullptr
+	   && !unwinder_from_target->enabled ())
+    seen_disabled_unwinder = true;
 
   unwinder_from_target = target_get_tailcall_unwinder ();
   if (unwinder_from_target != NULL
+      && unwinder_from_target->enabled ()
       && frame_unwind_try_unwinder (this_frame, this_cache,
 				   unwinder_from_target))
     return;
+  else if (unwinder_from_target != nullptr
+	   && !unwinder_from_target->enabled ())
+    seen_disabled_unwinder |= true;
 
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
   std::vector<const frame_unwind*> table = get_frame_unwind_table (gdbarch);
   for (auto unwinder: table)
-    if (frame_unwind_try_unwinder (this_frame, this_cache, unwinder))
-      return;
+    {
+      if (!unwinder->enabled ())
+	{
+	  seen_disabled_unwinder |= true;
+	  continue;
+	}
+      if (frame_unwind_try_unwinder (this_frame, this_cache, unwinder))
+	return;
+    }
 
-  internal_error (_("frame_unwind_find_by_frame failed"));
+  if (seen_disabled_unwinder)
+    error (_("frame_unwind_find_by_frame failed"));
+  else
+    internal_error (_("frame_unwind_find_by_frame failed"));
 }
 
 /* A default frame sniffer which always accepts the frame.  Used by
@@ -416,10 +455,11 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
   std::vector<const frame_unwind*> table = get_frame_unwind_table (gdbarch);
 
   ui_out *uiout = current_uiout;
-  ui_out_emit_table table_emitter (uiout, 3, -1, "FrameUnwinders");
+  ui_out_emit_table table_emitter (uiout, 4, -1, "FrameUnwinders");
   uiout->table_header (27, ui_left, "name", "Name");
   uiout->table_header (25, ui_left, "type", "Type");
   uiout->table_header (25, ui_left, "class", "Class");
+  uiout->table_header (8, ui_left, "enabled", "Enabled");
   uiout->table_body ();
 
   for (const struct frame_unwind* unwinder: table)
@@ -427,15 +467,95 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
       const char *name = unwinder->name ();
       const char *type = frame_type_str (unwinder->type ());
       const char *uclass = frame_unwinder_class_str (unwinder->unwinder_class ());
+      const char *enabled = (unwinder->enabled ()) ? "Y" : "N";
 
       ui_out_emit_list tuple_emitter (uiout, nullptr);
       uiout->field_string ("name", name);
       uiout->field_string ("type", type);
       uiout->field_string ("class", uclass);
+      uiout->field_string ("enabled", enabled);
       uiout->text ("\n");
     }
 }
 
+/* Helper function to both enable and disable frame unwinders.
+   if ENABLE is true, this call will be enabling unwinders,
+   otherwise the unwinders will be disabled.  */
+static void
+enable_disable_frame_unwinders (const char *args, int from_tty, bool enable)
+{
+
+  reinit_frame_cache ();
+  if (args == nullptr)
+    {
+      error (_("specify which frame unwinder(s) should be %s"),
+	     (enable)? "enabled" : "disabled");
+    }
+  struct gdbarch* gdbarch = current_inferior ()->arch ();
+  std::vector<const frame_unwind *> unwinder_list
+    = gdbarch_get_unwinder_list (gdbarch);
+
+  /* First see if the user wants to change all unwinders.  */
+  if (check_for_argument (&args, "-all"))
+    {
+      for (const frame_unwind *u : unwinder_list)
+	{
+	  u->set_enabled (enable);
+	}
+      return;
+    }
+
+  /* If user entered a specific unwinder name, handle it here.  If the
+     unwinder is already at the expected state, error out.  */
+  if (check_for_argument (&args, "-name"))
+    {
+      bool did_something = false;
+      for (const frame_unwind *unwinder : unwinder_list)
+	{
+	  if (strcasecmp (unwinder->name (), args) == 0)
+	    {
+	      if (unwinder->enabled () == enable)
+		error (_("unwinder %s is already %s"),
+			 unwinder->name (),
+			 (unwinder->enabled ()) ? ("enabled") : ("disabled"));
+	      unwinder->set_enabled (enable);
+
+	      did_something = true;
+	      break;
+	    }
+	}
+      if (!did_something)
+	error (_("couldn't find unwinder named %s"),args);
+    }
+  else
+    {
+      /* Discard '-class' argument, if provided.  */
+      check_for_argument (&args, "-class");
+      enum frame_unwind_class dclass = str_to_frame_unwind_class (&args);
+      for (auto unwinder: unwinder_list)
+	{
+	  if (unwinder->unwinder_class () == dclass)
+	    {
+	      unwinder->set_enabled (enable);
+	    }
+	}
+    }
+}
+
+/* Implement "maint frame-unwinders disable" command.  */
+static void
+maintenance_disable_frame_unwinders (const char *args, int from_tty)
+{
+  enable_disable_frame_unwinders (args, from_tty, false);
+}
+
+/* Implement "maint frame-unwinders disable" command.  */
+static void
+maintenance_enable_frame_unwinders (const char *args, int from_tty)
+{
+  enable_disable_frame_unwinders (args, from_tty, true);
+}
+
 void _initialize_frame_unwind ();
 void
 _initialize_frame_unwind ()
@@ -447,4 +567,37 @@ _initialize_frame_unwind ()
 	   _("List the frame unwinders currently in effect, "
 	     "starting with the highest priority."),
 	   &maintenanceinfolist);
+
+  /* Add "maint frame-unwinder disable/enable".  */
+  static struct cmd_list_element *maint_frame_unwinder;
+
+  add_basic_prefix_cmd ("frame-unwinder", class_maintenance,
+			_("Commands handling frame unwinders."),
+			&maint_frame_unwinder, 0, &maintenancelist);
+
+  add_cmd ("disable", class_maintenance, maintenance_disable_frame_unwinders,
+	   _("\
+Disable one or more frame unwinder(s).\n\
+Usage: maint frame-unwinder disable [OPTION] UNWINDER\n\
+\n\
+The meaning of UNWINDER depends on the OPTION given. These are the possibilities:\n\
+\t-all    - UNWINDER is ignored. All available unwinders will be disabled\n\
+\t-name   - UNWINDER is the exact name of the frame unwinder is to be disabled\n\
+\t-class  - UNWINDER is the class of unwinders to be disabled.\n\
+\n\
+UNWINDER is case insensitive."),
+	   & maint_frame_unwinder);
+
+  add_cmd ("enable", class_maintenance, maintenance_enable_frame_unwinders,
+	   _("\
+Enable one or more frame unwinder(s).\n\
+Usage: maint frame-unwinder enable [OPTION] UNWINDER\n\
+\n\
+The meaning of UNWINDER depends on the OPTION given. These are the possibilities:\n\
+\t-all    - UNWINDER is ignored. All available unwinders will be enabled\n\
+\t-name   - UNWINDER is the exact name of the frame unwinder is to be enabled\n\
+\t-class  - UNWINDER is the class of unwinders to be enabled.\n\
+\n\
+UNWINDER is case insensitive."),
+	   & maint_frame_unwinder);
 }
diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
index eff36f7eab5..3a0394d249a 100644
--- a/gdb/frame-unwind.h
+++ b/gdb/frame-unwind.h
@@ -175,6 +175,9 @@ class frame_unwind
      unwinding.  */
   enum frame_unwind_class m_unwinder_class;
   const struct frame_data *m_unwind_data;
+
+  /* Whether this unwinder can be used when sniffing.  */
+  mutable bool m_enabled = true;
 public:
   frame_unwind (const char *n, frame_type t, frame_unwind_class c,
 		       const struct frame_data *d)
@@ -200,6 +203,16 @@ class frame_unwind
     return m_unwind_data;
   }
 
+  bool enabled () const
+  {
+    return m_enabled;
+  }
+
+  void set_enabled (bool state) const
+  {
+    m_enabled = state;
+  }
+
   /* Default stop_reason function.  It reports NO_REASON, unless the
      frame is the outermost.  */
   virtual enum unwind_stop_reason stop_reason (const frame_info_ptr &this_frame,
diff --git a/gdb/testsuite/gdb.base/frame-unwind-disable.c b/gdb/testsuite/gdb.base/frame-unwind-disable.c
new file mode 100644
index 00000000000..078517c011e
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-unwind-disable.c
@@ -0,0 +1,21 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2024 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/>.  */
+
+int main ()
+{
+    return 0;
+}
diff --git a/gdb/testsuite/gdb.base/frame-unwind-disable.exp b/gdb/testsuite/gdb.base/frame-unwind-disable.exp
new file mode 100644
index 00000000000..4efc87ccfcf
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-unwind-disable.exp
@@ -0,0 +1,114 @@
+# Copyright 2024 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/>.
+
+# Test multiple situations in which we may use the maintenance command to
+# disable and enable frame unwinders, and check that they really are
+# disabled when they say the are.
+
+standard_testfile .c
+
+# Proc to check if the unwinder of the given name is in the desired state.
+# STATE can be either Y or N.
+proc check_unwinder_state { unwinder_name state {testname ""} } {
+    if {${testname} == ""} {
+	set testname "checking state ${state} for ${unwinder_name}"
+    }
+    gdb_test "maint info frame-unwinders" \
+	".*${unwinder_name}\\s+\\w+\\s+\\w+.+${state}.*" \
+	"${testname}"
+}
+
+proc check_unwinder_class { unwinder_class state {testname ""} } {
+    set should_pass true
+    if {$testname == ""} {
+	set testname "checking if $unwinder_class state is $state"
+    }
+    gdb_test_multiple "maint info frame-unwinders" "collect for $testname" -lbl {
+	-re "^\\w+\\s+\\w+\\s+${unwinder_class}\\s+\(.\)\\s+" {
+	    set cur_state $expec_out(1, string)
+	    if {$cur_state != $state} {
+		set should_pass false
+	    }
+	    exp_continue
+	}
+	-re "${::gdb_prompt} $" {
+	    pass $gdb_test_name
+	}
+    }
+
+    gdb_assert {$should_pass == true} "$testname"
+}
+
+if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile} ]} {
+    return -1
+}
+
+if {![runto_main]} {
+    untested "couldn't run to main"
+    return
+}
+
+# Test disabling all unwinders
+check_unwinder_class "FRAME_UNWIND_\\w+" "Y" \
+    "Checking all unwinders before any changes"
+gdb_test_no_output "maint frame-unwinder disable -all"
+check_unwinder_class "FRAME_UNWIND_\\w+" "N" \
+    "Checking all unwinders were properly disabled"
+
+# Test if GDB can still make a backtrace once all unwinders are disabled.
+# It should be impossible.
+gdb_test "backtrace" ".*frame_unwind_find_by_frame failed.*" \
+    "confirm that no suitable unwinders are found"
+
+# Reenable all unwinders
+gdb_test_no_output "maint frame-unwinder enable -all"
+check_unwinder_class "FRAME_UNWIND_\\w+" "Y" \
+    "Checking all unwinders after re-enabling them all"
+
+# Check that we are able to get backtraces once again.
+gdb_test "backtrace" ".0\\s+main .. at.*" \
+    "confirm we now get usable backtraces"
+
+# Check if we can disable an unwinder based on the name.
+check_unwinder_state "dummy" "Y"
+gdb_test_no_output "maint frame-unwinder disable -name dummy"
+check_unwinder_state "dummy" "N"
+# And verify what happens if you try to disable it again.
+gdb_test "maint frame-unwinder disable -name dummy" \
+    "unwinder dummy is already disabled" \
+    "disable already disabled unwinder"
+check_unwinder_state "dummy" "N" "check if dummy continues disabled"
+
+foreach class {GDB ARCH DEBUGINFO EXTENSION} {
+    # Disable all unwinders of type CLASS, and check that the command worked.
+    gdb_test_no_output "maint frame-unwinder disable ${class}"
+    check_unwinder_class "FRAME_UNWINDER_${class}" "N"
+}
+
+# Now check if we are able to enable a single unwinder, and what happens if we
+# enable it twice.
+gdb_test_no_output "maint frame-unwinder enable -name dummy"
+check_unwinder_state "dummy" "Y" "successfully enabled dummy unwinder"
+gdb_test "maint frame-unwinder enable -name dummy" \
+    "unwinder dummy is already enabled" \
+    "enable already enabled unwinder"
+check_unwinder_state "dummy" "Y" "check if dummy continues enabled"
+
+foreach class {GDB ARCH DEBUGINFO EXTENSION} {
+    # Enable all unwinders of type CLASS, and check that the command worked.
+    # using "-class" option to ensure it works.  Should make no difference.
+    gdb_test_no_output "maint frame-unwinder enable -class ${class}"
+    check_unwinder_class "FRAME_UNWINDER_${class}" "Y"
+}
-- 
2.43.2


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/4] GDB: introduce ability to disable frame unwinders
  2024-03-06 12:51 ` [PATCH 4/4] GDB: introduce ability to disable frame unwinders Guinevere Larsen
@ 2024-03-06 13:47   ` Eli Zaretskii
  2024-03-06 14:07     ` Guinevere Larsen
  2024-03-08 17:22   ` Tom Tromey
  1 sibling, 1 reply; 23+ messages in thread
From: Eli Zaretskii @ 2024-03-06 13:47 UTC (permalink / raw)
  To: Guinevere Larsen; +Cc: gdb-patches, blarsen

> From: Guinevere Larsen <blarsen@redhat.com>
> Cc: Guinevere Larsen <blarsen@redhat.com>
> Date: Wed,  6 Mar 2024 13:51:35 +0100
> 
>  gdb/NEWS                                      |   7 +
>  gdb/doc/gdb.texinfo                           |  24 +++
>  gdb/frame-unwind.c                            | 161 +++++++++++++++++-
>  gdb/frame-unwind.h                            |  13 ++
>  gdb/testsuite/gdb.base/frame-unwind-disable.c |  21 +++
>  .../gdb.base/frame-unwind-disable.exp         | 114 +++++++++++++
>  6 files changed, 336 insertions(+), 4 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.base/frame-unwind-disable.c
>  create mode 100644 gdb/testsuite/gdb.base/frame-unwind-disable.exp

Thanks.

> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -43,6 +43,13 @@ disable missing-debug-handler LOCUS HANDLER
>  maintenance info linux-lwps
>    List all LWPs under control of the linux-nat target.
>  
> +maintenance frame-unwinder disable [-name]
> +maintenance frame-unwinder enable [-name]
> +  Enable or disable frame unwinders.  This is only meant to be used when
> +  testing unwinders themselves, and you want to ensure that a fallback
> +  algorithm won't obscure a regression. GDB is not expected to behave well
                                         ^^
Two spaces between sentences, please.

> +@kindex maint frame-unwinder disable
> +@kindex maint frame-unwinder enable
> +@item maint frame-unwinder disable [OPTION] UNWINDER
> +@item maint frame-unwinder enable [OPTION] UNWINDER

OPTION and UNWINDER should be @var{option} and @var{unwinder},
respectively.

> +The meaning of the argument @samp{unwinder} depends on the @samp{option}

The correct markup here is @var, not @samp, since these are not
literal symbols, but instead names of parameters that stand for
something else.  Likewise elsewhere in the gdb.texinfo part of the
patch.

> +@table @samp
> +@item @code{-all}
> +ignore @samp{unwinder} and disable/enable all unwinders

This should be a complete sentence: begin with a capital letter and
end with a period.

> +@item @code{-class}
> +@samp{unwinder} is the class on frame unwinders to be disabled or enabled.
                                ^^
This should probably be "of"?

> +The class may include the prefix @code{FRAME_UNWINDER_}, but it is not
> +required.  This is the default option.

What do you mean by the last sentence?  What is "this" that is the
default option?  And what does "default" mean for an option, since an
option is by definition something that doesn't happen by default.

> +Disable one or more frame unwinder(s).\n\
> +Usage: maint frame-unwinder disable [OPTION] UNWINDER\n\
> +\n\
> +The meaning of UNWINDER depends on the OPTION given. These are the possibilities:\n\
                                                      ^^
> +\t-all    - UNWINDER is ignored. All available unwinders will be disabled\n\
                                  ^^

Two spaces between sentences in doc strings as well.

> +The meaning of UNWINDER depends on the OPTION given. These are the possibilities:\n\
> +\t-all    - UNWINDER is ignored. All available unwinders will be enabled\n\

Likewise here.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/4] GDB: introduce ability to disable frame unwinders
  2024-03-06 13:47   ` Eli Zaretskii
@ 2024-03-06 14:07     ` Guinevere Larsen
  2024-03-06 14:16       ` Eli Zaretskii
  0 siblings, 1 reply; 23+ messages in thread
From: Guinevere Larsen @ 2024-03-06 14:07 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

On 06/03/2024 14:47, Eli Zaretskii wrote:
>> From: Guinevere Larsen <blarsen@redhat.com>
>> Cc: Guinevere Larsen <blarsen@redhat.com>
>> Date: Wed,  6 Mar 2024 13:51:35 +0100
>>
>>   gdb/NEWS                                      |   7 +
>>   gdb/doc/gdb.texinfo                           |  24 +++
>>   gdb/frame-unwind.c                            | 161 +++++++++++++++++-
>>   gdb/frame-unwind.h                            |  13 ++
>>   gdb/testsuite/gdb.base/frame-unwind-disable.c |  21 +++
>>   .../gdb.base/frame-unwind-disable.exp         | 114 +++++++++++++
>>   6 files changed, 336 insertions(+), 4 deletions(-)
>>   create mode 100644 gdb/testsuite/gdb.base/frame-unwind-disable.c
>>   create mode 100644 gdb/testsuite/gdb.base/frame-unwind-disable.exp
> Thanks.
>
>> --- a/gdb/NEWS
>> +++ b/gdb/NEWS
>> @@ -43,6 +43,13 @@ disable missing-debug-handler LOCUS HANDLER
>>   maintenance info linux-lwps
>>     List all LWPs under control of the linux-nat target.
>>   
>> +maintenance frame-unwinder disable [-name]
>> +maintenance frame-unwinder enable [-name]
>> +  Enable or disable frame unwinders.  This is only meant to be used when
>> +  testing unwinders themselves, and you want to ensure that a fallback
>> +  algorithm won't obscure a regression. GDB is not expected to behave well
>                                           ^^
> Two spaces between sentences, please.
>
>> +@kindex maint frame-unwinder disable
>> +@kindex maint frame-unwinder enable
>> +@item maint frame-unwinder disable [OPTION] UNWINDER
>> +@item maint frame-unwinder enable [OPTION] UNWINDER
> OPTION and UNWINDER should be @var{option} and @var{unwinder},
> respectively.
>
>> +The meaning of the argument @samp{unwinder} depends on the @samp{option}
> The correct markup here is @var, not @samp, since these are not
> literal symbols, but instead names of parameters that stand for
> something else.  Likewise elsewhere in the gdb.texinfo part of the
> patch.

Ah, thanks for explaining! Should I also change the @samp in the table 
section?

>
>> +@table @samp
>> +@item @code{-all}
>> +ignore @samp{unwinder} and disable/enable all unwinders
> This should be a complete sentence: begin with a capital letter and
> end with a period.
>
>> +@item @code{-class}
>> +@samp{unwinder} is the class on frame unwinders to be disabled or enabled.
>                                  ^^
> This should probably be "of"?
>
>> +The class may include the prefix @code{FRAME_UNWINDER_}, but it is not
>> +required.  This is the default option.
> What do you mean by the last sentence?  What is "this" that is the
> default option?  And what does "default" mean for an option, since an
> option is by definition something that doesn't happen by default.

option is an optional parameter, and if the user decides to not provide 
it, the command will work as if the user had provided the -class option.

I can see where the confusion came from, but I'm not really sure how to 
improve the wording.

>
>> +Disable one or more frame unwinder(s).\n\
>> +Usage: maint frame-unwinder disable [OPTION] UNWINDER\n\
>> +\n\
>> +The meaning of UNWINDER depends on the OPTION given. These are the possibilities:\n\
>                                                        ^^
>> +\t-all    - UNWINDER is ignored. All available unwinders will be disabled\n\
>                                    ^^
>
> Two spaces between sentences in doc strings as well.
>
>> +The meaning of UNWINDER depends on the OPTION given. These are the possibilities:\n\
>> +\t-all    - UNWINDER is ignored. All available unwinders will be enabled\n\
> Likewise here.
>
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
>

-- 
Cheers,
Guinevere Larsen
She/Her/Hers


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/4] GDB: introduce ability to disable frame unwinders
  2024-03-06 14:07     ` Guinevere Larsen
@ 2024-03-06 14:16       ` Eli Zaretskii
  0 siblings, 0 replies; 23+ messages in thread
From: Eli Zaretskii @ 2024-03-06 14:16 UTC (permalink / raw)
  To: Guinevere Larsen; +Cc: gdb-patches

> Date: Wed, 6 Mar 2024 15:07:17 +0100
> Cc: gdb-patches@sourceware.org
> From: Guinevere Larsen <blarsen@redhat.com>
> 
> >> +The meaning of the argument @samp{unwinder} depends on the @samp{option}
> > The correct markup here is @var, not @samp, since these are not
> > literal symbols, but instead names of parameters that stand for
> > something else.  Likewise elsewhere in the gdb.texinfo part of the
> > patch.
> 
> Ah, thanks for explaining! Should I also change the @samp in the table 
> section?

Only for @samp{unwinder} and @samp{option}.  The rest are literals,
like "-all".


> >> +The class may include the prefix @code{FRAME_UNWINDER_}, but it is not
> >> +required.  This is the default option.
> > What do you mean by the last sentence?  What is "this" that is the
> > default option?  And what does "default" mean for an option, since an
> > option is by definition something that doesn't happen by default.
> 
> option is an optional parameter, and if the user decides to not provide 
> it, the command will work as if the user had provided the -class option.

Then I suggest to rephrase like this:

  By default, @var{unwinder} is interpreted as a class, even if
  @samp{-class} was omitted.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 3/4] gdb: Migrate frame unwinders to use C++ classes
  2024-03-06 12:51 ` [PATCH 3/4] gdb: Migrate frame unwinders to use C++ classes Guinevere Larsen
@ 2024-03-07 11:01   ` Lancelot SIX
  2024-03-07 11:04     ` Guinevere Larsen
  2024-03-08 17:07   ` Tom Tromey
  1 sibling, 1 reply; 23+ messages in thread
From: Lancelot SIX @ 2024-03-07 11:01 UTC (permalink / raw)
  To: Guinevere Larsen; +Cc: gdb-patches

On Wed, Mar 06, 2024 at 01:51:34PM +0100, Guinevere Larsen wrote:

Hi Gwen,

I have not read the specific of this series, but testing the patches as
is causes a build failure on amdgpu-tdep.c

> diff --git a/gdb/amdgpu-tdep.c b/gdb/amdgpu-tdep.c
> index 8189de8e565..45c83a562e8 100644
> --- a/gdb/amdgpu-tdep.c
> +++ b/gdb/amdgpu-tdep.c
> @@ -892,7 +892,7 @@ amdgpu_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
>    return frame_unwind_got_register (this_frame, regnum, regnum);
>  }
>  
> -static const frame_unwind amdgpu_frame_unwind = {
> +static const frame_unwind_legacy amdgpu_frame_unwind (
>    "amdgpu",
>    NORMAL_FRAME,
>    FRAME_UNWIND_ARCH,
> @@ -903,7 +903,7 @@ static const frame_unwind amdgpu_frame_unwind = {
>    default_frame_sniffer,
>    nullptr,
>    nullptr,
            ^
Moving from the aggregate initializer to a regular constructor call,
this last coma must be removed.

With this fixed, I tested this series on the gdb.rocm/*.exp part (both
on upstream GDB and our downstream port) and see no regression.

I'll try to actually read the actual later.

Best,
Lancelot.

> -};
> +);
>  
>  static int
>  print_insn_amdgpu (bfd_vma memaddr, struct disassemble_info *info)
> 
> -- 
> 2.43.2
> 

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 3/4] gdb: Migrate frame unwinders to use C++ classes
  2024-03-07 11:01   ` Lancelot SIX
@ 2024-03-07 11:04     ` Guinevere Larsen
  0 siblings, 0 replies; 23+ messages in thread
From: Guinevere Larsen @ 2024-03-07 11:04 UTC (permalink / raw)
  To: Lancelot SIX; +Cc: gdb-patches

On 07/03/2024 12:01, Lancelot SIX wrote:
> On Wed, Mar 06, 2024 at 01:51:34PM +0100, Guinevere Larsen wrote:
>
> Hi Gwen,
>
> I have not read the specific of this series, but testing the patches as
> is causes a build failure on amdgpu-tdep.c
>
>> diff --git a/gdb/amdgpu-tdep.c b/gdb/amdgpu-tdep.c
>> index 8189de8e565..45c83a562e8 100644
>> --- a/gdb/amdgpu-tdep.c
>> +++ b/gdb/amdgpu-tdep.c
>> @@ -892,7 +892,7 @@ amdgpu_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache,
>>     return frame_unwind_got_register (this_frame, regnum, regnum);
>>   }
>>   
>> -static const frame_unwind amdgpu_frame_unwind = {
>> +static const frame_unwind_legacy amdgpu_frame_unwind (
>>     "amdgpu",
>>     NORMAL_FRAME,
>>     FRAME_UNWIND_ARCH,
>> @@ -903,7 +903,7 @@ static const frame_unwind amdgpu_frame_unwind = {
>>     default_frame_sniffer,
>>     nullptr,
>>     nullptr,
>              ^
> Moving from the aggregate initializer to a regular constructor call,
> this last coma must be removed.
>
> With this fixed, I tested this series on the gdb.rocm/*.exp part (both
> on upstream GDB and our downstream port) and see no regression.

Huh... it seems that the amdgpu target is not compiled when I used 
`--enable-targets=all`, since I didn't get this build failure locally.

Thanks for spotting that, I'll update the patch locally!

-- 
Cheers,
Guinevere Larsen
She/Her/Hers

>
> I'll try to actually read the actual later.
>
> Best,
> Lancelot.
>
>> -};
>> +);
>>   
>>   static int
>>   print_insn_amdgpu (bfd_vma memaddr, struct disassemble_info *info)
>>
>> -- 
>> 2.43.2
>>


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 1/4] gdb: make gdbarch store a vector of frame unwinders
  2024-03-06 12:51 ` [PATCH 1/4] gdb: make gdbarch store a vector of frame unwinders Guinevere Larsen
@ 2024-03-08 16:34   ` Tom Tromey
  2024-03-11 10:51     ` Guinevere Larsen
  0 siblings, 1 reply; 23+ messages in thread
From: Tom Tromey @ 2024-03-08 16:34 UTC (permalink / raw)
  To: Guinevere Larsen; +Cc: gdb-patches

>>>>> Guinevere Larsen <blarsen@redhat.com> writes:

> Before this commit, all frame unwinders would be stored in the obstack
> of a gdbarch and accessed by using the registry system. This made for
> unwieldy code, and unnecessarily complex logic in the frame_unwinder
> implementation, along with making frame_unwind structs be unable to have
> non-trivial constructors.

> Seeing as a future patch of this series wants to refactor the
> frame_unwind struct to use inheritance, obstack storage would no longer
> be viable. In preparation for that change, this commit adds an
> std::vector to gdbarch to store the unwinders in.

> There should be no user-visible changes.

I'm not really sure about this patch.

Like on the one hand, it is fine.  The arch is going to store the
unwinder table.

On the other hand, the registry system is there to let modules be kind
of independent.  The lines are blurry though.

> +std::vector<const frame_unwind*>&

Missing spaces in here.

> -static const registry<gdbarch>::key<struct frame_unwind_table>
> -     frame_unwind_data;

An alternative approach would be to just use a different type in here.

This can use the default destruction approach and then it's just
allocated with 'new'.  So then you can use any old C++ type.

FWIW I think the real issue with obstack allocation isn't constructors
but destruction.  See https://sourceware.org/pipermail/gdb-patches/2024-February/206888.html

Tom

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 2/4] gdb: add "unwinder class" to frame unwinders
  2024-03-06 12:51 ` [PATCH 2/4] gdb: add "unwinder class" to " Guinevere Larsen
@ 2024-03-08 16:40   ` Tom Tromey
  0 siblings, 0 replies; 23+ messages in thread
From: Tom Tromey @ 2024-03-08 16:40 UTC (permalink / raw)
  To: Guinevere Larsen; +Cc: gdb-patches

>>>>> Guinevere Larsen <blarsen@redhat.com> writes:

> A future patch will add a way to disable certain unwinders based on
> different characteristics. This patch aims to make it more convenient
> to disable related unwinders in bulk, such as architecture specific
> ones, by indentifying all unwinders by which part of the code adds it.
> The classes, and explanations, are as follows:

Thanks.

> +/* Conversion list between the enum for frame_unwind_class and
> +   string.  */
> +static std::map<enum frame_unwind_class, std::string> unwind_class_conversion =

This should probably map to const char *.
That would avoid some unnecessary allocations.

 
> +enum frame_unwind_class {
> +  FRAME_UNWIND_GDB,
> +  FRAME_UNWIND_EXTENSION,
> +  FRAME_UNWIND_DEBUGINFO,
> +  FRAME_UNWIND_ARCH,
> +};

This should have a bunch of comments.
Also the "{" should probably be on the next line.

Tom

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 3/4] gdb: Migrate frame unwinders to use C++ classes
  2024-03-06 12:51 ` [PATCH 3/4] gdb: Migrate frame unwinders to use C++ classes Guinevere Larsen
  2024-03-07 11:01   ` Lancelot SIX
@ 2024-03-08 17:07   ` Tom Tromey
  2024-03-12 16:24     ` Guinevere Larsen
  1 sibling, 1 reply; 23+ messages in thread
From: Tom Tromey @ 2024-03-08 17:07 UTC (permalink / raw)
  To: Guinevere Larsen; +Cc: gdb-patches

>>>>> Guinevere Larsen <blarsen@redhat.com> writes:

> Frame unwinders have historically been a structure populated with
> callback pointers, so that architectures (or other specific unwinders)
> could install their own way to handle the inferior. However, since
> moving to c++, we could use polymorphism to get the same functionality
> in a more readable way. Polymorphism also makes it simpler to add new
> functionality to all frame unwinders, since all that's required is
> adding it to the base class.

> As part of the changes to add support to disabling frame unwinders,
> this commit makes the first baby step in  using polymorphism for the
> frame unwinders, by making frame_unwind a virtual class, and adds a
> couple of new classes. The main class added is frame_unwind_legacy,
> which works the same as the previous structs, using function pointers
> as callbacks. This class was added to allow the transition to happen
> piecemeal. New unwinders should instead follow the lead of the other
> classes implemented.

> 2 of the others, frame_unwind_python and frame_unwind_trampoline, were added
> because it seemed simpler at the moment to do that instead of reworking
> the dynamic allocation to work with the legacy class, and can be used as
> an example to future implementations.

>  /* AArch64 prologue unwinder.  */
> -static frame_unwind aarch64_prologue_unwind =
> -{
> +static frame_unwind_legacy aarch64_prologue_unwind (

Some of these should probably be 'const'...  not really your problem but
the patch points it out.

> +/* See frame-unwind.h.  */
> +
> +enum unwind_stop_reason
> +frame_unwind::stop_reason (const frame_info_ptr &this_frame,
> +			   void **this_prologue_cache) const
> +{
> +  return default_frame_unwind_stop_reason (this_frame, this_prologue_cache);
> +}
> +

It looks like all of these methods here could just be inline in the
header.

> -struct frame_unwind
> +class frame_unwind
>  {
> -  const char *name;
> +private:

class defaults to private.

> +  frame_unwind (const char *n, frame_type t, frame_unwind_class c,
> +		       const struct frame_data *d)
> +    : m_name (n), m_type (t), m_unwinder_class (c), m_unwind_data (d) { }

This looks weird.

> +  /* Calculate the ID of the given frame using this unwinder.  */
> +  virtual void this_id (const frame_info_ptr &this_frame,
> +			void **this_prologue_cache,
> +			struct frame_id *id) const
> +  {
> +    error (_("No method this_id implemented for unwinder %s"), m_name);
> +  }

Why not pure virtual?

> +  frame_unwind_legacy (const char *n, frame_type t, frame_unwind_class c,
> +		       frame_unwind_stop_reason_ftype *sr,
> +		       frame_this_id_ftype *ti,
> +		       frame_prev_register_ftype *pr,
> +		       const struct frame_data *ud,
> +		       frame_sniffer_ftype *s,
> +		       frame_dealloc_cache_ftype *dc = nullptr,
> +		       frame_prev_arch_ftype *pa = nullptr)
> +  : frame_unwind (n, t, c, ud), stop_reason_p (sr),
> +    this_id_p (ti), prev_register_p (pr), sniffer_p (s),
> +    dealloc_cache_p (dc), prev_arch_p (pa) { }

I wonder if making this constexpr would help avoid running a ton of
constructors at startup time.

Alternatively maybe an approach would be to overload
frame_unwind_append_unwinder to automatically instantiate the frame_unwind_legacy
object given the C-style struct.  I think this would delay
running the constructor until the gdbarch is initialized, which
in many cases would be never.

> +	  = obstack_new<frame_unwind_python>
> +	    (gdbarch_obstack(newarch), (const struct frame_data *) newarch);
> +

I think it's better to derive from allocate_on_obstack instead.  This
helps prevent errors.

Also a missing space.

Tom

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/4] GDB: introduce ability to disable frame unwinders
  2024-03-06 12:51 ` [PATCH 4/4] GDB: introduce ability to disable frame unwinders Guinevere Larsen
  2024-03-06 13:47   ` Eli Zaretskii
@ 2024-03-08 17:22   ` Tom Tromey
  2024-03-11 14:09     ` Guinevere Larsen
  1 sibling, 1 reply; 23+ messages in thread
From: Tom Tromey @ 2024-03-08 17:22 UTC (permalink / raw)
  To: Guinevere Larsen; +Cc: gdb-patches

>>>>> Guinevere Larsen <blarsen@redhat.com> writes:

 
> +static enum frame_unwind_class
> +str_to_frame_unwind_class (const char **c_str)

Comment.

> +{
> +  std::string full_name = "FRAME_UNWIND_";
> +  const int start_length = full_name.length ();
> +  if (strncasecmp (*c_str, full_name.c_str (), start_length) == 0)
> +    full_name = *c_str;

In an earlier patch I was wondering how useful those FRAME_UNWIND_
strings were for the maint output.  Maybe just using human names here
would be overall better.

Then this could just call streq.

Anyway if you want to keep this approach, it's weird to allocate a
string and then use the C API.  Perhaps a string_view would be better.

 
> +/* Helper function to both enable and disable frame unwinders.
> +   if ENABLE is true, this call will be enabling unwinders,
> +   otherwise the unwinders will be disabled.  */
> +static void
> +enable_disable_frame_unwinders (const char *args, int from_tty, bool enable)
> +{
> +
> +  reinit_frame_cache ();

Stray blank line.

> +  if (args == nullptr)
> +    {
> +      error (_("specify which frame unwinder(s) should be %s"),
> +	     (enable)? "enabled" : "disabled");
> +    }

No braces.

> +  /* First see if the user wants to change all unwinders.  */
> +  if (check_for_argument (&args, "-all"))
> +    {
> +      for (const frame_unwind *u : unwinder_list)
> +	{
> +	  u->set_enabled (enable);
> +	}

This also looks over-braced.

> +  add_cmd ("disable", class_maintenance, maintenance_disable_frame_unwinders,
> +	   _("\
> +Disable one or more frame unwinder(s).\n\
> +Usage: maint frame-unwinder disable [OPTION] UNWINDER\n\
> +\n\
> +The meaning of UNWINDER depends on the OPTION given. These are the possibilities:\n\
> +\t-all    - UNWINDER is ignored. All available unwinders will be disabled\n\
> +\t-name   - UNWINDER is the exact name of the frame unwinder is to be disabled\n\
> +\t-class  - UNWINDER is the class of unwinders to be disabled.\n\

I guess I'd write that more like

disable [-all | -name UNWINDER | -class NAME]

or something like that, rather than spelling out that UNWINDER is
ignored in one case.


I think there's probably a bug in bugzilla about disabling unwinders, so
this should probably have a Bug: trailer.

Tom

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 1/4] gdb: make gdbarch store a vector of frame unwinders
  2024-03-08 16:34   ` Tom Tromey
@ 2024-03-11 10:51     ` Guinevere Larsen
  2024-03-11 18:01       ` Tom Tromey
  0 siblings, 1 reply; 23+ messages in thread
From: Guinevere Larsen @ 2024-03-11 10:51 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 08/03/2024 17:34, Tom Tromey wrote:
>>>>>> Guinevere Larsen <blarsen@redhat.com> writes:
>> Before this commit, all frame unwinders would be stored in the obstack
>> of a gdbarch and accessed by using the registry system. This made for
>> unwieldy code, and unnecessarily complex logic in the frame_unwinder
>> implementation, along with making frame_unwind structs be unable to have
>> non-trivial constructors.
>> Seeing as a future patch of this series wants to refactor the
>> frame_unwind struct to use inheritance, obstack storage would no longer
>> be viable. In preparation for that change, this commit adds an
>> std::vector to gdbarch to store the unwinders in.
>> There should be no user-visible changes.
> I'm not really sure about this patch.
>
> Like on the one hand, it is fine.  The arch is going to store the
> unwinder table.
>
> On the other hand, the registry system is there to let modules be kind
> of independent.  The lines are blurry though.
>
>> +std::vector<const frame_unwind*>&
> Missing spaces in here.
>
>> -static const registry<gdbarch>::key<struct frame_unwind_table>
>> -     frame_unwind_data;
> An alternative approach would be to just use a different type in here.
>
> This can use the default destruction approach and then it's just
> allocated with 'new'.  So then you can use any old C++ type.

I guess you're right, but would it make any real difference to have the 
gdbarch store a vector out right, or to store a vector in an obfuscated way?

I looked back through the git log and the latest change that could have 
added a significant change to how unwinders are stored could have 
happened in early 2014, so I don't really think the registry would 
meaningfully save in complexity.

But if you feel strongly that the registry is better, I can rework this 
patch to work with that instead... Or just drop it. Once Andrew informed 
of obstack_new, this patch felt unnecessary to the series as a whole, 
just a related cleanup that I forgot to reword.

>
> FWIW I think the real issue with obstack allocation isn't constructors
> but destruction.  See https://sourceware.org/pipermail/gdb-patches/2024-February/206888.html
I see what you mean (though I don't necessarily understand why). I would 
prefer if we could not restrict the destructors, but since GDB doesn't 
ever dealloc gdbarches to begin with, I don't think that would be a big 
issue either way.

-- 
Cheers,
Guinevere Larsen
She/Her/Hers


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/4] GDB: introduce ability to disable frame unwinders
  2024-03-08 17:22   ` Tom Tromey
@ 2024-03-11 14:09     ` Guinevere Larsen
  0 siblings, 0 replies; 23+ messages in thread
From: Guinevere Larsen @ 2024-03-11 14:09 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 08/03/2024 18:22, Tom Tromey wrote:
>>>>>> Guinevere Larsen <blarsen@redhat.com> writes:
>   
>> +static enum frame_unwind_class
>> +str_to_frame_unwind_class (const char **c_str)
> Comment.
>
>> +{
>> +  std::string full_name = "FRAME_UNWIND_";
>> +  const int start_length = full_name.length ();
>> +  if (strncasecmp (*c_str, full_name.c_str (), start_length) == 0)
>> +    full_name = *c_str;
> In an earlier patch I was wondering how useful those FRAME_UNWIND_
> strings were for the maint output.  Maybe just using human names here
> would be overall better.
>
> Then this could just call streq.

I don't want to call streq because I want case insensitive matching, but 
I will drop the FRAME_UNWIND_ prefix from printing.

>
> Anyway if you want to keep this approach, it's weird to allocate a
> string and then use the C API.  Perhaps a string_view would be better.
I used the string because at that moment it felt safer than dealing with 
strcat, and originally I was going to use the operator equals rather 
than strcasecmp. I'll drop it
>
>   
>> +/* Helper function to both enable and disable frame unwinders.
>> +   if ENABLE is true, this call will be enabling unwinders,
>> +   otherwise the unwinders will be disabled.  */
>> +static void
>> +enable_disable_frame_unwinders (const char *args, int from_tty, bool enable)
>> +{
>> +
>> +  reinit_frame_cache ();
> Stray blank line.
>
>> +  if (args == nullptr)
>> +    {
>> +      error (_("specify which frame unwinder(s) should be %s"),
>> +	     (enable)? "enabled" : "disabled");
>> +    }
> No braces.
>
>> +  /* First see if the user wants to change all unwinders.  */
>> +  if (check_for_argument (&args, "-all"))
>> +    {
>> +      for (const frame_unwind *u : unwinder_list)
>> +	{
>> +	  u->set_enabled (enable);
>> +	}
> This also looks over-braced.
>
>> +  add_cmd ("disable", class_maintenance, maintenance_disable_frame_unwinders,
>> +	   _("\
>> +Disable one or more frame unwinder(s).\n\
>> +Usage: maint frame-unwinder disable [OPTION] UNWINDER\n\
>> +\n\
>> +The meaning of UNWINDER depends on the OPTION given. These are the possibilities:\n\
>> +\t-all    - UNWINDER is ignored. All available unwinders will be disabled\n\
>> +\t-name   - UNWINDER is the exact name of the frame unwinder is to be disabled\n\
>> +\t-class  - UNWINDER is the class of unwinders to be disabled.\n\
> I guess I'd write that more like
>
> disable [-all | -name UNWINDER | -class NAME]
>
> or something like that, rather than spelling out that UNWINDER is
> ignored in one case.
That sounds like a much better solution to what I came up with hahaha. 
Thank you!
>
>
> I think there's probably a bug in bugzilla about disabling unwinders, so
> this should probably have a Bug: trailer.
Is there? :o I'll go looking for it.

-- 
Cheers,
Guinevere Larsen
She/Her/Hers

>
> Tom
>


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 0/4] Modernize frame unwinders and add disable feature
  2024-03-06 12:51 [PATCH 0/4] Modernize frame unwinders and add disable feature Guinevere Larsen
                   ` (3 preceding siblings ...)
  2024-03-06 12:51 ` [PATCH 4/4] GDB: introduce ability to disable frame unwinders Guinevere Larsen
@ 2024-03-11 14:56 ` Luis Machado
  2024-03-11 15:00   ` Guinevere Larsen
  4 siblings, 1 reply; 23+ messages in thread
From: Luis Machado @ 2024-03-11 14:56 UTC (permalink / raw)
  To: Guinevere Larsen, gdb-patches

Hi,

On 3/6/24 12:51, Guinevere Larsen wrote:
> This patch series started with me trying to make it easier to test GDB's
> ability to unwind using CFI data, to improve a previous patch I sent to
> the list. However, once I finished these changes, I realized there was
> an unrelated bug I should fix before proposing the CFI test. Since these
> changes are significant enough already, and I think would be interesting
> on their own, I figured I shoudl submit this patch series as is right
> now while I figure out the other bug.
> 
> The first patch is just a minor change, storing frame unwinders in a
> vector instead of through an unwinder table accessible using the
> registry system. This isn't required (like I originally thought it was),
> but it does make the whole system more readable in my opinion.
> 
> Patch 3 has the real meat of the modernization, making GDB use
> polymorphism to handle frame unwinders. This is slightly slower than
> using function pointers, but much more readable in my opinion.
> 
> As for the unwinder classes, they were chosen somewhat arbitrarily,
> mostly based on where I found an unwinder and its name. I almost expect
> some unwinders to be mis-categorized, but that should be easy to fix.
> 
> The changes up to patch 3 have been tested with a try-branch, no
> regressions as far as I could see.
> 
> Guinevere Larsen (4):
>   gdb: make gdbarch store a vector of frame unwinders
>   gdb: add "unwinder class" to frame unwinders
>   gdb: Migrate frame unwinders to use C++ classes
>   GDB: introduce ability to disable frame unwinders

I haven't gone through the series in detail, but I thought I'd give it a try on one of the
aarch64 machines I have access to. I didn't look particularly healthy:


# of unexpected core files      47
# of expected passes            116521
# of unexpected failures        581
# of expected failures          77
# of known failures             116
# of untested testcases         128
# of unresolved testcases       1102
# of unsupported tests          458
# of duplicate test names       10

I see a number of internal errors going on. Mostly like these:

        ../../../repos/binutils-gdb/gdbsupport/errors.cc:58
0xaaaad27e7877 check_ptrace_stopped_lwp_gone
        ../../../repos/binutils-gdb/gdb/linux-nat.c:1634
0xaaaad27e7877 check_ptrace_stopped_lwp_gone
        ../../../repos/binutils-gdb/gdb/linux-nat.c:1630
0xaaaad2ae0fa3 linux_resume_one_lwp

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 0/4] Modernize frame unwinders and add disable feature
  2024-03-11 14:56 ` [PATCH 0/4] Modernize frame unwinders and add disable feature Luis Machado
@ 2024-03-11 15:00   ` Guinevere Larsen
  2024-03-11 15:10     ` Luis Machado
  0 siblings, 1 reply; 23+ messages in thread
From: Guinevere Larsen @ 2024-03-11 15:00 UTC (permalink / raw)
  To: Luis Machado, gdb-patches

On 11/03/2024 15:56, Luis Machado wrote:
> Hi,
>
> On 3/6/24 12:51, Guinevere Larsen wrote:
>> This patch series started with me trying to make it easier to test GDB's
>> ability to unwind using CFI data, to improve a previous patch I sent to
>> the list. However, once I finished these changes, I realized there was
>> an unrelated bug I should fix before proposing the CFI test. Since these
>> changes are significant enough already, and I think would be interesting
>> on their own, I figured I shoudl submit this patch series as is right
>> now while I figure out the other bug.
>>
>> The first patch is just a minor change, storing frame unwinders in a
>> vector instead of through an unwinder table accessible using the
>> registry system. This isn't required (like I originally thought it was),
>> but it does make the whole system more readable in my opinion.
>>
>> Patch 3 has the real meat of the modernization, making GDB use
>> polymorphism to handle frame unwinders. This is slightly slower than
>> using function pointers, but much more readable in my opinion.
>>
>> As for the unwinder classes, they were chosen somewhat arbitrarily,
>> mostly based on where I found an unwinder and its name. I almost expect
>> some unwinders to be mis-categorized, but that should be easy to fix.
>>
>> The changes up to patch 3 have been tested with a try-branch, no
>> regressions as far as I could see.
>>
>> Guinevere Larsen (4):
>>    gdb: make gdbarch store a vector of frame unwinders
>>    gdb: add "unwinder class" to frame unwinders
>>    gdb: Migrate frame unwinders to use C++ classes
>>    GDB: introduce ability to disable frame unwinders
> I haven't gone through the series in detail, but I thought I'd give it a try on one of the
> aarch64 machines I have access to. I didn't look particularly healthy:
>
>
> # of unexpected core files      47
> # of expected passes            116521
> # of unexpected failures        581
> # of expected failures          77
> # of known failures             116
> # of untested testcases         128
> # of unresolved testcases       1102
> # of unsupported tests          458
> # of duplicate test names       10
>
> I see a number of internal errors going on. Mostly like these:
>
>          ../../../repos/binutils-gdb/gdbsupport/errors.cc:58
> 0xaaaad27e7877 check_ptrace_stopped_lwp_gone
>          ../../../repos/binutils-gdb/gdb/linux-nat.c:1634
> 0xaaaad27e7877 check_ptrace_stopped_lwp_gone
>          ../../../repos/binutils-gdb/gdb/linux-nat.c:1630
> 0xaaaad2ae0fa3 linux_resume_one_lwp
>
Oh no! Linaro CI had showed something was wrong, but I wasn't able to 
grab an aarch64 machine to test yet. I'll check it out when I can, 
thanks for narrowing it down for me

-- 
Cheers,
Guinevere Larsen
She/Her/Hers


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 0/4] Modernize frame unwinders and add disable feature
  2024-03-11 15:00   ` Guinevere Larsen
@ 2024-03-11 15:10     ` Luis Machado
  2024-03-13 12:08       ` Guinevere Larsen
  0 siblings, 1 reply; 23+ messages in thread
From: Luis Machado @ 2024-03-11 15:10 UTC (permalink / raw)
  To: Guinevere Larsen, gdb-patches

On 3/11/24 15:00, Guinevere Larsen wrote:
> On 11/03/2024 15:56, Luis Machado wrote:
>> Hi,
>>
>> On 3/6/24 12:51, Guinevere Larsen wrote:
>>> This patch series started with me trying to make it easier to test GDB's
>>> ability to unwind using CFI data, to improve a previous patch I sent to
>>> the list. However, once I finished these changes, I realized there was
>>> an unrelated bug I should fix before proposing the CFI test. Since these
>>> changes are significant enough already, and I think would be interesting
>>> on their own, I figured I shoudl submit this patch series as is right
>>> now while I figure out the other bug.
>>>
>>> The first patch is just a minor change, storing frame unwinders in a
>>> vector instead of through an unwinder table accessible using the
>>> registry system. This isn't required (like I originally thought it was),
>>> but it does make the whole system more readable in my opinion.
>>>
>>> Patch 3 has the real meat of the modernization, making GDB use
>>> polymorphism to handle frame unwinders. This is slightly slower than
>>> using function pointers, but much more readable in my opinion.
>>>
>>> As for the unwinder classes, they were chosen somewhat arbitrarily,
>>> mostly based on where I found an unwinder and its name. I almost expect
>>> some unwinders to be mis-categorized, but that should be easy to fix.
>>>
>>> The changes up to patch 3 have been tested with a try-branch, no
>>> regressions as far as I could see.
>>>
>>> Guinevere Larsen (4):
>>>    gdb: make gdbarch store a vector of frame unwinders
>>>    gdb: add "unwinder class" to frame unwinders
>>>    gdb: Migrate frame unwinders to use C++ classes
>>>    GDB: introduce ability to disable frame unwinders
>> I haven't gone through the series in detail, but I thought I'd give it a try on one of the
>> aarch64 machines I have access to. I didn't look particularly healthy:
>>
>>
>> # of unexpected core files      47
>> # of expected passes            116521
>> # of unexpected failures        581
>> # of expected failures          77
>> # of known failures             116
>> # of untested testcases         128
>> # of unresolved testcases       1102
>> # of unsupported tests          458
>> # of duplicate test names       10
>>
>> I see a number of internal errors going on. Mostly like these:
>>
>>          ../../../repos/binutils-gdb/gdbsupport/errors.cc:58
>> 0xaaaad27e7877 check_ptrace_stopped_lwp_gone
>>          ../../../repos/binutils-gdb/gdb/linux-nat.c:1634
>> 0xaaaad27e7877 check_ptrace_stopped_lwp_gone
>>          ../../../repos/binutils-gdb/gdb/linux-nat.c:1630
>> 0xaaaad2ae0fa3 linux_resume_one_lwp
>>
> Oh no! Linaro CI had showed something was wrong, but I wasn't able to grab an aarch64 machine to test yet. I'll check it out when I can, thanks for narrowing it down for me
> 

You're welcome. Do let me know if you can't easily find an aarch64 machine to try things on, and I can provide more info
and take a deeper look.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 1/4] gdb: make gdbarch store a vector of frame unwinders
  2024-03-11 10:51     ` Guinevere Larsen
@ 2024-03-11 18:01       ` Tom Tromey
  0 siblings, 0 replies; 23+ messages in thread
From: Tom Tromey @ 2024-03-11 18:01 UTC (permalink / raw)
  To: Guinevere Larsen; +Cc: Tom Tromey, gdb-patches

> I guess you're right, but would it make any real difference to have
> the gdbarch store a vector out right, or to store a vector in an
> obfuscated way?

I'm not really sure how I feel about it.  In this case it's probably
harmless.

>> FWIW I think the real issue with obstack allocation isn't constructors
>> but destruction.  See https://sourceware.org/pipermail/gdb-patches/2024-February/206888.html

> I see what you mean (though I don't necessarily understand why). I
> would prefer if we could not restrict the destructors, but since GDB
> doesn't ever dealloc gdbarches to begin with, I don't think that would
> be a big issue either way.

That's true for things that happen to be allocated on the gdbarch
obstack right now, but not other obstacks, and perhaps not in the future
if we ever decide a gdbarch should be deleted.

FWIW the issue with non-trivial destructors is that when the obstack
itself is destroyed, there's no mechanism for calling any of these.
This could introduce leaks or whatever else.

Tom

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 3/4] gdb: Migrate frame unwinders to use C++ classes
  2024-03-08 17:07   ` Tom Tromey
@ 2024-03-12 16:24     ` Guinevere Larsen
  0 siblings, 0 replies; 23+ messages in thread
From: Guinevere Larsen @ 2024-03-12 16:24 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 08/03/2024 18:07, Tom Tromey wrote:
>>>>>> Guinevere Larsen <blarsen@redhat.com> writes:
>> Frame unwinders have historically been a structure populated with
>> callback pointers, so that architectures (or other specific unwinders)
>> could install their own way to handle the inferior. However, since
>> moving to c++, we could use polymorphism to get the same functionality
>> in a more readable way. Polymorphism also makes it simpler to add new
>> functionality to all frame unwinders, since all that's required is
>> adding it to the base class.
>> As part of the changes to add support to disabling frame unwinders,
>> this commit makes the first baby step in  using polymorphism for the
>> frame unwinders, by making frame_unwind a virtual class, and adds a
>> couple of new classes. The main class added is frame_unwind_legacy,
>> which works the same as the previous structs, using function pointers
>> as callbacks. This class was added to allow the transition to happen
>> piecemeal. New unwinders should instead follow the lead of the other
>> classes implemented.
>> 2 of the others, frame_unwind_python and frame_unwind_trampoline, were added
>> because it seemed simpler at the moment to do that instead of reworking
>> the dynamic allocation to work with the legacy class, and can be used as
>> an example to future implementations.
>>   /* AArch64 prologue unwinder.  */
>> -static frame_unwind aarch64_prologue_unwind =
>> -{
>> +static frame_unwind_legacy aarch64_prologue_unwind (
> Some of these should probably be 'const'...  not really your problem but
> the patch points it out.
Sure, I can do this.
>
>> +/* See frame-unwind.h.  */
>> +
>> +enum unwind_stop_reason
>> +frame_unwind::stop_reason (const frame_info_ptr &this_frame,
>> +			   void **this_prologue_cache) const
>> +{
>> +  return default_frame_unwind_stop_reason (this_frame, this_prologue_cache);
>> +}
>> +
> It looks like all of these methods here could just be inline in the
> header.
>
>> -struct frame_unwind
>> +class frame_unwind
>>   {
>> -  const char *name;
>> +private:
> class defaults to private.
>
>> +  frame_unwind (const char *n, frame_type t, frame_unwind_class c,
>> +		       const struct frame_data *d)
>> +    : m_name (n), m_type (t), m_unwinder_class (c), m_unwind_data (d) { }
> This looks weird.
Any specific reason why? If the problem is the single-letter parameters, 
I can change that (it was from an earlier iteration), but other than 
that, I'm not sure what you find weird in this.
>
>> +  /* Calculate the ID of the given frame using this unwinder.  */
>> +  virtual void this_id (const frame_info_ptr &this_frame,
>> +			void **this_prologue_cache,
>> +			struct frame_id *id) const
>> +  {
>> +    error (_("No method this_id implemented for unwinder %s"), m_name);
>> +  }
> Why not pure virtual?

not all classes have these methods defined, and if we make it pure 
virtual, all classes will need to implement them (or we get linker 
errors), so it seems easier to implement a base error in here.

>
>> +  frame_unwind_legacy (const char *n, frame_type t, frame_unwind_class c,
>> +		       frame_unwind_stop_reason_ftype *sr,
>> +		       frame_this_id_ftype *ti,
>> +		       frame_prev_register_ftype *pr,
>> +		       const struct frame_data *ud,
>> +		       frame_sniffer_ftype *s,
>> +		       frame_dealloc_cache_ftype *dc = nullptr,
>> +		       frame_prev_arch_ftype *pa = nullptr)
>> +  : frame_unwind (n, t, c, ud), stop_reason_p (sr),
>> +    this_id_p (ti), prev_register_p (pr), sniffer_p (s),
>> +    dealloc_cache_p (dc), prev_arch_p (pa) { }
> I wonder if making this constexpr would help avoid running a ton of
> constructors at startup time.
according to cppreference, constexpr constructors must not have virtual 
base classes, so unfortunately it isn't possible =/
>
> Alternatively maybe an approach would be to overload
> frame_unwind_append_unwinder to automatically instantiate the frame_unwind_legacy
> object given the C-style struct.  I think this would delay
> running the constructor until the gdbarch is initialized, which
> in many cases would be never.

I tried compiling without this patch to see what's GDB startup time, 
then checked the startup time with this patch. In my system, I can't see 
a difference in startup time with the "time" command (so 0.01s precision).

I could try to find a way to delay instantiation to when the unwinder is 
installed, so we avoid unnecessary constructor calls.

>> +	  = obstack_new<frame_unwind_python>
>> +	    (gdbarch_obstack(newarch), (const struct frame_data *) newarch);
>> +
> I think it's better to derive from allocate_on_obstack instead.  This
> helps prevent errors.
I am only allocating things on the obstack because I couldn't figure out 
a better solution to dynamically allocate the unwinder in a way that 
wouldn't leak the memory if gdbarch was de-allocated. If you are dead 
set on delaying constructing the objects at startup, I would probably 
make gdbarch store unique_ptrs instead of raw pointers, or some other 
similar thing.
>
> Also a missing space.
>
> Tom
>

-- 
Cheers,
Guinevere Larsen
She/Her/Hers


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 0/4] Modernize frame unwinders and add disable feature
  2024-03-11 15:10     ` Luis Machado
@ 2024-03-13 12:08       ` Guinevere Larsen
  2024-03-13 12:44         ` Luis Machado
  0 siblings, 1 reply; 23+ messages in thread
From: Guinevere Larsen @ 2024-03-13 12:08 UTC (permalink / raw)
  To: Luis Machado, gdb-patches

On 11/03/2024 16:10, Luis Machado wrote:
> On 3/11/24 15:00, Guinevere Larsen wrote:
>> On 11/03/2024 15:56, Luis Machado wrote:
>>> Hi,
>>>
>>> On 3/6/24 12:51, Guinevere Larsen wrote:
>>>> This patch series started with me trying to make it easier to test GDB's
>>>> ability to unwind using CFI data, to improve a previous patch I sent to
>>>> the list. However, once I finished these changes, I realized there was
>>>> an unrelated bug I should fix before proposing the CFI test. Since these
>>>> changes are significant enough already, and I think would be interesting
>>>> on their own, I figured I shoudl submit this patch series as is right
>>>> now while I figure out the other bug.
>>>>
>>>> The first patch is just a minor change, storing frame unwinders in a
>>>> vector instead of through an unwinder table accessible using the
>>>> registry system. This isn't required (like I originally thought it was),
>>>> but it does make the whole system more readable in my opinion.
>>>>
>>>> Patch 3 has the real meat of the modernization, making GDB use
>>>> polymorphism to handle frame unwinders. This is slightly slower than
>>>> using function pointers, but much more readable in my opinion.
>>>>
>>>> As for the unwinder classes, they were chosen somewhat arbitrarily,
>>>> mostly based on where I found an unwinder and its name. I almost expect
>>>> some unwinders to be mis-categorized, but that should be easy to fix.
>>>>
>>>> The changes up to patch 3 have been tested with a try-branch, no
>>>> regressions as far as I could see.
>>>>
>>>> Guinevere Larsen (4):
>>>>     gdb: make gdbarch store a vector of frame unwinders
>>>>     gdb: add "unwinder class" to frame unwinders
>>>>     gdb: Migrate frame unwinders to use C++ classes
>>>>     GDB: introduce ability to disable frame unwinders
>>> I haven't gone through the series in detail, but I thought I'd give it a try on one of the
>>> aarch64 machines I have access to. I didn't look particularly healthy:
>>>
>>>
>>> # of unexpected core files      47
>>> # of expected passes            116521
>>> # of unexpected failures        581
>>> # of expected failures          77
>>> # of known failures             116
>>> # of untested testcases         128
>>> # of unresolved testcases       1102
>>> # of unsupported tests          458
>>> # of duplicate test names       10
>>>
>>> I see a number of internal errors going on. Mostly like these:
>>>
>>>           ../../../repos/binutils-gdb/gdbsupport/errors.cc:58
>>> 0xaaaad27e7877 check_ptrace_stopped_lwp_gone
>>>           ../../../repos/binutils-gdb/gdb/linux-nat.c:1634
>>> 0xaaaad27e7877 check_ptrace_stopped_lwp_gone
>>>           ../../../repos/binutils-gdb/gdb/linux-nat.c:1630
>>> 0xaaaad2ae0fa3 linux_resume_one_lwp
>>>
>> Oh no! Linaro CI had showed something was wrong, but I wasn't able to grab an aarch64 machine to test yet. I'll check it out when I can, thanks for narrowing it down for me
>>
> You're welcome. Do let me know if you can't easily find an aarch64 machine to try things on, and I can provide more info
> and take a deeper look.
>
I think I found the reason why aarch64 started failing so many cases. 
Can you try the following change and see if it works?

I'm not 100% sure because the machine I'm using seems to have some 
rather unstable results, but I think it should have solved the problem.

-- 
Cheers,
Guinevere Larsen
She/Her/Hers



diff --git a/gdb/tramp-frame.c b/gdb/tramp-frame.c
index 722d24ee129..a6f23e2b1b6 100644
--- a/gdb/tramp-frame.c
+++ b/gdb/tramp-frame.c
@@ -178,6 +178,9 @@ class frame_unwind_trampoline : public frame_unwind {
    {
      return prev_arch_p (this_frame, this_prologue_cache);
    }
+
+  void dealloc_cache (frame_info *self, void *this_cache) const override
+  { }
  };

  void


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 0/4] Modernize frame unwinders and add disable feature
  2024-03-13 12:08       ` Guinevere Larsen
@ 2024-03-13 12:44         ` Luis Machado
  0 siblings, 0 replies; 23+ messages in thread
From: Luis Machado @ 2024-03-13 12:44 UTC (permalink / raw)
  To: Guinevere Larsen, gdb-patches

On 3/13/24 12:08, Guinevere Larsen wrote:
> On 11/03/2024 16:10, Luis Machado wrote:
>> On 3/11/24 15:00, Guinevere Larsen wrote:
>>> On 11/03/2024 15:56, Luis Machado wrote:
>>>> Hi,
>>>>
>>>> On 3/6/24 12:51, Guinevere Larsen wrote:
>>>>> This patch series started with me trying to make it easier to test GDB's
>>>>> ability to unwind using CFI data, to improve a previous patch I sent to
>>>>> the list. However, once I finished these changes, I realized there was
>>>>> an unrelated bug I should fix before proposing the CFI test. Since these
>>>>> changes are significant enough already, and I think would be interesting
>>>>> on their own, I figured I shoudl submit this patch series as is right
>>>>> now while I figure out the other bug.
>>>>>
>>>>> The first patch is just a minor change, storing frame unwinders in a
>>>>> vector instead of through an unwinder table accessible using the
>>>>> registry system. This isn't required (like I originally thought it was),
>>>>> but it does make the whole system more readable in my opinion.
>>>>>
>>>>> Patch 3 has the real meat of the modernization, making GDB use
>>>>> polymorphism to handle frame unwinders. This is slightly slower than
>>>>> using function pointers, but much more readable in my opinion.
>>>>>
>>>>> As for the unwinder classes, they were chosen somewhat arbitrarily,
>>>>> mostly based on where I found an unwinder and its name. I almost expect
>>>>> some unwinders to be mis-categorized, but that should be easy to fix.
>>>>>
>>>>> The changes up to patch 3 have been tested with a try-branch, no
>>>>> regressions as far as I could see.
>>>>>
>>>>> Guinevere Larsen (4):
>>>>>     gdb: make gdbarch store a vector of frame unwinders
>>>>>     gdb: add "unwinder class" to frame unwinders
>>>>>     gdb: Migrate frame unwinders to use C++ classes
>>>>>     GDB: introduce ability to disable frame unwinders
>>>> I haven't gone through the series in detail, but I thought I'd give it a try on one of the
>>>> aarch64 machines I have access to. I didn't look particularly healthy:
>>>>
>>>>
>>>> # of unexpected core files      47
>>>> # of expected passes            116521
>>>> # of unexpected failures        581
>>>> # of expected failures          77
>>>> # of known failures             116
>>>> # of untested testcases         128
>>>> # of unresolved testcases       1102
>>>> # of unsupported tests          458
>>>> # of duplicate test names       10
>>>>
>>>> I see a number of internal errors going on. Mostly like these:
>>>>
>>>>           ../../../repos/binutils-gdb/gdbsupport/errors.cc:58
>>>> 0xaaaad27e7877 check_ptrace_stopped_lwp_gone
>>>>           ../../../repos/binutils-gdb/gdb/linux-nat.c:1634
>>>> 0xaaaad27e7877 check_ptrace_stopped_lwp_gone
>>>>           ../../../repos/binutils-gdb/gdb/linux-nat.c:1630
>>>> 0xaaaad2ae0fa3 linux_resume_one_lwp
>>>>
>>> Oh no! Linaro CI had showed something was wrong, but I wasn't able to grab an aarch64 machine to test yet. I'll check it out when I can, thanks for narrowing it down for me
>>>
>> You're welcome. Do let me know if you can't easily find an aarch64 machine to try things on, and I can provide more info
>> and take a deeper look.
>>
> I think I found the reason why aarch64 started failing so many cases. Can you try the following change and see if it works?
> 
> I'm not 100% sure because the machine I'm using seems to have some rather unstable results, but I think it should have solved the problem.
> 

The attached fixlet completely clears things up, and the testsuite for aarch64-linux is back to normal.

# of unexpected core files      1
# of expected passes            119178
# of unexpected failures        22
# of expected failures          77
# of known failures             120
# of untested testcases         128
# of unsupported tests          458
# of duplicate test names       3

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2024-03-13 12:44 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-06 12:51 [PATCH 0/4] Modernize frame unwinders and add disable feature Guinevere Larsen
2024-03-06 12:51 ` [PATCH 1/4] gdb: make gdbarch store a vector of frame unwinders Guinevere Larsen
2024-03-08 16:34   ` Tom Tromey
2024-03-11 10:51     ` Guinevere Larsen
2024-03-11 18:01       ` Tom Tromey
2024-03-06 12:51 ` [PATCH 2/4] gdb: add "unwinder class" to " Guinevere Larsen
2024-03-08 16:40   ` Tom Tromey
2024-03-06 12:51 ` [PATCH 3/4] gdb: Migrate frame unwinders to use C++ classes Guinevere Larsen
2024-03-07 11:01   ` Lancelot SIX
2024-03-07 11:04     ` Guinevere Larsen
2024-03-08 17:07   ` Tom Tromey
2024-03-12 16:24     ` Guinevere Larsen
2024-03-06 12:51 ` [PATCH 4/4] GDB: introduce ability to disable frame unwinders Guinevere Larsen
2024-03-06 13:47   ` Eli Zaretskii
2024-03-06 14:07     ` Guinevere Larsen
2024-03-06 14:16       ` Eli Zaretskii
2024-03-08 17:22   ` Tom Tromey
2024-03-11 14:09     ` Guinevere Larsen
2024-03-11 14:56 ` [PATCH 0/4] Modernize frame unwinders and add disable feature Luis Machado
2024-03-11 15:00   ` Guinevere Larsen
2024-03-11 15:10     ` Luis Machado
2024-03-13 12:08       ` Guinevere Larsen
2024-03-13 12:44         ` Luis Machado

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).