Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH v8 0/5] Modernize frame unwinders and add disable feature
@ 2024-12-10 19:51 Guinevere Larsen
  2024-12-10 19:51 ` [PATCH v8 1/5] gdb: make gdbarch store a vector of frame unwinders Guinevere Larsen
                   ` (6 more replies)
  0 siblings, 7 replies; 24+ messages in thread
From: Guinevere Larsen @ 2024-12-10 19: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.

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

Patch 2 adds a new field for frame unwinders, their "class", which
roughly correlates to which area of GDB adds that unwinder.  They were
chosen rather arbitrarily based on my understanding, where the unwinder
is added and its name. This class will be used to simplify bulk
disabling of unwinders introduced by patch 4.

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. Also, as
noted by Thiago Bauermann, no frame unwinders that use the legacy class
ever pass a frame_data pointer, so that field was removed from those
unwinders.

Patch 4 adds the possibility to disable unwinders based on their name or
class, and disable all unwinders at once if desired. It also reverts
commit 3c3bb0580be0 since that patch pointed out that if a more generic
system was implemented it could be reverted.

Patch 5 adds the test I was trying to upstream all along.

All the documentation has been approved by Eli already. Patches 2 and 3
were already approved by Simon.

Changes for v8:
* Simplified the loop printing the table for "maint info
  frame-unwinders".
* Used gdb::option to parse the options for "maint frame-unwinder
  disable" 
* Changed error message when no unwinder is found but some unwinders
  were disabled
* Cosmetic fixes.

Changes for v7:
* Moved the std::vector back to being accessed through the registry system
    Removed the approvals from patch 1 because I think this change is
    reasonably big, and needs another review.
* Several cosmetic changes to the code, following feedback from Simon
  and Thiago.
* Fixed the test from patch 4, following Thiago's suggestion
* Users can no longer specify the unwinder class with the
  FRAME_UNWINDER_ prefix

Changes for v6:
* Patches 1 and 2 were approved by Simon
* Several cosmetic changes to patch 3 and 4
* New dealloc_cache default implementation (noop), and frame_unwind_legacy
  defaults to it if no function pointer is given.
* New prev_arch default implementation (getting the current arch), and
  frame_unwind_legacy defaults to it if no pointer is given
* Renamed "sniffer" to "sniff", to make it more clear what the method
  does
* Fixed some problems with the testcase.

Changes for v5:
* Reverted commit 3c3bb0580be0, with patch 4 following the advice on
  that commit
* on patch 3, some of the original functions were converted into the
  classes' methods instead of just passing the call along.
* some methods of frame_unwind_legacy were switched to be purely
  virtual

Changes for v4:
* Rebase on current master (No changes needed)
* improved documentation of unwinder classes follow Eli feedback
* added KFAIL to new test on patch 5 if running on 32 bit arm

Changes for v3:
* Fixed Linaro CI issue in 32 bit arm
* Fixed the few comments from Eli.
* Documented addition of unwinder class in the patch that adds it.

Changes for v2:
* Added back the test that checks if GDB can handle a mix of CUs with
  and without debuginfo in a single backtrace.
* Fixed all the nitpicks for style.
* Removed FRAME_UNWIND_ prefix when talking about classes
* Thoroughly changed the documentation, to be more readable

Guinevere Larsen (5):
  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/testsuite: Test for a backtrace through object without debuginfo

 gdb/NEWS                                      |  21 +
 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                             |   7 +-
 gdb/arc-tdep.c                                |  10 +-
 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                           |  64 +--
 gdb/dummy-frame.c                             |   8 +-
 gdb/dummy-frame.h                             |   2 +-
 gdb/dwarf2/frame-tailcall.c                   |   9 +-
 gdb/dwarf2/frame-tailcall.h                   |   2 +-
 gdb/dwarf2/frame.c                            |  46 +-
 gdb/dwarf2/frame.h                            |   6 -
 gdb/frame-unwind.c                            | 420 ++++++++++++++----
 gdb/frame-unwind.h                            | 173 +++++++-
 gdb/frame.c                                   |  21 +-
 gdb/frv-linux-tdep.c                          |   6 +-
 gdb/frv-tdep.c                                |   5 +-
 gdb/ft32-tdep.c                               |   6 +-
 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/or1k-tdep.c                               |   7 +-
 gdb/ppc-fbsd-tdep.c                           |   5 +-
 gdb/ppc-obsd-tdep.c                           |   5 +-
 gdb/python/py-unwind.c                        |  61 ++-
 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 +-
 .../backtrace-through-cu-nodebug-caller.c     |  28 ++
 .../backtrace-through-cu-nodebug-main.c       |  32 ++
 .../gdb.base/backtrace-through-cu-nodebug.exp |  95 ++++
 .../gdb.base/frame-info-consistent.exp        |   8 +-
 gdb/testsuite/gdb.base/frame-unwind-disable.c |  22 +
 .../gdb.base/frame-unwind-disable.exp         | 137 ++++++
 gdb/testsuite/gdb.base/maint.exp              |   4 -
 gdb/tic6x-tdep.c                              |  12 +-
 gdb/tilegx-tdep.c                             |   5 +-
 gdb/tramp-frame.c                             |  70 ++-
 gdb/v850-tdep.c                               |   5 +-
 gdb/vax-tdep.c                                |   6 +-
 gdb/windows-tdep.c                            |  35 +-
 gdb/windows-tdep.h                            |  16 +-
 gdb/xstormy16-tdep.c                          |   5 +-
 gdb/xtensa-tdep.c                             |   7 +-
 gdb/z80-tdep.c                                |   7 +-
 97 files changed, 1339 insertions(+), 551 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
 create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
 create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
 create mode 100644 gdb/testsuite/gdb.base/frame-unwind-disable.c
 create mode 100644 gdb/testsuite/gdb.base/frame-unwind-disable.exp

-- 
2.47.0


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

* [PATCH v8 1/5] gdb: make gdbarch store a vector of frame unwinders
  2024-12-10 19:51 [PATCH v8 0/5] Modernize frame unwinders and add disable feature Guinevere Larsen
@ 2024-12-10 19:51 ` Guinevere Larsen
  2025-01-14 14:28   ` Andrew Burgess
  2024-12-10 19:51 ` [PATCH v8 2/5] gdb: add "unwinder class" to " Guinevere Larsen
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 24+ messages in thread
From: Guinevere Larsen @ 2024-12-10 19:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Guinevere Larsen, Thiago Jung Bauermann

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 destructors.

Seeing as a future patch of this series wants to refactor the
frame_unwind struct to use inheritance, and we'd like to not restrict
the future derived classes on what destructors are allowed. In
preparation for that change, this commit changes the registry in gdbarch
to instead store an std::vector, which doesn't require using an obstack
and doesn't rely on a linked list.

There should be no user-visible changes.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
---
 gdb/frame-unwind.c | 107 +++++++++++++++------------------------------
 1 file changed, 36 insertions(+), 71 deletions(-)

diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
index 352779fcdcc..e61f6244913 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -31,61 +31,42 @@
 #include "cli/cli-cmds.h"
 #include "inferior.h"
 
-struct frame_unwind_table_entry
+/* Default sniffers, that must always be the first in the unwinder list,
+   no matter the architecture.  */
+static constexpr auto standard_unwinders =
 {
-  const struct frame_unwind *unwinder;
-  struct frame_unwind_table_entry *next;
+  &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
+     before the tailcall sniffer, resulting in confusion.  This is
+     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.  */
+  &dwarf2_tailcall_frame_unwind,
+  &inline_frame_unwind,
 };
 
-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 constexpr int prepend_unwinder_index = standard_unwinders.size ();
 
-static const registry<gdbarch>::key<struct frame_unwind_table>
+static const registry<gdbarch>::key<std::vector<const frame_unwind *>>
      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 *
+/* Retrieve 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)
 {
-  struct frame_unwind_table *table = frame_unwind_data.get (gdbarch);
+  std::vector<const frame_unwind *> *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;
+  table = new std::vector<const frame_unwind *>;
+  table->insert (table->begin (), standard_unwinders.begin (),
+		 standard_unwinders.end ());
 
-  struct obstack *obstack = gdbarch_obstack (gdbarch);
-  link = add_unwinder (obstack, &dummy_frame_unwind, link);
-  /* 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
-     before the tailcall sniffer, resulting in confusion.  This is
-     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);
-
-  /* The insertion point for OSABI sniffers.  */
-  table->osabi_head = link;
   frame_unwind_data.set (gdbarch, table);
 
   return table;
@@ -95,27 +76,16 @@ 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);
+
+  table->insert (table->begin () + prepend_unwinder_index, unwinder);
 }
 
 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;
-
-  /* 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;
+  get_frame_unwind_table (gdbarch)->push_back (unwinder);
 }
 
 /* Call SNIFFER from UNWINDER.  If it succeeded set UNWINDER for
@@ -188,9 +158,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 +172,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 +316,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,15 +324,11 @@ 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 (auto unwinder : *table)
     {
-      const char *name = entry->unwinder->name;
-      const char *type = frame_type_str (entry->unwinder->type);
-
       ui_out_emit_list tuple_emitter (uiout, nullptr);
-      uiout->field_string ("name", name);
-      uiout->field_string ("type", type);
+      uiout->field_string ("name", unwinder->name);
+      uiout->field_string ("type", frame_type_str (unwinder->type));
       uiout->text ("\n");
     }
 }
-- 
2.47.0


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

* [PATCH v8 2/5] gdb: add "unwinder class" to frame unwinders
  2024-12-10 19:51 [PATCH v8 0/5] Modernize frame unwinders and add disable feature Guinevere Larsen
  2024-12-10 19:51 ` [PATCH v8 1/5] gdb: make gdbarch store a vector of frame unwinders Guinevere Larsen
@ 2024-12-10 19:51 ` Guinevere Larsen
  2025-01-14 15:28   ` Andrew Burgess
  2024-12-10 19:51 ` [PATCH v8 3/5] gdb: Migrate frame unwinders to use C++ classes Guinevere Larsen
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 24+ messages in thread
From: Guinevere Larsen @ 2024-12-10 19:51 UTC (permalink / raw)
  To: gdb-patches
  Cc: Guinevere Larsen, Eli Zaretskii, Thiago Jung Bauermann, Simon Marchi

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 identifying 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.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Approved-By: Simon Marchi <simon.marchi@efficios.com>
---
 gdb/NEWS                    |  4 ++++
 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/doc/gdb.texinfo         | 15 ++++++++++++++-
 gdb/dummy-frame.c           |  1 +
 gdb/dwarf2/frame-tailcall.c |  1 +
 gdb/dwarf2/frame.c          |  2 ++
 gdb/frame-unwind.c          | 22 +++++++++++++++++++++-
 gdb/frame-unwind.h          | 19 +++++++++++++++++++
 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/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 +
 81 files changed, 168 insertions(+), 2 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 361d7726ba0..245b355669a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -164,6 +164,10 @@ maintenance print remote-registers
   which registers were included in the last stop reply packet received by
   GDB.
 
+mainenance info frame-unwinders
+  Add a CLASS column to the output.  This class is a somewhat arbitrary
+  grouping of unwinders, based on which area of GDB adds the unwinder.
+
 show configuration
   Now includes the version of GNU Readline library that GDB is using.
 
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index bc8746e27f0..e5498089ca9 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -1209,6 +1209,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,
@@ -1304,6 +1305,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 abded2ac192..b087afabae7 100644
--- a/gdb/alpha-mdebug-tdep.c
+++ b/gdb/alpha-mdebug-tdep.c
@@ -334,6 +334,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 301ab5cd01d..4ce45d29ade 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 5b1e77b3e86..5359959e3bb 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 e03180b08af..f1cd1fdef09 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -2670,6 +2670,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,
@@ -2816,6 +2817,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,
@@ -3008,6 +3010,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,
@@ -3019,6 +3022,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 e3e815e1123..ec32a57d7ea 100644
--- a/gdb/amd64-windows-tdep.c
+++ b/gdb/amd64-windows-tdep.c
@@ -1188,6 +1188,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 6fe79732158..b8a5fd80fa0 100644
--- a/gdb/amdgpu-tdep.c
+++ b/gdb/amdgpu-tdep.c
@@ -892,6 +892,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 9adf2959cf3..a4ab78d8ade 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 c27b2a2e1af..657ecd57098 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -2469,6 +2469,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,
@@ -3188,6 +3189,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,
@@ -3298,6 +3300,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,
@@ -3427,6 +3430,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,
@@ -3953,6 +3957,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 9c97d3cf1c7..08b3cb146f4 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 1fa7a66d043..60838f0548e 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 79a442068b5..1f53d63c982 100644
--- a/gdb/bpf-tdep.c
+++ b/gdb/bpf-tdep.c
@@ -185,6 +185,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 8562def71a6..4db2a234819 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 d69b8e5a2ca..6e8426fe2d8 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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 85ac3d9aab6..115c1f46b7f 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -42237,7 +42237,20 @@ architecture, then enabling this flag does not cause them to be used.
 
 @kindex maint info frame-unwinders
 @item maint info frame-unwinders
-List the frame unwinders currently in effect, starting with the highest priority.
+List the frame unwinders currently in effect, starting with the highest
+priority.  This also lists the unwinder class, which is mostly defined by
+which area of @value{GDBN} uses it.  The currently available classes are:
+
+@table @samp
+@item GDB
+Internal unwinders, added by @value{GDBN} core.
+@item EXTENSION
+Unwinders added by extension languages.
+@item DEBUGINFO
+Unwinders installed by debug information readers.
+@item ARCH
+Unwinders installed by the architecture specific code.
+@end table
 
 @kindex maint set worker-threads
 @kindex maint show worker-threads
diff --git a/gdb/dummy-frame.c b/gdb/dummy-frame.c
index 9f540d02f1a..e7de15b5c4d 100644
--- a/gdb/dummy-frame.c
+++ b/gdb/dummy-frame.c
@@ -379,6 +379,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 6ecf8a0b15d..50efd4eb5ff 100644
--- a/gdb/dwarf2/frame-tailcall.c
+++ b/gdb/dwarf2/frame-tailcall.c
@@ -472,6 +472,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 6d8a4fb5a9e..492b6928c62 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -1341,6 +1341,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,
@@ -1353,6 +1354,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 e61f6244913..d61f06d3305 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -31,6 +31,16 @@
 #include "cli/cli-cmds.h"
 #include "inferior.h"
 
+/* Conversion list between the enum for frame_unwind_class and
+   string.  */
+static const char * unwind_class_conversion[] =
+{
+  "GDB",
+  "EXTENSION",
+  "DEBUGINFO",
+  "ARCH",
+};
+
 /* Default sniffers, that must always be the first in the unwinder list,
    no matter the architecture.  */
 static constexpr auto standard_unwinders =
@@ -72,6 +82,13 @@ get_frame_unwind_table (struct gdbarch *gdbarch)
   return table;
 }
 
+static const char *
+frame_unwinder_class_str (frame_unwind_class uclass)
+{
+  gdb_assert (uclass < UNWIND_CLASS_NUMBER);
+  return unwind_class_conversion[uclass];
+}
+
 void
 frame_unwind_prepend_unwinder (struct gdbarch *gdbarch,
 				const struct frame_unwind *unwinder)
@@ -319,9 +336,10 @@ 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 (9, ui_left, "class", "Class");
   uiout->table_body ();
 
   for (auto unwinder : *table)
@@ -329,6 +347,8 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
       ui_out_emit_list tuple_emitter (uiout, nullptr);
       uiout->field_string ("name", unwinder->name);
       uiout->field_string ("type", frame_type_str (unwinder->type));
+      uiout->field_string ("class", frame_unwinder_class_str (
+					unwinder->unwinder_class));
       uiout->text ("\n");
     }
 }
diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
index 53fcd6869e9..ef8bbe091b5 100644
--- a/gdb/frame-unwind.h
+++ b/gdb/frame-unwind.h
@@ -156,12 +156,31 @@ 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);
 
+/* Unwinders are classified by what part of GDB code created it.  */
+enum frame_unwind_class
+{
+  /* This is mostly handled by core GDB code.  */
+  FRAME_UNWIND_GDB,
+  /* This unwinder was added by one of GDB's extension languages.  */
+  FRAME_UNWIND_EXTENSION,
+  /* The unwinder was created and mostly handles debug information.  */
+  FRAME_UNWIND_DEBUGINFO,
+  /* The unwinder was created and handles target dependent things.  */
+  FRAME_UNWIND_ARCH,
+  /* Meta enum value, to ensure we're always sent a valid unwinder class.  */
+  UNWIND_CLASS_NUMBER,
+};
+
 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 46424453ef0..7a7c4904475 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 6ae3f0d0f55..a1845a25750 100644
--- a/gdb/frv-tdep.c
+++ b/gdb/frv-tdep.c
@@ -1407,6 +1407,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 15a585a356e..e3827ad96e7 100644
--- a/gdb/ft32-tdep.c
+++ b/gdb/ft32-tdep.c
@@ -529,6 +529,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 e91d664ffa3..5b4466ed316 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 8f73f8d2374..2619b60655a 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 ad93c2b2048..2447b87ebae 100644
--- a/gdb/hppa-tdep.c
+++ b/gdb/hppa-tdep.c
@@ -2286,6 +2286,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,
@@ -2399,6 +2400,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,
@@ -2480,6 +2482,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 3539c6599a0..1d2b1a09d16 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 a552a2bee8f..6defd225bdb 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -2143,6 +2143,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,
@@ -2298,6 +2299,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,
@@ -2309,6 +2311,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,
@@ -2391,6 +2394,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,
@@ -2540,6 +2544,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 b0c1ad3d0ab..b10cc251bf1 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 759c526a7c2..8b3057159dc 100644
--- a/gdb/inline-frame.c
+++ b/gdb/inline-frame.c
@@ -272,6 +272,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 5776c66f78a..e0db1b0de4e 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 77d41bf86ba..33a19d2ba4d 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -1112,6 +1112,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 98a07281051..4eb5f2ad426 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 e91a69b73b9..4f00deb677e 100644
--- a/gdb/loongarch-tdep.c
+++ b/gdb/loongarch-tdep.c
@@ -457,6 +457,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 28dfb2fb7c6..bce12c5fae7 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 8eea6620df9..11a150a92b8 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 c6428f6db6f..aadb8c00d86 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 12cd0efbbe7..e14ac622fda 100644
--- a/gdb/m68hc11-tdep.c
+++ b/gdb/m68hc11-tdep.c
@@ -936,6 +936,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 7250ce09826..7cca4f86394 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 375d5e6e54c..2ff507e7816 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 a4ef343f9dc..7dd6371f995 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 609c665f2af..7a56471a4dd 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 90988cdfdac..18cb9485639 100644
--- a/gdb/mips-sde-tdep.c
+++ b/gdb/mips-sde-tdep.c
@@ -164,6 +164,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 c00efbd02ad..f5eb2093c8b 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -2929,6 +2929,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,
@@ -3365,6 +3366,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,
@@ -3744,6 +3746,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,
@@ -3860,6 +3863,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 d44eebfcb17..565300afd3c 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 29ad1f2bb2e..82d3478ed68 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 41a8f990a16..6d7fee230c1 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 8ad51d0798b..910a6f7a1d0 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/or1k-tdep.c b/gdb/or1k-tdep.c
index 290f748cc56..6e0466c3408 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 3f0f93f1ac0..12eb396a0e3 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 1bd79b3b3e1..ceb8b5d85db 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 68deaf98d81..ab32f5057f9 100644
--- a/gdb/python/py-unwind.c
+++ b/gdb/python/py-unwind.c
@@ -984,6 +984,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 bf4a368f4fe..47f2a7f6510 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -1944,6 +1944,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,
@@ -1956,6 +1957,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 932708ca4e9..2a292cc4ff8 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -3904,6 +3904,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 46a7ee9c3d4..0fcd5f9f1f3 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 3cc0232a691..f1c31ae27ea 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 a36c337c625..e3bffa8f33d 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -3841,6 +3841,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,
@@ -3982,6 +3983,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 6b12fe0a314..6cb74c23a3c 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 c24c75665ed..7fa73cf6b8c 100644
--- a/gdb/s12z-tdep.c
+++ b/gdb/s12z-tdep.c
@@ -444,6 +444,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 bc1db550d2e..556fad64926 100644
--- a/gdb/s390-linux-tdep.c
+++ b/gdb/s390-linux-tdep.c
@@ -544,6 +544,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 be176f07c6f..f0af9a8796d 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 4eaeae0d254..9fe4036dbbe 100644
--- a/gdb/sentinel-frame.c
+++ b/gdb/sentinel-frame.c
@@ -82,6 +82,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 d211265c15f..c8809e3018e 100644
--- a/gdb/sh-tdep.c
+++ b/gdb/sh-tdep.c
@@ -1953,6 +1953,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,
@@ -2020,6 +2021,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 fc22e66826f..5b3dd067375 100644
--- a/gdb/sparc-netbsd-tdep.c
+++ b/gdb/sparc-netbsd-tdep.c
@@ -252,6 +252,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 3182a7778d3..049c315fa14 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 aea3766d9b5..bb9c8a549b1 100644
--- a/gdb/sparc-sol2-tdep.c
+++ b/gdb/sparc-sol2-tdep.c
@@ -183,6 +183,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 b48b130c0c5..d69ab87be09 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 a30c7c448e5..1d4c075a961 100644
--- a/gdb/sparc64-fbsd-tdep.c
+++ b/gdb/sparc64-fbsd-tdep.c
@@ -200,6 +200,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 b101f4970d9..82eb99f57c1 100644
--- a/gdb/sparc64-netbsd-tdep.c
+++ b/gdb/sparc64-netbsd-tdep.c
@@ -226,6 +226,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 cef6efd3379..e81afa604e7 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 b7ca4ca1f5b..e8d09e2516e 100644
--- a/gdb/sparc64-sol2-tdep.c
+++ b/gdb/sparc64-sol2-tdep.c
@@ -186,6 +186,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 dc8032f2969..90b04fc0ff1 100644
--- a/gdb/sparc64-tdep.c
+++ b/gdb/sparc64-tdep.c
@@ -1139,6 +1139,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 6732478eb09..bac8ba84ed0 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 92f2be0b208..16cd25f4635 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 4f7c62d11fa..6368f67a2e7 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 de1cc6c47bd..d3f1af75417 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 a42c872feff..25d07c59376 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 766d1d144be..e01da4ecab4 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 d7a56ccc33c..d12dfdc7347 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 c442b60623b..66a12cd3be7 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.47.0


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

* [PATCH v8 3/5] gdb: Migrate frame unwinders to use C++ classes
  2024-12-10 19:51 [PATCH v8 0/5] Modernize frame unwinders and add disable feature Guinevere Larsen
  2024-12-10 19:51 ` [PATCH v8 1/5] gdb: make gdbarch store a vector of frame unwinders Guinevere Larsen
  2024-12-10 19:51 ` [PATCH v8 2/5] gdb: add "unwinder class" to " Guinevere Larsen
@ 2024-12-10 19:51 ` Guinevere Larsen
  2025-01-14 17:13   ` Andrew Burgess
  2024-12-10 19:51 ` [PATCH v8 4/5] gdb: introduce ability to disable frame unwinders Guinevere Larsen
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 24+ messages in thread
From: Guinevere Larsen @ 2024-12-10 19:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Guinevere Larsen, Thiago Jung Bauermann, Simon Marchi

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.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Approved-By: Simon Marchi <simon.marchi@efficios.com>
---
 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           |   6 +-
 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          |  68 +++++++++++++++--
 gdb/frame-unwind.h          | 147 ++++++++++++++++++++++++++++++++----
 gdb/frame.c                 |  21 ++----
 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/or1k-tdep.c             |   6 +-
 gdb/ppc-fbsd-tdep.c         |   4 +-
 gdb/ppc-obsd-tdep.c         |   4 +-
 gdb/python/py-unwind.c      |  62 +++++++++------
 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           |  71 +++++++++++------
 gdb/v850-tdep.c             |   4 +-
 gdb/vax-tdep.c              |   5 +-
 gdb/windows-tdep.c          |  35 ++++-----
 gdb/windows-tdep.h          |  16 +++-
 gdb/xstormy16-tdep.c        |   4 +-
 gdb/xtensa-tdep.c           |   6 +-
 gdb/z80-tdep.c              |   6 +-
 87 files changed, 553 insertions(+), 403 deletions(-)

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index e5498089ca9..840f9877361 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -1205,8 +1205,7 @@ aarch64_prologue_prev_register (const frame_info_ptr &this_frame,
 }
 
 /* AArch64 prologue unwinder.  */
-static frame_unwind aarch64_prologue_unwind =
-{
+static const frame_unwind_legacy aarch64_prologue_unwind (
   "aarch64 prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1215,7 +1214,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.
@@ -1301,8 +1300,7 @@ aarch64_stub_unwind_sniffer (const struct frame_unwind *self,
 }
 
 /* AArch64 stub unwinder.  */
-static frame_unwind aarch64_stub_unwind =
-{
+static const frame_unwind_legacy aarch64_stub_unwind (
   "aarch64 stub",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -1311,7 +1309,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 b087afabae7..b43849c203d 100644
--- a/gdb/alpha-mdebug-tdep.c
+++ b/gdb/alpha-mdebug-tdep.c
@@ -330,8 +330,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,
@@ -340,7 +339,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 4ce45d29ade..7201b724a9e 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 5359959e3bb..d1ebb0693e6 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 f1cd1fdef09..3c75f2fa22d 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -2666,8 +2666,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,
@@ -2676,7 +2675,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.  */
 
@@ -2813,8 +2812,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,
@@ -2823,7 +2821,7 @@ static const struct frame_unwind amd64_sigtramp_frame_unwind =
   amd64_sigtramp_frame_prev_register,
   NULL,
   amd64_sigtramp_frame_sniffer
-};
+);
 \f
 
 static CORE_ADDR
@@ -3006,8 +3004,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,
@@ -3016,10 +3013,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,
@@ -3028,7 +3024,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 ec32a57d7ea..83880afd1cf 100644
--- a/gdb/amd64-windows-tdep.c
+++ b/gdb/amd64-windows-tdep.c
@@ -1184,8 +1184,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,
@@ -1194,7 +1193,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 b8a5fd80fa0..3272d24a820 100644
--- a/gdb/amdgpu-tdep.c
+++ b/gdb/amdgpu-tdep.c
@@ -889,7 +889,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,
@@ -899,8 +899,8 @@ static const frame_unwind amdgpu_frame_unwind = {
   nullptr,
   default_frame_sniffer,
   nullptr,
-  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 a4ab78d8ade..d9fafc4d847 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 657ecd57098..b5273c4a049 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -2466,7 +2466,7 @@ arm_prologue_prev_register (const frame_info_ptr &this_frame,
 				       prev_regnum);
 }
 
-static frame_unwind arm_prologue_unwind = {
+static const frame_unwind_legacy arm_prologue_unwind (
   "arm prologue",
   NORMAL_FRAME,
   FRAME_UNWIND_ARCH,
@@ -2475,7 +2475,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
@@ -3186,7 +3186,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,
@@ -3195,7 +3195,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)
@@ -3296,8 +3296,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,
@@ -3305,8 +3304,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.
@@ -3427,7 +3426,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,
@@ -3436,7 +3435,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
@@ -3953,8 +3952,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,
@@ -3963,7 +3961,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 08b3cb146f4..b3fe3fef759 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 60838f0548e..78ade80ad25 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 1f53d63c982..8f3e50d86c1 100644
--- a/gdb/bpf-tdep.c
+++ b/gdb/bpf-tdep.c
@@ -181,8 +181,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,
@@ -191,7 +190,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 4db2a234819..f531b4ce1f0 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 6e8426fe2d8..4f41a51b576 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 const 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 e7de15b5c4d..697cc11a184 100644
--- a/gdb/dummy-frame.c
+++ b/gdb/dummy-frame.c
@@ -375,8 +375,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,
@@ -384,8 +383,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 50efd4eb5ff..54813d00b03 100644
--- a/gdb/dwarf2/frame-tailcall.c
+++ b/gdb/dwarf2/frame-tailcall.c
@@ -468,8 +468,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,
@@ -480,7 +479,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 492b6928c62..85e1d59bc09 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -1329,16 +1329,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,
@@ -1348,10 +1347,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,
@@ -1363,7 +1361,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 d61f06d3305..3ab6af1c4da 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -121,8 +121,8 @@ frame_unwind_try_unwinder (const frame_info_ptr &this_frame, void **this_cache,
 
   try
     {
-      frame_debug_printf ("trying unwinder \"%s\"", unwinder->name);
-      res = unwinder->sniffer (unwinder, this_frame, this_cache);
+      frame_debug_printf ("trying unwinder \"%s\"", unwinder->name ());
+      res = unwinder->sniff (this_frame, this_cache);
     }
   catch (const gdb_exception &ex)
     {
@@ -327,6 +327,64 @@ frame_unwind_got_address (const frame_info_ptr &frame, int regnum,
   return reg_val;
 }
 
+/* See frame-unwind.h.  */
+
+enum unwind_stop_reason
+frame_unwind_legacy::stop_reason (const frame_info_ptr &this_frame,
+				  void **this_prologue_cache) const
+{
+  return m_stop_reason (this_frame, this_prologue_cache);
+}
+
+/* See frame-unwind.h.  */
+
+void
+frame_unwind_legacy::this_id (const frame_info_ptr &this_frame,
+			      void **this_prologue_cache,
+			      struct frame_id *id) const
+{
+  return m_this_id (this_frame, this_prologue_cache, id);
+}
+
+/* See frame-unwind.h.  */
+
+struct value *
+frame_unwind_legacy::prev_register (const frame_info_ptr &this_frame,
+				    void **this_prologue_cache,
+				    int regnum) const
+{
+  return m_prev_register (this_frame, this_prologue_cache, regnum);
+}
+
+/* See frame-unwind.h.  */
+
+int
+frame_unwind_legacy::sniff (const frame_info_ptr &this_frame,
+			    void **this_prologue_cache) const
+{
+  return m_sniffer (this, this_frame, this_prologue_cache);
+}
+
+/* See frame-unwind.h.  */
+
+void
+frame_unwind_legacy::dealloc_cache (frame_info *self, void *this_cache) const
+{
+  if (m_dealloc_cache != nullptr)
+    m_dealloc_cache (self, this_cache);
+}
+
+/* See frame-unwind.h.  */
+
+struct gdbarch *
+frame_unwind_legacy::prev_arch (const frame_info_ptr &this_frame,
+				void **this_prologue_cache) const
+{
+  if (m_prev_arch == nullptr)
+    return frame_unwind::prev_arch (this_frame, this_prologue_cache);
+  return m_prev_arch (this_frame, this_prologue_cache);
+}
+
 /* Implement "maintenance info frame-unwinders" command.  */
 
 static void
@@ -345,10 +403,10 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
   for (auto unwinder : *table)
     {
       ui_out_emit_list tuple_emitter (uiout, nullptr);
-      uiout->field_string ("name", unwinder->name);
-      uiout->field_string ("type", frame_type_str (unwinder->type));
+      uiout->field_string ("name", unwinder->name ());
+      uiout->field_string ("type", frame_type_str (unwinder->type ()));
       uiout->field_string ("class", frame_unwinder_class_str (
-					unwinder->unwinder_class));
+					unwinder->unwinder_class ()));
       uiout->text ("\n");
     }
 }
diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
index ef8bbe091b5..13f18618d24 100644
--- a/gdb/frame-unwind.h
+++ b/gdb/frame-unwind.h
@@ -171,25 +171,146 @@ enum frame_unwind_class
   UNWIND_CLASS_NUMBER,
 };
 
-struct frame_unwind
+class frame_unwind
 {
-  const char *name;
+public:
+  frame_unwind (const char *name, frame_type type, frame_unwind_class uclass,
+		const frame_data *data)
+    : m_name (name), m_type (type), m_unwinder_class (uclass),
+      m_unwind_data (data)
+  { }
+
+  const char *name () const
+  { return m_name; }
+
+  frame_type type () const
+  { return m_type; }
+
+  frame_unwind_class unwinder_class () const
+  { return m_unwinder_class; }
+
+  const frame_data *unwind_data () const
+  { return m_unwind_data; }
+
+  /* Default stop_reason implementation.  It reports NO_REASON, unless the
+     frame is the outermost.  */
+
+  virtual unwind_stop_reason stop_reason (const frame_info_ptr &this_frame,
+					  void **this_prologue_cache) const
+  {
+    return default_frame_unwind_stop_reason (this_frame, this_prologue_cache);
+  }
+
+  /* Default frame sniffer.  Will always return that the unwinder
+     is able to unwind the frame.  */
+
+  virtual int sniff (const frame_info_ptr &this_frame,
+		     void **this_prologue_cache) const
+  { return 1; }
+
+  /* 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 = 0;
+
+  /* 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 = 0;
+
+  /* Properly deallocate the cache.  */
+
+  virtual void dealloc_cache (frame_info *self, void *this_cache) const
+  { }
+
+  /* Get the previous architecture.  */
+
+  virtual gdbarch *prev_arch (const frame_info_ptr &this_frame,
+			      void **this_prologue_cache) const
+  { return get_frame_arch (this_frame); }
+
+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;
+  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;
-  /* 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_class m_unwinder_class;
+
+  const frame_data *m_unwind_data;
+};
+
+/* 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
+{
+public:
+  frame_unwind_legacy (const char *name, frame_type type,
+		       frame_unwind_class uclass,
+		       frame_unwind_stop_reason_ftype *stop_reason_func,
+		       frame_this_id_ftype *this_id_func,
+		       frame_prev_register_ftype *prev_register_func,
+		       const struct frame_data *data,
+		       frame_sniffer_ftype *sniffer_func,
+		       frame_dealloc_cache_ftype *dealloc_cache_func = nullptr,
+		       frame_prev_arch_ftype *prev_arch_func = nullptr)
+  : frame_unwind (name, type, uclass, data), m_stop_reason (stop_reason_func),
+    m_this_id (this_id_func), m_prev_register (prev_register_func),
+    m_sniffer (sniffer_func), m_dealloc_cache (dealloc_cache_func),
+    m_prev_arch (prev_arch_func)
+  { }
+
+  /* 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 sniff (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.  */
+
+  struct gdbarch *prev_arch (const frame_info_ptr &this_frame,
+			     void **this_prologue_cache) const override;
+
+private:
+
+  frame_unwind_stop_reason_ftype *m_stop_reason;
+  frame_this_id_ftype *m_this_id;
+  frame_prev_register_ftype *m_prev_register;
+  frame_sniffer_ftype *m_sniffer;
+  frame_dealloc_cache_ftype *m_dealloc_cache;
+  frame_prev_arch_ftype *m_prev_arch;
 };
 
 /* Register a frame unwinder, _prepending_ it to the front of the
diff --git a/gdb/frame.c b/gdb/frame.c
index a6900b28072..e930aaf2e1d 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -269,12 +269,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);
 }
 
@@ -487,12 +485,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>,";
 
@@ -2363,7 +2361,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))))
@@ -2991,7 +2989,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 *
@@ -3072,11 +3070,8 @@ 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);
+      arch = next_frame->unwind->prev_arch (next_frame,
+					    &next_frame->prologue_cache);
 
       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 7a7c4904475..4941e4a0741 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 a1845a25750..b022cc75f85 100644
--- a/gdb/frv-tdep.c
+++ b/gdb/frv-tdep.c
@@ -1404,7 +1404,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,
@@ -1413,7 +1413,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 e3827ad96e7..2ef77a271b4 100644
--- a/gdb/ft32-tdep.c
+++ b/gdb/ft32-tdep.c
@@ -525,8 +525,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,
@@ -535,7 +534,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 5b4466ed316..a845900d299 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 2619b60655a..f43a5e73856 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 2447b87ebae..1fb68f261c6 100644
--- a/gdb/hppa-tdep.c
+++ b/gdb/hppa-tdep.c
@@ -2282,8 +2282,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,
@@ -2292,7 +2291,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
@@ -2396,8 +2395,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,
@@ -2406,7 +2404,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
@@ -2479,7 +2477,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,
@@ -2488,7 +2486,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 1d2b1a09d16..ea8addce599 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 6defd225bdb..75d175ad863 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -2139,8 +2139,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,
@@ -2149,7 +2148,7 @@ static const struct frame_unwind i386_frame_unwind =
   i386_frame_prev_register,
   NULL,
   default_frame_sniffer
-};
+);
 
 /* Normal frames, but in a function epilogue.  */
 
@@ -2295,8 +2294,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,
@@ -2305,10 +2303,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,
@@ -2317,7 +2314,7 @@ static const struct frame_unwind i386_epilogue_frame_unwind =
   i386_epilogue_frame_prev_register,
   NULL,
   i386_epilogue_frame_sniffer
-};
+);
 \f
 
 /* Stack-based trampolines.  */
@@ -2390,8 +2387,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,
@@ -2400,7 +2396,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.  */
 
@@ -2540,8 +2536,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,
@@ -2550,7 +2545,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 b10cc251bf1..11375d5bc69 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 8b3057159dc..1426ae2faf6 100644
--- a/gdb/inline-frame.c
+++ b/gdb/inline-frame.c
@@ -269,7 +269,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,
@@ -278,7 +278,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 3ddc9cf2335..96412154f6a 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 e0db1b0de4e..094f627e37b 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 33a19d2ba4d..e37f3072470 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -1108,8 +1108,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,
@@ -1119,7 +1118,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 4eb5f2ad426..c94aef4d839 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 4f00deb677e..1cab29bbecb 100644
--- a/gdb/loongarch-tdep.c
+++ b/gdb/loongarch-tdep.c
@@ -454,7 +454,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,
@@ -464,8 +464,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 bce12c5fae7..376b3622840 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 11a150a92b8..72c18337c4a 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 aadb8c00d86..8300528364e 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 e14ac622fda..84a44f525c2 100644
--- a/gdb/m68hc11-tdep.c
+++ b/gdb/m68hc11-tdep.c
@@ -933,7 +933,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,
@@ -942,7 +942,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 7cca4f86394..f7388d952e9 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 2ff507e7816..4318484a5c5 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 7dd6371f995..9b6f491c840 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 7a56471a4dd..99c0fc5abc4 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 18cb9485639..783e6fd35ab 100644
--- a/gdb/mips-sde-tdep.c
+++ b/gdb/mips-sde-tdep.c
@@ -160,8 +160,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,
@@ -170,7 +169,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 f5eb2093c8b..4091e5d7a44 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -2925,8 +2925,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,
@@ -2935,7 +2934,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,
@@ -3362,8 +3361,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,
@@ -3372,7 +3370,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,
@@ -3742,8 +3740,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,
@@ -3752,7 +3749,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,
@@ -3859,8 +3856,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,
@@ -3869,7 +3865,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 565300afd3c..f989bd6c75b 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 82d3478ed68..f69810f83c3 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 6d7fee230c1..17ebddf386a 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 910a6f7a1d0..e08ead24b4f 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/or1k-tdep.c b/gdb/or1k-tdep.c
index 6e0466c3408..bdfad9bfb03 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 12eb396a0e3..0403b5662b8 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 ceb8b5d85db..37be2d18032 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 ab32f5057f9..e10553c8a93 100644
--- a/gdb/python/py-unwind.c
+++ b/gdb/python/py-unwind.c
@@ -785,11 +785,35 @@ pending_framepy_level (PyObject *self, PyObject *args)
   return gdb_py_object_from_longest (level).release ();
 }
 
+/* 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 sniff (const frame_info_ptr &this_frame,
+	     void **this_prologue_cache) const override;
+
+  void this_id (const frame_info_ptr &this_frame, void **this_prologue_cache,
+		struct frame_id *id) const override;
+
+  struct value *prev_register (const frame_info_ptr &this_frame,
+			       void **this_prologue_cache,
+			       int regnum) const override;
+
+  void dealloc_cache (frame_info *self, void *this_cache) const override;
+};
+
 /* frame_unwind.this_id method.  */
 
-static void
-pyuw_this_id (const frame_info_ptr &this_frame, void **cache_ptr,
-	      struct frame_id *this_id)
+void
+frame_unwind_python::this_id (const frame_info_ptr &this_frame,
+			      void **cache_ptr,
+			      struct frame_id *this_id) const
 {
   *this_id = ((cached_frame_info *) *cache_ptr)->frame_id;
   pyuw_debug_printf ("frame_id: %s", this_id->to_string ().c_str ());
@@ -797,9 +821,9 @@ pyuw_this_id (const frame_info_ptr &this_frame, void **cache_ptr,
 
 /* frame_unwind.prev_register.  */
 
-static struct value *
-pyuw_prev_register (const frame_info_ptr &this_frame, void **cache_ptr,
-		    int regnum)
+struct value *
+frame_unwind_python::prev_register (const frame_info_ptr &this_frame,
+				    void **cache_ptr, int regnum) const
 {
   PYUW_SCOPED_DEBUG_ENTER_EXIT;
 
@@ -820,13 +844,13 @@ pyuw_prev_register (const frame_info_ptr &this_frame, void **cache_ptr,
 
 /* Frame sniffer dispatch.  */
 
-static int
-pyuw_sniffer (const struct frame_unwind *self, const frame_info_ptr &this_frame,
-	      void **cache_ptr)
+int
+frame_unwind_python::sniff (const frame_info_ptr &this_frame,
+			    void **cache_ptr) const
 {
   PYUW_SCOPED_DEBUG_ENTER_EXIT;
 
-  struct gdbarch *gdbarch = (struct gdbarch *) (self->unwind_data);
+  struct gdbarch *gdbarch = (struct gdbarch *) (this->unwind_data ());
   cached_frame_info *cached_frame;
 
   gdbpy_enter enter_py (gdbarch);
@@ -947,8 +971,8 @@ pyuw_sniffer (const struct frame_unwind *self, const frame_info_ptr &this_frame,
 
 /* Frame cache release shim.  */
 
-static void
-pyuw_dealloc_cache (frame_info *this_frame, void *cache)
+void
+frame_unwind_python::dealloc_cache (frame_info *this_frame, void *cache) const
 {
   PYUW_SCOPED_DEBUG_ENTER_EXIT;
   cached_frame_info *cached_frame = (cached_frame_info *) cache;
@@ -980,17 +1004,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 47f2a7f6510..a47b34e887c 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -1940,8 +1940,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,
@@ -1951,10 +1950,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,
@@ -1964,7 +1962,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 aea6507c947..a5b4082b243 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 2a292cc4ff8..0801d076a38 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -3900,8 +3900,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,
@@ -3911,8 +3910,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 0fcd5f9f1f3..f4aecce055b 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 f1c31ae27ea..e6255b1d3ba 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 e3bffa8f33d..26a78418edb 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -3837,8 +3837,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,
@@ -3847,7 +3846,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.  */
@@ -3979,8 +3978,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,
@@ -3988,7 +3986,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 6cb74c23a3c..18d184851cb 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 7fa73cf6b8c..faef1a42601 100644
--- a/gdb/s12z-tdep.c
+++ b/gdb/s12z-tdep.c
@@ -441,7 +441,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,
@@ -450,8 +450,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 556fad64926..96d6d446219 100644
--- a/gdb/s390-linux-tdep.c
+++ b/gdb/s390-linux-tdep.c
@@ -541,7 +541,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,
@@ -550,7 +550,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 f0af9a8796d..516546b317d 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 9fe4036dbbe..c809259920d 100644
--- a/gdb/sentinel-frame.c
+++ b/gdb/sentinel-frame.c
@@ -78,8 +78,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,
@@ -89,5 +88,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 c8809e3018e..375b796bfc8 100644
--- a/gdb/sh-tdep.c
+++ b/gdb/sh-tdep.c
@@ -1950,7 +1950,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,
@@ -1959,7 +1959,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)
@@ -2017,8 +2017,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,
@@ -2027,7 +2026,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 5b3dd067375..bc058dcbf63 100644
--- a/gdb/sparc-netbsd-tdep.c
+++ b/gdb/sparc-netbsd-tdep.c
@@ -248,8 +248,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,
@@ -258,7 +257,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 049c315fa14..2bc944efca0 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 bb9c8a549b1..b6ac8011b7b 100644
--- a/gdb/sparc-sol2-tdep.c
+++ b/gdb/sparc-sol2-tdep.c
@@ -179,8 +179,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,
@@ -189,7 +188,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 d69ab87be09..5fab313132b 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 1d4c075a961..6630a9b51b0 100644
--- a/gdb/sparc64-fbsd-tdep.c
+++ b/gdb/sparc64-fbsd-tdep.c
@@ -196,8 +196,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,
@@ -206,7 +205,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 82eb99f57c1..53c49fe1130 100644
--- a/gdb/sparc64-netbsd-tdep.c
+++ b/gdb/sparc64-netbsd-tdep.c
@@ -222,8 +222,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,
@@ -232,7 +231,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 e81afa604e7..8596d2fa069 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 e8d09e2516e..cadbf49788c 100644
--- a/gdb/sparc64-sol2-tdep.c
+++ b/gdb/sparc64-sol2-tdep.c
@@ -182,8 +182,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,
@@ -192,7 +191,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 90b04fc0ff1..7810b81c03b 100644
--- a/gdb/sparc64-tdep.c
+++ b/gdb/sparc64-tdep.c
@@ -1135,8 +1135,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,
@@ -1145,7 +1144,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 bac8ba84ed0..c7e42b154db 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 16cd25f4635..e2247d769df 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 6368f67a2e7..9353cf605e1 100644
--- a/gdb/tramp-frame.c
+++ b/gdb/tramp-frame.c
@@ -57,10 +57,41 @@ tramp_frame_cache (const frame_info_ptr &this_frame,
   return tramp_cache->trad_cache;
 }
 
-static void
-tramp_frame_this_id (const frame_info_ptr &this_frame,
-		     void **this_cache,
-		     struct frame_id *this_id)
+class frame_unwind_trampoline : public frame_unwind
+{
+public:
+  frame_unwind_trampoline (enum frame_type type, const struct frame_data *data,
+			   frame_prev_arch_ftype *prev_arch_func)
+    : frame_unwind ("trampoline", type, FRAME_UNWIND_GDB, data),
+      m_prev_arch (prev_arch_func)
+  { }
+
+  int sniff (const frame_info_ptr &this_frame,
+	     void **this_prologue_cache) const override;
+
+  void this_id (const frame_info_ptr &this_frame, void **this_prologue_cache,
+		struct frame_id *id) const override;
+
+  value *prev_register (const frame_info_ptr &this_frame,
+			void **this_prologue_cache,
+			int regnum) const override;
+
+  struct gdbarch *prev_arch (const frame_info_ptr &this_frame,
+			     void **this_prologue_cache) const override
+  {
+    if (m_prev_arch == nullptr)
+      return frame_unwind::prev_arch (this_frame, this_prologue_cache);
+    return m_prev_arch (this_frame, this_prologue_cache);
+  }
+
+private:
+  frame_prev_arch_ftype *m_prev_arch;
+};
+
+void
+frame_unwind_trampoline::this_id (const frame_info_ptr &this_frame,
+				  void **this_cache,
+				  struct frame_id *this_id) const
 {
   struct trad_frame_cache *trad_cache
     = tramp_frame_cache (this_frame, this_cache);
@@ -68,10 +99,10 @@ tramp_frame_this_id (const frame_info_ptr &this_frame,
   trad_frame_get_id (trad_cache, this_id);
 }
 
-static struct value *
-tramp_frame_prev_register (const frame_info_ptr &this_frame,
-			   void **this_cache,
-			   int prev_regnum)
+struct value *
+frame_unwind_trampoline::prev_register (const frame_info_ptr &this_frame,
+					void **this_cache,
+					int prev_regnum) const
 {
   struct trad_frame_cache *trad_cache
     = tramp_frame_cache (this_frame, this_cache);
@@ -119,12 +150,11 @@ tramp_frame_start (const struct tramp_frame *tramp,
   return 0;
 }
 
-static int
-tramp_frame_sniffer (const struct frame_unwind *self,
-		     const frame_info_ptr &this_frame,
-		     void **this_cache)
+int
+frame_unwind_trampoline::sniff (const frame_info_ptr &this_frame,
+				void **this_cache) const
 {
-  const struct tramp_frame *tramp = self->unwind_data->tramp_frame;
+  const struct tramp_frame *tramp = unwind_data ()->tramp_frame;
   CORE_ADDR pc = get_frame_pc (this_frame);
   CORE_ADDR func;
   struct tramp_frame_cache *tramp_cache;
@@ -161,16 +191,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 d3f1af75417..59a29307b14 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 25d07c59376..4660979928c 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 8a29a33042f..a5243dc8715 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,20 +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::sniff (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);
 
   CORE_ADDR pc = get_frame_pc (this_frame);
@@ -1376,7 +1373,7 @@ cygwin_sigwrapper_frame_sniffer (const struct frame_unwind *self_,
 		      paddress (gdbarch, end));
 
   int tlsoffset;
-  cygwin_sigwrapper_frame_analyze (gdbarch, start, end, self->patterns_list,
+  cygwin_sigwrapper_frame_analyze (gdbarch, start, end, patterns_list,
 				   &tlsoffset);
   if (tlsoffset == 0)
     return 0;
@@ -1396,13 +1393,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..84206fb0c89 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 sniff (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 e01da4ecab4..e7f4e6fe600 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 d12dfdc7347..c87940c1cfc 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 66a12cd3be7..c75b6ff3160 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.47.0


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

* [PATCH v8 4/5] gdb: introduce ability to disable frame unwinders
  2024-12-10 19:51 [PATCH v8 0/5] Modernize frame unwinders and add disable feature Guinevere Larsen
                   ` (2 preceding siblings ...)
  2024-12-10 19:51 ` [PATCH v8 3/5] gdb: Migrate frame unwinders to use C++ classes Guinevere Larsen
@ 2024-12-10 19:51 ` Guinevere Larsen
  2025-01-16 12:06   ` Andrew Burgess
  2025-01-16 16:22   ` Andrew Burgess
  2024-12-10 19:51 ` [PATCH v8 5/5] gdb/testsuite: Test for a backtrace through object without debuginfo Guinevere Larsen
                   ` (2 subsequent siblings)
  6 siblings, 2 replies; 24+ messages in thread
From: Guinevere Larsen @ 2024-12-10 19:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Guinevere Larsen, Eli Zaretskii, Thiago Jung Bauermann

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. This was requested back in 2003
in PR backtrace/8434.

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.

This commit is a more generic version of commit 3c3bb0580be0,
and so, based on the final paragraph of the commit message:
    gdb: Add switch to disable DWARF stack unwinders
<...>
    If in the future we find ourselves adding more switches to disable
    different unwinders, then we should probably move to a more generic
    solution, and remove this patch.
this patch also reverts 3c3bb0580be0

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=8434
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
---
 gdb/NEWS                                      |  17 ++
 gdb/doc/gdb.texinfo                           |  49 ++--
 gdb/dwarf2/frame-tailcall.c                   |   3 -
 gdb/dwarf2/frame.c                            |  30 ---
 gdb/dwarf2/frame.h                            |   6 -
 gdb/frame-unwind.c                            | 237 +++++++++++++++++-
 gdb/frame-unwind.h                            |   9 +
 .../gdb.base/frame-info-consistent.exp        |   8 +-
 gdb/testsuite/gdb.base/frame-unwind-disable.c |  22 ++
 .../gdb.base/frame-unwind-disable.exp         | 137 ++++++++++
 gdb/testsuite/gdb.base/maint.exp              |   4 -
 11 files changed, 437 insertions(+), 85 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 245b355669a..ef3317c11cb 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -125,6 +125,13 @@ maintenance info blocks [ADDRESS]
   are listed starting at the inner global block out to the most inner
   block.
 
+maintenance frame-unwinder disable [-all | -name NAME | [-class] CLASS]
+maintenance frame-unwinder enable [-all | -name NAME | [-class] CLASS]
+  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.
+
 info missing-objfile-handlers
   List all the registered missing-objfile handlers.
 
@@ -167,6 +174,16 @@ maintenance print remote-registers
 mainenance info frame-unwinders
   Add a CLASS column to the output.  This class is a somewhat arbitrary
   grouping of unwinders, based on which area of GDB adds the unwinder.
+  Also add an ENABLED column, that will show if the unwinder is enabled
+  or not.
+
+maintenance set dwarf unwinders (on|off)
+  This command has been removed because the same functionality can be
+  achieved with maint frame-unwinder (enable|disable) DEBUGINFO.
+
+maintenance show dwarf unwinders
+  This command has been removed since the functionality can be achieved
+  by checking the last column of maint info frame-unwinders.
 
 show configuration
   Now includes the version of GNU Readline library that GDB is using.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 115c1f46b7f..10e6654a03b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -42210,31 +42210,6 @@ On hosts without threading, or where worker threads have been disabled
 at runtime, this setting has no effect, as DWARF reading is always
 done on the main thread, and is therefore always synchronous.
 
-@kindex maint set dwarf unwinders
-@kindex maint show dwarf unwinders
-@item maint set dwarf unwinders
-@itemx maint show dwarf unwinders
-Control use of the DWARF frame unwinders.
-
-@cindex DWARF frame unwinders
-Many targets that support DWARF debugging use @value{GDBN}'s DWARF
-frame unwinders to build the backtrace.  Many of these targets will
-also have a second mechanism for building the backtrace for use in
-cases where DWARF information is not available, this second mechanism
-is often an analysis of a function's prologue.
-
-In order to extend testing coverage of the second level stack
-unwinding mechanisms it is helpful to be able to disable the DWARF
-stack unwinders, this can be done with this switch.
-
-In normal use of @value{GDBN} disabling the DWARF unwinders is not
-advisable, there are cases that are better handled through DWARF than
-prologue analysis, and the debug experience is likely to be better
-with the DWARF frame unwinders enabled.
-
-If DWARF frame unwinders are not supported for a particular target
-architecture, then enabling this flag does not cause them to be used.
-
 @kindex maint info frame-unwinders
 @item maint info frame-unwinders
 List the frame unwinders currently in effect, starting with the highest
@@ -42252,6 +42227,30 @@ Unwinders installed by debug information readers.
 Unwinders installed by the architecture specific code.
 @end table
 
+@kindex maint frame-unwinder disable
+@kindex maint frame-unwinder enable
+@item maint frame-unwinder disable [@code{-all} | @code{-name} @var{name} | [@code{-class}] @var{class}]
+@item maint frame-unwinder enable [@code{-all} | @code{-name} @var{name} | [@code{-class}] @var{class}]
+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 each of the invocations are as follows:
+
+@table @samp
+@item @code{-all}
+Disable or enable all unwinders.
+@item @code{-name} @var{name}
+@var{name} is the exact name of the unwinder to be disabled or enabled.
+@item [@code{-class}] @var{class}
+@var{class} is the class of frame unwinders to be disabled or enabled.
+The class may include the prefix @code{FRAME_UNWINDER_}, but it is not
+required.
+@end table
+
+@var{name} and @var{class} are always case insensitive.  If no option
+starting wth @code{-} is given, @value{GDBN} assumes @code{-class}.
+
 @kindex maint set worker-threads
 @kindex maint show worker-threads
 @item maint set worker-threads
diff --git a/gdb/dwarf2/frame-tailcall.c b/gdb/dwarf2/frame-tailcall.c
index 54813d00b03..2d7ab740f62 100644
--- a/gdb/dwarf2/frame-tailcall.c
+++ b/gdb/dwarf2/frame-tailcall.c
@@ -321,9 +321,6 @@ tailcall_frame_sniffer (const struct frame_unwind *self,
   int next_levels;
   struct tailcall_cache *cache;
 
-  if (!dwarf2_frame_unwinders_enabled_p)
-    return 0;
-
   /* Inner tail call element does not make sense for a sentinel frame.  */
   next_frame = get_next_frame (this_frame);
   if (next_frame == NULL)
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 85e1d59bc09..e0e8eb5dbec 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -183,9 +183,6 @@ static ULONGEST read_encoded_value (struct comp_unit *unit, gdb_byte encoding,
 				    unrelocated_addr func_base);
 \f
 
-/* See dwarf2/frame.h.  */
-bool dwarf2_frame_unwinders_enabled_p = true;
-
 /* Store the length the expression for the CFA in the `cfa_reg' field,
    which is unused in that case.  */
 #define cfa_exp_len cfa_reg
@@ -1306,9 +1303,6 @@ static int
 dwarf2_frame_sniffer (const struct frame_unwind *self,
 		      const frame_info_ptr &this_frame, void **this_cache)
 {
-  if (!dwarf2_frame_unwinders_enabled_p)
-    return 0;
-
   /* Grab an address that is guaranteed to reside somewhere within the
      function.  get_frame_pc(), with a no-return next function, can
      end up returning something past the end of this function's body.
@@ -2253,34 +2247,10 @@ dwarf2_build_frame_info (struct objfile *objfile)
   set_comp_unit (objfile, unit.release ());
 }
 
-/* Handle 'maintenance show dwarf unwinders'.  */
-
-static void
-show_dwarf_unwinders_enabled_p (struct ui_file *file, int from_tty,
-				struct cmd_list_element *c,
-				const char *value)
-{
-  gdb_printf (file,
-	      _("The DWARF stack unwinders are currently %s.\n"),
-	      value);
-}
-
 void _initialize_dwarf2_frame ();
 void
 _initialize_dwarf2_frame ()
 {
-  add_setshow_boolean_cmd ("unwinders", class_obscure,
-			   &dwarf2_frame_unwinders_enabled_p , _("\
-Set whether the DWARF stack frame unwinders are used."), _("\
-Show whether the DWARF stack frame unwinders are used."), _("\
-When enabled the DWARF stack frame unwinders can be used for architectures\n\
-that support the DWARF unwinders.  Enabling the DWARF unwinders for an\n\
-architecture that doesn't support them will have no effect."),
-			   NULL,
-			   show_dwarf_unwinders_enabled_p,
-			   &set_dwarf_cmdlist,
-			   &show_dwarf_cmdlist);
-
 #if GDB_SELF_TEST
   selftests::register_test_foreach_arch ("execute_cfa_program",
 					 selftests::execute_cfa_program_test);
diff --git a/gdb/dwarf2/frame.h b/gdb/dwarf2/frame.h
index 2167310fbdf..fe2d669a983 100644
--- a/gdb/dwarf2/frame.h
+++ b/gdb/dwarf2/frame.h
@@ -198,12 +198,6 @@ struct dwarf2_frame_state
   bool armcc_cfa_offsets_reversed = false;
 };
 
-/* When this is true the DWARF frame unwinders can be used if they are
-   registered with the gdbarch.  Not all architectures can or do use the
-   DWARF unwinders.  Setting this to true on a target that does not
-   otherwise support the DWARF unwinders has no effect.  */
-extern bool dwarf2_frame_unwinders_enabled_p;
-
 /* Set the architecture-specific register state initialization
    function for GDBARCH to INIT_REG.  */
 
diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
index 3ab6af1c4da..968bb846402 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -29,6 +29,7 @@
 #include "gdbarch.h"
 #include "dwarf2/frame-tailcall.h"
 #include "cli/cli-cmds.h"
+#include "cli/cli-option.h"
 #include "inferior.h"
 
 /* Conversion list between the enum for frame_unwind_class and
@@ -89,6 +90,20 @@ frame_unwinder_class_str (frame_unwind_class uclass)
   return unwind_class_conversion[uclass];
 }
 
+/* Case insensitive search for a frame_unwind_class based on the given
+   string.  */
+static enum frame_unwind_class
+str_to_frame_unwind_class (const char *class_str)
+{
+  for (int i = 0; i < UNWIND_CLASS_NUMBER; i++)
+    {
+      if (strcasecmp (unwind_class_conversion[i], class_str) == 0)
+	return (frame_unwind_class) i;
+    }
+
+  error (_("Unknown frame unwind class: %s"), class_str);
+}
+
 void
 frame_unwind_prepend_unwinder (struct gdbarch *gdbarch,
 				const struct frame_unwind *unwinder)
@@ -175,27 +190,46 @@ 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));
 
-  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 internal_error at the end of this function.  */
+  bool seen_disabled_unwinder = false;
+  /* Lambda to factor out the logic of checking if an unwinder is enabled,
+     testing it and otherwise recording if we saw a disable unwinder.  */
+  auto test_unwinder = [&] (const struct frame_unwind *unwinder)
+    {
+      if (unwinder == nullptr)
+	return false;
+
+      if (!unwinder->enabled ())
+	{
+	  seen_disabled_unwinder = true;
+	  return false;
+	}
+
+      return frame_unwind_try_unwinder (this_frame,
+					this_cache,
+					unwinder);
+    };
 
-  unwinder_from_target = target_get_unwinder ();
-  if (unwinder_from_target != NULL
-      && frame_unwind_try_unwinder (this_frame, this_cache,
-				   unwinder_from_target))
+  if (test_unwinder (target_get_unwinder ()))
     return;
 
-  unwinder_from_target = target_get_tailcall_unwinder ();
-  if (unwinder_from_target != NULL
-      && frame_unwind_try_unwinder (this_frame, this_cache,
-				   unwinder_from_target))
+  if (test_unwinder (target_get_tailcall_unwinder ()))
     return;
 
   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 (test_unwinder (unwinder))
+	return;
+    }
 
-  internal_error (_("frame_unwind_find_by_frame failed"));
+  if (seen_disabled_unwinder)
+    error (_("Required frame unwinder may have been disabled"
+	     ", see 'maint info frame-unwinders'"));
+  else
+    internal_error (_("frame_unwind_find_by_frame failed"));
 }
 
 /* A default frame sniffer which always accepts the frame.  Used by
@@ -394,10 +428,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 (9, ui_left, "class", "Class");
+  uiout->table_header (8, ui_left, "enabled", "Enabled");
   uiout->table_body ();
 
   for (auto unwinder : *table)
@@ -407,10 +442,145 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
       uiout->field_string ("type", frame_type_str (unwinder->type ()));
       uiout->field_string ("class", frame_unwinder_class_str (
 					unwinder->unwinder_class ()));
+      uiout->field_string ("enabled", unwinder->enabled () ? "Y" : "N");
       uiout->text ("\n");
     }
 }
 
+/* Options for disabling frame unwinders.  */
+struct maint_frame_unwind_options
+{
+  std::string unwinder_name;
+  const char *unwinder_class = nullptr;
+  bool all = false;
+};
+
+static const gdb::option::option_def maint_frame_unwind_opt_defs[] = {
+
+  gdb::option::flag_option_def<maint_frame_unwind_options> {
+    "all",
+    [] (maint_frame_unwind_options *opt) { return &opt->all; },
+    nullptr,
+    N_("Change the state of all unwinders")
+  },
+  gdb::option::string_option_def<maint_frame_unwind_options> {
+    "name",
+    [] (maint_frame_unwind_options *opt) { return &opt->unwinder_name; },
+    nullptr,
+    N_("The name of the unwinder to have its state changed")
+  },
+  gdb::option::enum_option_def<maint_frame_unwind_options> {
+    "class",
+    unwind_class_conversion,
+    [] (maint_frame_unwind_options *opt) { return &opt->unwinder_class; },
+    nullptr,
+    N_("The class of unwinders to have their states changed")
+  }
+};
+
+static inline gdb::option::option_def_group
+make_frame_unwind_enable_disable_options (maint_frame_unwind_options *opts)
+{
+  return {{maint_frame_unwind_opt_defs}, opts};
+}
+
+/* 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)
+{
+  if (args == nullptr)
+    {
+      if (enable)
+	error (_("Specify which frame unwinder(s) should be enabled"));
+      else
+	error (_("Specify which frame unwinder(s) should be disabled"));
+    }
+
+  struct gdbarch* gdbarch = current_inferior ()->arch ();
+  std::vector<const frame_unwind *> *unwinder_list
+    = get_frame_unwind_table (gdbarch);
+
+  maint_frame_unwind_options opts;
+  gdb::option::process_options
+    (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR,
+     make_frame_unwind_enable_disable_options (&opts));
+
+  if ((opts.all && !opts.unwinder_name.empty ())
+      || (opts.all && opts.unwinder_class != nullptr)
+      || (!opts.unwinder_name.empty () && opts.unwinder_class != nullptr))
+    error (_("Options are mutually exclusive"));
+
+  /* First see if the user wants to change all unwinders.  */
+  if (opts.all)
+    {
+      for (const frame_unwind *u : *unwinder_list)
+	u->set_enabled (enable);
+
+      reinit_frame_cache ();
+      return;
+    }
+
+  /* If user entered a specific unwinder name, handle it here.  If the
+     unwinder is already at the expected state, error out.  */
+  if (!opts.unwinder_name.empty ())
+    {
+      bool did_something = false;
+      for (const frame_unwind *unwinder : *unwinder_list)
+	{
+	  if (strcasecmp (unwinder->name (),
+			  opts.unwinder_name.c_str ()) == 0)
+	    {
+	      if (unwinder->enabled () == enable)
+		{
+		  if (unwinder->enabled ())
+		    error (_("unwinder %s is already enabled"),
+			     unwinder->name ());
+		  else
+		    error (_("unwinder %s is already disabled"),
+			     unwinder->name ());
+		}
+	      unwinder->set_enabled (enable);
+
+	      did_something = true;
+	      break;
+	    }
+	}
+      if (!did_something)
+	error (_("couldn't find unwinder named %s"),
+	       opts.unwinder_name.c_str ());
+    }
+  else
+    {
+      if (opts.unwinder_class == nullptr)
+	opts.unwinder_class = args;
+      enum frame_unwind_class dclass = str_to_frame_unwind_class
+	(opts.unwinder_class);
+      for (auto unwinder: *unwinder_list)
+	{
+	  if (unwinder->unwinder_class () == dclass)
+	    unwinder->set_enabled (enable);
+	}
+    }
+
+  reinit_frame_cache ();
+}
+
+/* Implement "maint frame-unwinder 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-unwinder enable" 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 ()
@@ -423,4 +593,45 @@ _initialize_frame_unwind ()
 List the frame unwinders currently in effect.\n\
 Unwinders are listed 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 [-all | -name NAME | [-class] CLASS]\n\
+\n\
+These are the meanings of the options:\n\
+\t-all    - All available unwinders will be disabled\n\
+\t-name   - NAME is the exact name of the frame unwinder to be disabled\n\
+\t-class  - CLASS is the class of unwinders to be disabled. Valid classes are:\n\
+\t\tGDB       - Unwinders added by GDB core;\n\
+\t\tEXTENSION - Unwinders added by extension languages;\n\
+\t\tDEBUGINFO - Unwinders that handle debug information;\n\
+\t\tARCH      - Unwinders that use architecture-specific information;\n\
+\n\
+UNWINDER and NAME are 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 [-all | -name NAME | [-class] CLASS]\n\
+\n\
+These are the meanings of the options:\n\
+\t-all    - All available unwinders will be enabled\n\
+\t-name   - NAME is the exact name of the frame unwinder to be enabled\n\
+\t-class  - CLASS is the class of unwinders to be enabled. Valid classes are:\n\
+\t\tGDB       - Unwinders added by GDB core;\n\
+\t\tEXTENSION - Unwinders added by extension languages;\n\
+\t\tDEBUGINFO - Unwinders that handle debug information;\n\
+\t\tARCH      - Unwinders that use architecture-specific information;\n\
+\n\
+UNWINDER and NAME are case insensitive."),
+	   &maint_frame_unwinder);
 }
diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
index 13f18618d24..adf8b47982c 100644
--- a/gdb/frame-unwind.h
+++ b/gdb/frame-unwind.h
@@ -192,6 +192,12 @@ class frame_unwind
   const frame_data *unwind_data () const
   { return m_unwind_data; }
 
+  bool enabled () const
+  { return m_enabled; }
+
+  void set_enabled (bool state) const
+  { m_enabled = state; }
+
   /* Default stop_reason implementation.  It reports NO_REASON, unless the
      frame is the outermost.  */
 
@@ -245,6 +251,9 @@ class frame_unwind
   frame_unwind_class m_unwinder_class;
 
   const frame_data *m_unwind_data;
+
+  /* Whether this unwinder can be used when sniffing.  */
+  mutable bool m_enabled = true;
 };
 
 /* This is a legacy version of the frame unwinder.  The original struct
diff --git a/gdb/testsuite/gdb.base/frame-info-consistent.exp b/gdb/testsuite/gdb.base/frame-info-consistent.exp
index fe0cfad95bc..4f483111a91 100644
--- a/gdb/testsuite/gdb.base/frame-info-consistent.exp
+++ b/gdb/testsuite/gdb.base/frame-info-consistent.exp
@@ -95,11 +95,11 @@ proc compare_frames {frames} {
     }
 }
 
-proc test {dwarf_unwinder} {
+proc test {enable} {
 
     clean_restart $::binfile
 
-    gdb_test_no_output "maint set dwarf unwinder $dwarf_unwinder"
+    gdb_test_no_output "maint frame-unwinder $enable DEBUGINFO"
 
     if {![runto_main]} {
 	return 0
@@ -134,6 +134,6 @@ proc test {dwarf_unwinder} {
     }
 }
 
-foreach_with_prefix dwarf {"off" "on"} {
-    test $dwarf
+foreach_with_prefix action {"disable" "enable"} {
+    test $action
 }
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..bbcfb01316e
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-unwind-disable.c
@@ -0,0 +1,22 @@
+/* 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..f4cad9a47fd
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-unwind-disable.exp
@@ -0,0 +1,137 @@
+# 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 ""} } {
+    set should_pass false
+    set command "maint info frame-unwinders"
+    if {${testname} == ""} {
+	set testname "checking state ${state} for ${unwinder_name}"
+    }
+    gdb_test_multiple "${command}" "${testname}" -lbl {
+	-re "${unwinder_name}\\s+\\w+\\s+\\w+\\s+${state}\\s+(?=\r\n)" {
+	    set should_pass true
+	    exp_continue
+	}
+	-re "${command}" {
+	    exp_continue
+	}
+	-re "\\w+\\s+\\w+\\s+\\w+\\s+\\w+\\s+(?=\r\n)" {
+	    exp_continue
+	}
+	-re -wrap "" {
+	    gdb_assert ${should_pass} "${gdb_test_name}"
+	}
+    }
+}
+
+# Check if all unwinders of class UNWINDER_CLASS are in the state STATE.
+# STATE can be either Y or N.
+# UNWINDER_CLASS can be one of: GDB, ARCH, EXTENSION, DEBUGINFO.  It can
+# also be \\w+ if checking all unwinders.
+proc check_unwinder_class { unwinder_class state {testname ""} } {
+    set command "maint info frame-unwinders"
+    set should_pass true
+    if {$testname == ""} {
+	set testname "checking if ${unwinder_class} state is ${state}"
+    }
+    gdb_test_multiple "${command}" "${testname}" -lbl {
+	-re "^\[^\r\n\]+\\s+\\w+\\s+${unwinder_class}\\s+\(\[YN\]\)\\s+(?=\r\n)" {
+	    # The unwinder name may have multiple words, so we need to use the
+	    # more generic [^\r\n] pattern to match the unwinders.
+	    set cur_state $expect_out(1,string)
+	    if {$cur_state == $state} {
+		set should_pass false
+	    }
+	    exp_continue
+	}
+	-re "${command}" {
+	    exp_continue
+	}
+	-re -wrap "" {
+	    gdb_assert ${should_pass} "${gdb_test_name}"
+	}
+    }
+}
+
+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 "\\w+" "Y" \
+    "all unwinders enabled before any changes"
+gdb_test_no_output "maint frame-unwinder disable -all"
+check_unwinder_class "\\w+" "N" \
+    "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" \
+    ".*Required frame unwinder may have been disabled, see 'maint info frame-unwinders'.*" \
+    "no suitable unwinders should be found"
+
+# Reenable all unwinders.
+gdb_test_no_output "maint frame-unwinder enable -all"
+check_unwinder_class "\\w+" "Y" \
+    "all unwinders should be re-enabled"
+
+# Check that we are able to get backtraces once again.
+gdb_test "backtrace" ".0\\s+main .. at.*" \
+    "we can get usable backtraces again"
+
+# 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" "dummy should continue 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 "${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" "dummy should continue 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 "${class}" "Y"
+}
diff --git a/gdb/testsuite/gdb.base/maint.exp b/gdb/testsuite/gdb.base/maint.exp
index 2c58ffa36c5..ae7ad0787e6 100644
--- a/gdb/testsuite/gdb.base/maint.exp
+++ b/gdb/testsuite/gdb.base/maint.exp
@@ -454,10 +454,6 @@ gdb_test_no_output "maint info line-table xxx.c" \
 
 set timeout $oldtimeout
 
-# Just check that the DWARF unwinders control flag is visible.
-gdb_test "maint show dwarf unwinders" \
-    "The DWARF stack unwinders are currently (on|off)\\."
-
 #============test help on maint commands
 
 test_prefix_command_help {"maint info" "maintenance info"} {
-- 
2.47.0


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

* [PATCH v8 5/5] gdb/testsuite: Test for a backtrace through object without debuginfo
  2024-12-10 19:51 [PATCH v8 0/5] Modernize frame unwinders and add disable feature Guinevere Larsen
                   ` (3 preceding siblings ...)
  2024-12-10 19:51 ` [PATCH v8 4/5] gdb: introduce ability to disable frame unwinders Guinevere Larsen
@ 2024-12-10 19:51 ` Guinevere Larsen
  2025-01-16 14:37   ` Andrew Burgess
  2025-01-07 12:11 ` [PING][PATCH v8 0/5] Modernize frame unwinders and add disable feature Guinevere Larsen
  2025-01-17 14:49 ` [PATCH " Guinevere Larsen
  6 siblings, 1 reply; 24+ messages in thread
From: Guinevere Larsen @ 2024-12-10 19:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Guinevere Larsen, Jan Kratochvil, Thiago Jung Bauermann

Fedora has been carrying this test since back in the Project Archer
days. A change back then caused GDB to stop being able to backtrace when
only some of the object files had debug information. Even though the
changed code never seems to have made its way into the main GDB project,
I think it makes sense to bring the test along to ensure something like
this doesn't pass unnoticed.

Co-Authored-By: Jan Kratochvil <jan@jankratochvil.net>
Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
---
 .../backtrace-through-cu-nodebug-caller.c     | 28 ++++++
 .../backtrace-through-cu-nodebug-main.c       | 32 +++++++
 .../gdb.base/backtrace-through-cu-nodebug.exp | 95 +++++++++++++++++++
 3 files changed, 155 insertions(+)
 create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
 create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
 create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp

diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
new file mode 100644
index 00000000000..3a63d72a468
--- /dev/null
+++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
@@ -0,0 +1,28 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2005-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/>.  */
+
+typedef int (*callback_t) (void);
+
+int
+caller (callback_t callback)
+{
+  /* Ensure some frame content to push away the return address.  */
+  volatile const long one = 1;
+
+  /* Modify the return value to prevent any tail-call optimization.  */
+  return (*callback) () - one;
+}
diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
new file mode 100644
index 00000000000..3e7ac57a166
--- /dev/null
+++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
@@ -0,0 +1,32 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2005-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/>.  */
+
+typedef int (*callback_t) (void);
+
+extern int caller (callback_t callback);
+
+int
+callback (void)
+{
+  return 1;
+}
+
+int
+main (void)
+{
+  return caller (callback);
+}
diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
new file mode 100644
index 00000000000..c0940b406a8
--- /dev/null
+++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
@@ -0,0 +1,95 @@
+# Copyright 2010-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 that GDB can generate accurate backtraces even if some of the stack
+# trace goes through a function with no debug information.
+
+standard_testfile -caller.c -main.c
+set objmainfile ${testfile}-main.o
+set objcallerfile ${testfile}-caller.o
+
+# recompile the inferior with or without CFI information, then run the
+# inferior until the point where the important test starts
+# returns TRUE on an ERROR.
+proc prepare_test {has_cfi} {
+    global srcdir subdir srcfile srcfile2 objmainfile objcallerfile binfile
+    if {$has_cfi} {
+	set extension "cfi"
+	if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
+	     "${srcdir}/${subdir}/${objcallerfile}" \
+	     object [list {additional_flags=-fomit-frame-pointer \
+		 -funwind-tables -fasynchronous-unwind-tables}]] != "" } {
+	    untested "couldn't compile with cfi"
+	    return true
+      }
+    } else {
+	set extension "no-cfi"
+	if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
+	     "${srcdir}/${subdir}/${objcallerfile}" \
+	     object [list {additional_flags=-fomit-frame-pointer \
+		 -fno-unwind-tables \
+		 -fno-asynchronous-unwind-tables}]] != "" } {
+	    untested "couldn't compile without cfi"
+	    return true
+      }
+    }
+    if {[gdb_compile [list "${srcdir}/${subdir}/${objmainfile}" \
+	    "${srcdir}/${subdir}/${objcallerfile}"] \
+	    "${binfile}-${extension}" binfile {}] != ""} {
+	untested "couldn't link object files"
+	return true
+    }
+
+    clean_restart "$binfile-${extension}"
+
+    with_test_prefix "${extension}" {
+
+	if ![runto callback] then {
+	   fail "has_cfi=$has_cfi: Can't run to callback"
+	   return true
+	}
+	gdb_test_no_output "maint frame-unwinder disable ARCH"
+	return false
+    }
+}
+
+if {[gdb_compile "${srcdir}/${subdir}/${srcfile2}" \
+	"${srcdir}/${subdir}/${objmainfile}" \
+	object {debug}] != "" } {
+    untested "couldn't compile main file"
+    return
+}
+
+if { [prepare_test false] } {
+     untested ${testfile}.exp
+} else {
+    gdb_test "bt" "Required frame unwinder may have been disabled.*" \
+	"verify unwind fail without CFI"
+}
+
+if { [prepare_test true] } {
+     untested ${testfile}.exp
+} else {
+    if { [istarget "arm*-*-*"] } {
+	setup_kfail backtrace/31950 *-*-*
+    }
+    set text {[^\r\n]+}
+    # #0  callback () at ...
+    # #1  0x00000000004004e9 in caller ()
+    # #2  0x00000000004004cd in main () at ...
+    gdb_test "bt" \
+	"#0 +callback $text\r\n#1 $text in caller $text\r\n#2 $text in main $text" \
+	"verify unwinding works for CFI without DIEs"
+}
-- 
2.47.0


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

* [PING][PATCH v8 0/5] Modernize frame unwinders and add disable feature
  2024-12-10 19:51 [PATCH v8 0/5] Modernize frame unwinders and add disable feature Guinevere Larsen
                   ` (4 preceding siblings ...)
  2024-12-10 19:51 ` [PATCH v8 5/5] gdb/testsuite: Test for a backtrace through object without debuginfo Guinevere Larsen
@ 2025-01-07 12:11 ` Guinevere Larsen
  2025-01-17 14:49 ` [PATCH " Guinevere Larsen
  6 siblings, 0 replies; 24+ messages in thread
From: Guinevere Larsen @ 2025-01-07 12:11 UTC (permalink / raw)
  To: gdb-patches, guinevere

Ping :)

All of the documentation, and patches 2 and 3, have already been approved.
On 12/10/24 4:51 PM, 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.
>
> The first patch is just a minor change, storing frame unwinders in a
> vector instead of through an unwinder table accessible, still accessed
> throught the registry subsystem. This isn't required (like I originally
> thought it was), but it does make the whole system more readable in my
> opinion.
>
> Patch 2 adds a new field for frame unwinders, their "class", which
> roughly correlates to which area of GDB adds that unwinder.  They were
> chosen rather arbitrarily based on my understanding, where the unwinder
> is added and its name. This class will be used to simplify bulk
> disabling of unwinders introduced by patch 4.
>
> 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. Also, as
> noted by Thiago Bauermann, no frame unwinders that use the legacy class
> ever pass a frame_data pointer, so that field was removed from those
> unwinders.
>
> Patch 4 adds the possibility to disable unwinders based on their name or
> class, and disable all unwinders at once if desired. It also reverts
> commit 3c3bb0580be0 since that patch pointed out that if a more generic
> system was implemented it could be reverted.
>
> Patch 5 adds the test I was trying to upstream all along.
>
> All the documentation has been approved by Eli already. Patches 2 and 3
> were already approved by Simon.
>
> Changes for v8:
> * Simplified the loop printing the table for "maint info
>    frame-unwinders".
> * Used gdb::option to parse the options for "maint frame-unwinder
>    disable"
> * Changed error message when no unwinder is found but some unwinders
>    were disabled
> * Cosmetic fixes.
>
> Changes for v7:
> * Moved the std::vector back to being accessed through the registry system
>      Removed the approvals from patch 1 because I think this change is
>      reasonably big, and needs another review.
> * Several cosmetic changes to the code, following feedback from Simon
>    and Thiago.
> * Fixed the test from patch 4, following Thiago's suggestion
> * Users can no longer specify the unwinder class with the
>    FRAME_UNWINDER_ prefix
>
> Changes for v6:
> * Patches 1 and 2 were approved by Simon
> * Several cosmetic changes to patch 3 and 4
> * New dealloc_cache default implementation (noop), and frame_unwind_legacy
>    defaults to it if no function pointer is given.
> * New prev_arch default implementation (getting the current arch), and
>    frame_unwind_legacy defaults to it if no pointer is given
> * Renamed "sniffer" to "sniff", to make it more clear what the method
>    does
> * Fixed some problems with the testcase.
>
> Changes for v5:
> * Reverted commit 3c3bb0580be0, with patch 4 following the advice on
>    that commit
> * on patch 3, some of the original functions were converted into the
>    classes' methods instead of just passing the call along.
> * some methods of frame_unwind_legacy were switched to be purely
>    virtual
>
> Changes for v4:
> * Rebase on current master (No changes needed)
> * improved documentation of unwinder classes follow Eli feedback
> * added KFAIL to new test on patch 5 if running on 32 bit arm
>
> Changes for v3:
> * Fixed Linaro CI issue in 32 bit arm
> * Fixed the few comments from Eli.
> * Documented addition of unwinder class in the patch that adds it.
>
> Changes for v2:
> * Added back the test that checks if GDB can handle a mix of CUs with
>    and without debuginfo in a single backtrace.
> * Fixed all the nitpicks for style.
> * Removed FRAME_UNWIND_ prefix when talking about classes
> * Thoroughly changed the documentation, to be more readable
>
> Guinevere Larsen (5):
>    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/testsuite: Test for a backtrace through object without debuginfo
>
>   gdb/NEWS                                      |  21 +
>   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                             |   7 +-
>   gdb/arc-tdep.c                                |  10 +-
>   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                           |  64 +--
>   gdb/dummy-frame.c                             |   8 +-
>   gdb/dummy-frame.h                             |   2 +-
>   gdb/dwarf2/frame-tailcall.c                   |   9 +-
>   gdb/dwarf2/frame-tailcall.h                   |   2 +-
>   gdb/dwarf2/frame.c                            |  46 +-
>   gdb/dwarf2/frame.h                            |   6 -
>   gdb/frame-unwind.c                            | 420 ++++++++++++++----
>   gdb/frame-unwind.h                            | 173 +++++++-
>   gdb/frame.c                                   |  21 +-
>   gdb/frv-linux-tdep.c                          |   6 +-
>   gdb/frv-tdep.c                                |   5 +-
>   gdb/ft32-tdep.c                               |   6 +-
>   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/or1k-tdep.c                               |   7 +-
>   gdb/ppc-fbsd-tdep.c                           |   5 +-
>   gdb/ppc-obsd-tdep.c                           |   5 +-
>   gdb/python/py-unwind.c                        |  61 ++-
>   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 +-
>   .../backtrace-through-cu-nodebug-caller.c     |  28 ++
>   .../backtrace-through-cu-nodebug-main.c       |  32 ++
>   .../gdb.base/backtrace-through-cu-nodebug.exp |  95 ++++
>   .../gdb.base/frame-info-consistent.exp        |   8 +-
>   gdb/testsuite/gdb.base/frame-unwind-disable.c |  22 +
>   .../gdb.base/frame-unwind-disable.exp         | 137 ++++++
>   gdb/testsuite/gdb.base/maint.exp              |   4 -
>   gdb/tic6x-tdep.c                              |  12 +-
>   gdb/tilegx-tdep.c                             |   5 +-
>   gdb/tramp-frame.c                             |  70 ++-
>   gdb/v850-tdep.c                               |   5 +-
>   gdb/vax-tdep.c                                |   6 +-
>   gdb/windows-tdep.c                            |  35 +-
>   gdb/windows-tdep.h                            |  16 +-
>   gdb/xstormy16-tdep.c                          |   5 +-
>   gdb/xtensa-tdep.c                             |   7 +-
>   gdb/z80-tdep.c                                |   7 +-
>   97 files changed, 1339 insertions(+), 551 deletions(-)
>   create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
>   create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
>   create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
>   create mode 100644 gdb/testsuite/gdb.base/frame-unwind-disable.c
>   create mode 100644 gdb/testsuite/gdb.base/frame-unwind-disable.exp
>

-- 
Cheers,
Guinevere Larsen
She/Her/Hers


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

* Re: [PATCH v8 1/5] gdb: make gdbarch store a vector of frame unwinders
  2024-12-10 19:51 ` [PATCH v8 1/5] gdb: make gdbarch store a vector of frame unwinders Guinevere Larsen
@ 2025-01-14 14:28   ` Andrew Burgess
  2025-01-14 20:34     ` Guinevere Larsen
  0 siblings, 1 reply; 24+ messages in thread
From: Andrew Burgess @ 2025-01-14 14:28 UTC (permalink / raw)
  To: Guinevere Larsen, gdb-patches; +Cc: Guinevere Larsen, Thiago Jung Bauermann

Guinevere Larsen <guinevere@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 destructors.
>
> Seeing as a future patch of this series wants to refactor the
> frame_unwind struct to use inheritance, and we'd like to not restrict
> the future derived classes on what destructors are allowed. In
> preparation for that change, this commit changes the registry in gdbarch
> to instead store an std::vector, which doesn't require using an obstack
> and doesn't rely on a linked list.
>
> There should be no user-visible changes.
>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> ---
>  gdb/frame-unwind.c | 107 +++++++++++++++------------------------------
>  1 file changed, 36 insertions(+), 71 deletions(-)
>
> diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
> index 352779fcdcc..e61f6244913 100644
> --- a/gdb/frame-unwind.c
> +++ b/gdb/frame-unwind.c
> @@ -31,61 +31,42 @@
>  #include "cli/cli-cmds.h"
>  #include "inferior.h"
>  
> -struct frame_unwind_table_entry
> +/* Default sniffers, that must always be the first in the unwinder list,
> +   no matter the architecture.  */
> +static constexpr auto standard_unwinders =

I'm not a huge fan of `auto` when the type is well known.  My thinking
is write once, read many times.  So I'd rather have the type information
available when I read the code.  For me:

  static constexpr std::initializer_list<const frame_unwind *> standard_unwinders =

tells me what's going on...

>  {
> -  const struct frame_unwind *unwinder;
> -  struct frame_unwind_table_entry *next;
> +  &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
> +     before the tailcall sniffer, resulting in confusion.  This is
> +     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.  */
> +  &dwarf2_tailcall_frame_unwind,
> +  &inline_frame_unwind,
>  };
>  
> -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 constexpr int prepend_unwinder_index = standard_unwinders.size ();
>  
> -static const registry<gdbarch>::key<struct frame_unwind_table>
> +static const registry<gdbarch>::key<std::vector<const frame_unwind *>>
>       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 *
> +/* Retrieve 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)

Given you're changing this code anyway, how about returning a reference
rather than a pointer, i.e.:

  static std::vector<const frame_unwind *> &
  get_frame_unwind_table (struct gdbarch *gdbarch)
  { ... }

The users of get_frame_unwind_table will need to be updated to match.

>  {
> -  struct frame_unwind_table *table = frame_unwind_data.get (gdbarch);
> +  std::vector<const frame_unwind *> *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;
> +  table = new std::vector<const frame_unwind *>;
> +  table->insert (table->begin (), standard_unwinders.begin (),
> +		 standard_unwinders.end ());
>  
> -  struct obstack *obstack = gdbarch_obstack (gdbarch);
> -  link = add_unwinder (obstack, &dummy_frame_unwind, link);
> -  /* 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
> -     before the tailcall sniffer, resulting in confusion.  This is
> -     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);
> -
> -  /* The insertion point for OSABI sniffers.  */
> -  table->osabi_head = link;
>    frame_unwind_data.set (gdbarch, table);
>  
>    return table;
> @@ -95,27 +76,16 @@ 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);
> +
> +  table->insert (table->begin () + prepend_unwinder_index, unwinder);
>  }
>  
>  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;
> -
> -  /* 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;
> +  get_frame_unwind_table (gdbarch)->push_back (unwinder);
>  }
>  
>  /* Call SNIFFER from UNWINDER.  If it succeeded set UNWINDER for
> @@ -188,9 +158,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 +172,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)

I think you can use:

  for (const auto &unwinder : *table)

here.  The 'const' is just good style as you're not going to modify it.
The '&' is not really necessary as the type is actually a pointer.
Better still might be:

  for (const frame_unwind *unwinder : *table)

But I don't mind the 'auto' here too much as the type is on the line
immediately above.  But I do think, if you're sticking with 'auto' here
then throwing the '&' in is a good idea.

> +    if (frame_unwind_try_unwinder (this_frame, this_cache, unwinder))
>        return;
>  
>    internal_error (_("frame_unwind_find_by_frame failed"));
> @@ -347,7 +316,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,15 +324,11 @@ 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 (auto unwinder : *table)

See the comments above.

If you're happy making this changes I suggest then:

Approved-By: Andrew Burgess <aburgess@redhat.com>

Thanks,
Andrew

>      {
> -      const char *name = entry->unwinder->name;
> -      const char *type = frame_type_str (entry->unwinder->type);
> -
>        ui_out_emit_list tuple_emitter (uiout, nullptr);
> -      uiout->field_string ("name", name);
> -      uiout->field_string ("type", type);
> +      uiout->field_string ("name", unwinder->name);
> +      uiout->field_string ("type", frame_type_str (unwinder->type));
>        uiout->text ("\n");
>      }
>  }
> -- 
> 2.47.0


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

* Re: [PATCH v8 2/5] gdb: add "unwinder class" to frame unwinders
  2024-12-10 19:51 ` [PATCH v8 2/5] gdb: add "unwinder class" to " Guinevere Larsen
@ 2025-01-14 15:28   ` Andrew Burgess
  0 siblings, 0 replies; 24+ messages in thread
From: Andrew Burgess @ 2025-01-14 15:28 UTC (permalink / raw)
  To: Guinevere Larsen, gdb-patches
  Cc: Guinevere Larsen, Eli Zaretskii, Thiago Jung Bauermann, Simon Marchi

Guinevere Larsen <guinevere@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 identifying 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.
>
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Approved-By: Simon Marchi <simon.marchi@efficios.com>

Approved-By: Andrew Burgess <aburgess@redhat.com>

Thanks,
Andrew


> ---
>  gdb/NEWS                    |  4 ++++
>  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/doc/gdb.texinfo         | 15 ++++++++++++++-
>  gdb/dummy-frame.c           |  1 +
>  gdb/dwarf2/frame-tailcall.c |  1 +
>  gdb/dwarf2/frame.c          |  2 ++
>  gdb/frame-unwind.c          | 22 +++++++++++++++++++++-
>  gdb/frame-unwind.h          | 19 +++++++++++++++++++
>  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/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 +
>  81 files changed, 168 insertions(+), 2 deletions(-)
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 361d7726ba0..245b355669a 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -164,6 +164,10 @@ maintenance print remote-registers
>    which registers were included in the last stop reply packet received by
>    GDB.
>  
> +mainenance info frame-unwinders
> +  Add a CLASS column to the output.  This class is a somewhat arbitrary
> +  grouping of unwinders, based on which area of GDB adds the unwinder.
> +
>  show configuration
>    Now includes the version of GNU Readline library that GDB is using.
>  
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
> index bc8746e27f0..e5498089ca9 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -1209,6 +1209,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,
> @@ -1304,6 +1305,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 abded2ac192..b087afabae7 100644
> --- a/gdb/alpha-mdebug-tdep.c
> +++ b/gdb/alpha-mdebug-tdep.c
> @@ -334,6 +334,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 301ab5cd01d..4ce45d29ade 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 5b1e77b3e86..5359959e3bb 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 e03180b08af..f1cd1fdef09 100644
> --- a/gdb/amd64-tdep.c
> +++ b/gdb/amd64-tdep.c
> @@ -2670,6 +2670,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,
> @@ -2816,6 +2817,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,
> @@ -3008,6 +3010,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,
> @@ -3019,6 +3022,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 e3e815e1123..ec32a57d7ea 100644
> --- a/gdb/amd64-windows-tdep.c
> +++ b/gdb/amd64-windows-tdep.c
> @@ -1188,6 +1188,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 6fe79732158..b8a5fd80fa0 100644
> --- a/gdb/amdgpu-tdep.c
> +++ b/gdb/amdgpu-tdep.c
> @@ -892,6 +892,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 9adf2959cf3..a4ab78d8ade 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 c27b2a2e1af..657ecd57098 100644
> --- a/gdb/arm-tdep.c
> +++ b/gdb/arm-tdep.c
> @@ -2469,6 +2469,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,
> @@ -3188,6 +3189,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,
> @@ -3298,6 +3300,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,
> @@ -3427,6 +3430,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,
> @@ -3953,6 +3957,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 9c97d3cf1c7..08b3cb146f4 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 1fa7a66d043..60838f0548e 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 79a442068b5..1f53d63c982 100644
> --- a/gdb/bpf-tdep.c
> +++ b/gdb/bpf-tdep.c
> @@ -185,6 +185,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 8562def71a6..4db2a234819 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 d69b8e5a2ca..6e8426fe2d8 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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 85ac3d9aab6..115c1f46b7f 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -42237,7 +42237,20 @@ architecture, then enabling this flag does not cause them to be used.
>  
>  @kindex maint info frame-unwinders
>  @item maint info frame-unwinders
> -List the frame unwinders currently in effect, starting with the highest priority.
> +List the frame unwinders currently in effect, starting with the highest
> +priority.  This also lists the unwinder class, which is mostly defined by
> +which area of @value{GDBN} uses it.  The currently available classes are:
> +
> +@table @samp
> +@item GDB
> +Internal unwinders, added by @value{GDBN} core.
> +@item EXTENSION
> +Unwinders added by extension languages.
> +@item DEBUGINFO
> +Unwinders installed by debug information readers.
> +@item ARCH
> +Unwinders installed by the architecture specific code.
> +@end table
>  
>  @kindex maint set worker-threads
>  @kindex maint show worker-threads
> diff --git a/gdb/dummy-frame.c b/gdb/dummy-frame.c
> index 9f540d02f1a..e7de15b5c4d 100644
> --- a/gdb/dummy-frame.c
> +++ b/gdb/dummy-frame.c
> @@ -379,6 +379,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 6ecf8a0b15d..50efd4eb5ff 100644
> --- a/gdb/dwarf2/frame-tailcall.c
> +++ b/gdb/dwarf2/frame-tailcall.c
> @@ -472,6 +472,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 6d8a4fb5a9e..492b6928c62 100644
> --- a/gdb/dwarf2/frame.c
> +++ b/gdb/dwarf2/frame.c
> @@ -1341,6 +1341,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,
> @@ -1353,6 +1354,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 e61f6244913..d61f06d3305 100644
> --- a/gdb/frame-unwind.c
> +++ b/gdb/frame-unwind.c
> @@ -31,6 +31,16 @@
>  #include "cli/cli-cmds.h"
>  #include "inferior.h"
>  
> +/* Conversion list between the enum for frame_unwind_class and
> +   string.  */
> +static const char * unwind_class_conversion[] =
> +{
> +  "GDB",
> +  "EXTENSION",
> +  "DEBUGINFO",
> +  "ARCH",
> +};
> +
>  /* Default sniffers, that must always be the first in the unwinder list,
>     no matter the architecture.  */
>  static constexpr auto standard_unwinders =
> @@ -72,6 +82,13 @@ get_frame_unwind_table (struct gdbarch *gdbarch)
>    return table;
>  }
>  
> +static const char *
> +frame_unwinder_class_str (frame_unwind_class uclass)
> +{
> +  gdb_assert (uclass < UNWIND_CLASS_NUMBER);
> +  return unwind_class_conversion[uclass];
> +}
> +
>  void
>  frame_unwind_prepend_unwinder (struct gdbarch *gdbarch,
>  				const struct frame_unwind *unwinder)
> @@ -319,9 +336,10 @@ 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 (9, ui_left, "class", "Class");
>    uiout->table_body ();
>  
>    for (auto unwinder : *table)
> @@ -329,6 +347,8 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
>        ui_out_emit_list tuple_emitter (uiout, nullptr);
>        uiout->field_string ("name", unwinder->name);
>        uiout->field_string ("type", frame_type_str (unwinder->type));
> +      uiout->field_string ("class", frame_unwinder_class_str (
> +					unwinder->unwinder_class));
>        uiout->text ("\n");
>      }
>  }
> diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
> index 53fcd6869e9..ef8bbe091b5 100644
> --- a/gdb/frame-unwind.h
> +++ b/gdb/frame-unwind.h
> @@ -156,12 +156,31 @@ 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);
>  
> +/* Unwinders are classified by what part of GDB code created it.  */
> +enum frame_unwind_class
> +{
> +  /* This is mostly handled by core GDB code.  */
> +  FRAME_UNWIND_GDB,
> +  /* This unwinder was added by one of GDB's extension languages.  */
> +  FRAME_UNWIND_EXTENSION,
> +  /* The unwinder was created and mostly handles debug information.  */
> +  FRAME_UNWIND_DEBUGINFO,
> +  /* The unwinder was created and handles target dependent things.  */
> +  FRAME_UNWIND_ARCH,
> +  /* Meta enum value, to ensure we're always sent a valid unwinder class.  */
> +  UNWIND_CLASS_NUMBER,
> +};
> +
>  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 46424453ef0..7a7c4904475 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 6ae3f0d0f55..a1845a25750 100644
> --- a/gdb/frv-tdep.c
> +++ b/gdb/frv-tdep.c
> @@ -1407,6 +1407,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 15a585a356e..e3827ad96e7 100644
> --- a/gdb/ft32-tdep.c
> +++ b/gdb/ft32-tdep.c
> @@ -529,6 +529,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 e91d664ffa3..5b4466ed316 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 8f73f8d2374..2619b60655a 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 ad93c2b2048..2447b87ebae 100644
> --- a/gdb/hppa-tdep.c
> +++ b/gdb/hppa-tdep.c
> @@ -2286,6 +2286,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,
> @@ -2399,6 +2400,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,
> @@ -2480,6 +2482,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 3539c6599a0..1d2b1a09d16 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 a552a2bee8f..6defd225bdb 100644
> --- a/gdb/i386-tdep.c
> +++ b/gdb/i386-tdep.c
> @@ -2143,6 +2143,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,
> @@ -2298,6 +2299,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,
> @@ -2309,6 +2311,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,
> @@ -2391,6 +2394,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,
> @@ -2540,6 +2544,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 b0c1ad3d0ab..b10cc251bf1 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 759c526a7c2..8b3057159dc 100644
> --- a/gdb/inline-frame.c
> +++ b/gdb/inline-frame.c
> @@ -272,6 +272,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 5776c66f78a..e0db1b0de4e 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 77d41bf86ba..33a19d2ba4d 100644
> --- a/gdb/jit.c
> +++ b/gdb/jit.c
> @@ -1112,6 +1112,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 98a07281051..4eb5f2ad426 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 e91a69b73b9..4f00deb677e 100644
> --- a/gdb/loongarch-tdep.c
> +++ b/gdb/loongarch-tdep.c
> @@ -457,6 +457,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 28dfb2fb7c6..bce12c5fae7 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 8eea6620df9..11a150a92b8 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 c6428f6db6f..aadb8c00d86 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 12cd0efbbe7..e14ac622fda 100644
> --- a/gdb/m68hc11-tdep.c
> +++ b/gdb/m68hc11-tdep.c
> @@ -936,6 +936,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 7250ce09826..7cca4f86394 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 375d5e6e54c..2ff507e7816 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 a4ef343f9dc..7dd6371f995 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 609c665f2af..7a56471a4dd 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 90988cdfdac..18cb9485639 100644
> --- a/gdb/mips-sde-tdep.c
> +++ b/gdb/mips-sde-tdep.c
> @@ -164,6 +164,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 c00efbd02ad..f5eb2093c8b 100644
> --- a/gdb/mips-tdep.c
> +++ b/gdb/mips-tdep.c
> @@ -2929,6 +2929,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,
> @@ -3365,6 +3366,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,
> @@ -3744,6 +3746,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,
> @@ -3860,6 +3863,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 d44eebfcb17..565300afd3c 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 29ad1f2bb2e..82d3478ed68 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 41a8f990a16..6d7fee230c1 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 8ad51d0798b..910a6f7a1d0 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/or1k-tdep.c b/gdb/or1k-tdep.c
> index 290f748cc56..6e0466c3408 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 3f0f93f1ac0..12eb396a0e3 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 1bd79b3b3e1..ceb8b5d85db 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 68deaf98d81..ab32f5057f9 100644
> --- a/gdb/python/py-unwind.c
> +++ b/gdb/python/py-unwind.c
> @@ -984,6 +984,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 bf4a368f4fe..47f2a7f6510 100644
> --- a/gdb/record-btrace.c
> +++ b/gdb/record-btrace.c
> @@ -1944,6 +1944,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,
> @@ -1956,6 +1957,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 932708ca4e9..2a292cc4ff8 100644
> --- a/gdb/riscv-tdep.c
> +++ b/gdb/riscv-tdep.c
> @@ -3904,6 +3904,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 46a7ee9c3d4..0fcd5f9f1f3 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 3cc0232a691..f1c31ae27ea 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 a36c337c625..e3bffa8f33d 100644
> --- a/gdb/rs6000-tdep.c
> +++ b/gdb/rs6000-tdep.c
> @@ -3841,6 +3841,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,
> @@ -3982,6 +3983,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 6b12fe0a314..6cb74c23a3c 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 c24c75665ed..7fa73cf6b8c 100644
> --- a/gdb/s12z-tdep.c
> +++ b/gdb/s12z-tdep.c
> @@ -444,6 +444,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 bc1db550d2e..556fad64926 100644
> --- a/gdb/s390-linux-tdep.c
> +++ b/gdb/s390-linux-tdep.c
> @@ -544,6 +544,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 be176f07c6f..f0af9a8796d 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 4eaeae0d254..9fe4036dbbe 100644
> --- a/gdb/sentinel-frame.c
> +++ b/gdb/sentinel-frame.c
> @@ -82,6 +82,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 d211265c15f..c8809e3018e 100644
> --- a/gdb/sh-tdep.c
> +++ b/gdb/sh-tdep.c
> @@ -1953,6 +1953,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,
> @@ -2020,6 +2021,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 fc22e66826f..5b3dd067375 100644
> --- a/gdb/sparc-netbsd-tdep.c
> +++ b/gdb/sparc-netbsd-tdep.c
> @@ -252,6 +252,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 3182a7778d3..049c315fa14 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 aea3766d9b5..bb9c8a549b1 100644
> --- a/gdb/sparc-sol2-tdep.c
> +++ b/gdb/sparc-sol2-tdep.c
> @@ -183,6 +183,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 b48b130c0c5..d69ab87be09 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 a30c7c448e5..1d4c075a961 100644
> --- a/gdb/sparc64-fbsd-tdep.c
> +++ b/gdb/sparc64-fbsd-tdep.c
> @@ -200,6 +200,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 b101f4970d9..82eb99f57c1 100644
> --- a/gdb/sparc64-netbsd-tdep.c
> +++ b/gdb/sparc64-netbsd-tdep.c
> @@ -226,6 +226,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 cef6efd3379..e81afa604e7 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 b7ca4ca1f5b..e8d09e2516e 100644
> --- a/gdb/sparc64-sol2-tdep.c
> +++ b/gdb/sparc64-sol2-tdep.c
> @@ -186,6 +186,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 dc8032f2969..90b04fc0ff1 100644
> --- a/gdb/sparc64-tdep.c
> +++ b/gdb/sparc64-tdep.c
> @@ -1139,6 +1139,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 6732478eb09..bac8ba84ed0 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 92f2be0b208..16cd25f4635 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 4f7c62d11fa..6368f67a2e7 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 de1cc6c47bd..d3f1af75417 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 a42c872feff..25d07c59376 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 766d1d144be..e01da4ecab4 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 d7a56ccc33c..d12dfdc7347 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 c442b60623b..66a12cd3be7 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.47.0


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

* Re: [PATCH v8 3/5] gdb: Migrate frame unwinders to use C++ classes
  2024-12-10 19:51 ` [PATCH v8 3/5] gdb: Migrate frame unwinders to use C++ classes Guinevere Larsen
@ 2025-01-14 17:13   ` Andrew Burgess
  0 siblings, 0 replies; 24+ messages in thread
From: Andrew Burgess @ 2025-01-14 17:13 UTC (permalink / raw)
  To: Guinevere Larsen, gdb-patches
  Cc: Guinevere Larsen, Thiago Jung Bauermann, Simon Marchi

Guinevere Larsen <guinevere@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.
>
> Finally, the cygwin unwinder was converted to a class since it was most
> of the way there already.
>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Approved-By: Simon Marchi <simon.marchi@efficios.com>
> ---
>  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           |   6 +-
>  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          |  68 +++++++++++++++--
>  gdb/frame-unwind.h          | 147 ++++++++++++++++++++++++++++++++----
>  gdb/frame.c                 |  21 ++----
>  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/or1k-tdep.c             |   6 +-
>  gdb/ppc-fbsd-tdep.c         |   4 +-
>  gdb/ppc-obsd-tdep.c         |   4 +-
>  gdb/python/py-unwind.c      |  62 +++++++++------
>  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           |  71 +++++++++++------
>  gdb/v850-tdep.c             |   4 +-
>  gdb/vax-tdep.c              |   5 +-
>  gdb/windows-tdep.c          |  35 ++++-----
>  gdb/windows-tdep.h          |  16 +++-
>  gdb/xstormy16-tdep.c        |   4 +-
>  gdb/xtensa-tdep.c           |   6 +-
>  gdb/z80-tdep.c              |   6 +-
>  87 files changed, 553 insertions(+), 403 deletions(-)
>
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
> index e5498089ca9..840f9877361 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -1205,8 +1205,7 @@ aarch64_prologue_prev_register (const frame_info_ptr &this_frame,
>  }
>  
>  /* AArch64 prologue unwinder.  */
> -static frame_unwind aarch64_prologue_unwind =
> -{
> +static const frame_unwind_legacy aarch64_prologue_unwind (
>    "aarch64 prologue",
>    NORMAL_FRAME,
>    FRAME_UNWIND_ARCH,
> @@ -1215,7 +1214,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.
> @@ -1301,8 +1300,7 @@ aarch64_stub_unwind_sniffer (const struct frame_unwind *self,
>  }
>  
>  /* AArch64 stub unwinder.  */
> -static frame_unwind aarch64_stub_unwind =
> -{
> +static const frame_unwind_legacy aarch64_stub_unwind (
>    "aarch64 stub",
>    NORMAL_FRAME,
>    FRAME_UNWIND_ARCH,
> @@ -1311,7 +1309,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 b087afabae7..b43849c203d 100644
> --- a/gdb/alpha-mdebug-tdep.c
> +++ b/gdb/alpha-mdebug-tdep.c
> @@ -330,8 +330,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,
> @@ -340,7 +339,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 4ce45d29ade..7201b724a9e 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 5359959e3bb..d1ebb0693e6 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 f1cd1fdef09..3c75f2fa22d 100644
> --- a/gdb/amd64-tdep.c
> +++ b/gdb/amd64-tdep.c
> @@ -2666,8 +2666,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,
> @@ -2676,7 +2675,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.  */
>  
> @@ -2813,8 +2812,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,
> @@ -2823,7 +2821,7 @@ static const struct frame_unwind amd64_sigtramp_frame_unwind =
>    amd64_sigtramp_frame_prev_register,
>    NULL,
>    amd64_sigtramp_frame_sniffer
> -};
> +);
>  \f
>  
>  static CORE_ADDR
> @@ -3006,8 +3004,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,
> @@ -3016,10 +3013,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,
> @@ -3028,7 +3024,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 ec32a57d7ea..83880afd1cf 100644
> --- a/gdb/amd64-windows-tdep.c
> +++ b/gdb/amd64-windows-tdep.c
> @@ -1184,8 +1184,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,
> @@ -1194,7 +1193,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 b8a5fd80fa0..3272d24a820 100644
> --- a/gdb/amdgpu-tdep.c
> +++ b/gdb/amdgpu-tdep.c
> @@ -889,7 +889,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,
> @@ -899,8 +899,8 @@ static const frame_unwind amdgpu_frame_unwind = {
>    nullptr,
>    default_frame_sniffer,
>    nullptr,
> -  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 a4ab78d8ade..d9fafc4d847 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 657ecd57098..b5273c4a049 100644
> --- a/gdb/arm-tdep.c
> +++ b/gdb/arm-tdep.c
> @@ -2466,7 +2466,7 @@ arm_prologue_prev_register (const frame_info_ptr &this_frame,
>  				       prev_regnum);
>  }
>  
> -static frame_unwind arm_prologue_unwind = {
> +static const frame_unwind_legacy arm_prologue_unwind (
>    "arm prologue",
>    NORMAL_FRAME,
>    FRAME_UNWIND_ARCH,
> @@ -2475,7 +2475,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
> @@ -3186,7 +3186,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,
> @@ -3195,7 +3195,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)
> @@ -3296,8 +3296,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,
> @@ -3305,8 +3304,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.
> @@ -3427,7 +3426,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,
> @@ -3436,7 +3435,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
> @@ -3953,8 +3952,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,
> @@ -3963,7 +3961,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 08b3cb146f4..b3fe3fef759 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 60838f0548e..78ade80ad25 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 1f53d63c982..8f3e50d86c1 100644
> --- a/gdb/bpf-tdep.c
> +++ b/gdb/bpf-tdep.c
> @@ -181,8 +181,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,
> @@ -191,7 +190,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 4db2a234819..f531b4ce1f0 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 6e8426fe2d8..4f41a51b576 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 const 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 e7de15b5c4d..697cc11a184 100644
> --- a/gdb/dummy-frame.c
> +++ b/gdb/dummy-frame.c
> @@ -375,8 +375,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,
> @@ -384,8 +383,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 50efd4eb5ff..54813d00b03 100644
> --- a/gdb/dwarf2/frame-tailcall.c
> +++ b/gdb/dwarf2/frame-tailcall.c
> @@ -468,8 +468,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,
> @@ -480,7 +479,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 492b6928c62..85e1d59bc09 100644
> --- a/gdb/dwarf2/frame.c
> +++ b/gdb/dwarf2/frame.c
> @@ -1329,16 +1329,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,
> @@ -1348,10 +1347,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,
> @@ -1363,7 +1361,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 d61f06d3305..3ab6af1c4da 100644
> --- a/gdb/frame-unwind.c
> +++ b/gdb/frame-unwind.c
> @@ -121,8 +121,8 @@ frame_unwind_try_unwinder (const frame_info_ptr &this_frame, void **this_cache,
>  
>    try
>      {
> -      frame_debug_printf ("trying unwinder \"%s\"", unwinder->name);
> -      res = unwinder->sniffer (unwinder, this_frame, this_cache);
> +      frame_debug_printf ("trying unwinder \"%s\"", unwinder->name ());
> +      res = unwinder->sniff (this_frame, this_cache);
>      }
>    catch (const gdb_exception &ex)
>      {
> @@ -327,6 +327,64 @@ frame_unwind_got_address (const frame_info_ptr &frame, int regnum,
>    return reg_val;
>  }
>  
> +/* See frame-unwind.h.  */
> +
> +enum unwind_stop_reason
> +frame_unwind_legacy::stop_reason (const frame_info_ptr &this_frame,
> +				  void **this_prologue_cache) const
> +{
> +  return m_stop_reason (this_frame, this_prologue_cache);
> +}
> +
> +/* See frame-unwind.h.  */
> +
> +void
> +frame_unwind_legacy::this_id (const frame_info_ptr &this_frame,
> +			      void **this_prologue_cache,
> +			      struct frame_id *id) const
> +{
> +  return m_this_id (this_frame, this_prologue_cache, id);
> +}
> +
> +/* See frame-unwind.h.  */
> +
> +struct value *
> +frame_unwind_legacy::prev_register (const frame_info_ptr &this_frame,
> +				    void **this_prologue_cache,
> +				    int regnum) const
> +{
> +  return m_prev_register (this_frame, this_prologue_cache, regnum);
> +}
> +
> +/* See frame-unwind.h.  */
> +
> +int
> +frame_unwind_legacy::sniff (const frame_info_ptr &this_frame,
> +			    void **this_prologue_cache) const
> +{
> +  return m_sniffer (this, this_frame, this_prologue_cache);
> +}
> +
> +/* See frame-unwind.h.  */
> +
> +void
> +frame_unwind_legacy::dealloc_cache (frame_info *self, void *this_cache) const
> +{
> +  if (m_dealloc_cache != nullptr)
> +    m_dealloc_cache (self, this_cache);
> +}
> +
> +/* See frame-unwind.h.  */
> +
> +struct gdbarch *
> +frame_unwind_legacy::prev_arch (const frame_info_ptr &this_frame,
> +				void **this_prologue_cache) const
> +{
> +  if (m_prev_arch == nullptr)
> +    return frame_unwind::prev_arch (this_frame, this_prologue_cache);
> +  return m_prev_arch (this_frame, this_prologue_cache);
> +}
> +
>  /* Implement "maintenance info frame-unwinders" command.  */
>  
>  static void
> @@ -345,10 +403,10 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
>    for (auto unwinder : *table)
>      {
>        ui_out_emit_list tuple_emitter (uiout, nullptr);
> -      uiout->field_string ("name", unwinder->name);
> -      uiout->field_string ("type", frame_type_str (unwinder->type));
> +      uiout->field_string ("name", unwinder->name ());
> +      uiout->field_string ("type", frame_type_str (unwinder->type ()));
>        uiout->field_string ("class", frame_unwinder_class_str (
> -					unwinder->unwinder_class));
> +					unwinder->unwinder_class ()));
>        uiout->text ("\n");
>      }
>  }
> diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
> index ef8bbe091b5..13f18618d24 100644
> --- a/gdb/frame-unwind.h
> +++ b/gdb/frame-unwind.h
> @@ -171,25 +171,146 @@ enum frame_unwind_class
>    UNWIND_CLASS_NUMBER,
>  };
>  
> -struct frame_unwind
> +class frame_unwind
>  {
> -  const char *name;
> +public:
> +  frame_unwind (const char *name, frame_type type, frame_unwind_class uclass,
> +		const frame_data *data)
> +    : m_name (name), m_type (type), m_unwinder_class (uclass),
> +      m_unwind_data (data)
> +  { }
> +
> +  const char *name () const
> +  { return m_name; }
> +
> +  frame_type type () const
> +  { return m_type; }
> +
> +  frame_unwind_class unwinder_class () const
> +  { return m_unwinder_class; }
> +
> +  const frame_data *unwind_data () const
> +  { return m_unwind_data; }
> +
> +  /* Default stop_reason implementation.  It reports NO_REASON, unless the
> +     frame is the outermost.  */
> +
> +  virtual unwind_stop_reason stop_reason (const frame_info_ptr &this_frame,
> +					  void **this_prologue_cache) const
> +  {
> +    return default_frame_unwind_stop_reason (this_frame, this_prologue_cache);
> +  }
> +
> +  /* Default frame sniffer.  Will always return that the unwinder
> +     is able to unwind the frame.  */
> +
> +  virtual int sniff (const frame_info_ptr &this_frame,
> +		     void **this_prologue_cache) const
> +  { return 1; }
> +
> +  /* 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 = 0;
> +
> +  /* 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 = 0;
> +
> +  /* Properly deallocate the cache.  */
> +
> +  virtual void dealloc_cache (frame_info *self, void *this_cache) const
> +  { }
> +
> +  /* Get the previous architecture.  */
> +
> +  virtual gdbarch *prev_arch (const frame_info_ptr &this_frame,
> +			      void **this_prologue_cache) const
> +  { return get_frame_arch (this_frame); }
> +
> +private:
> +
> +  const char *m_name;

This appears to be missing a comment...

> +
>    /* The frame's type.  Should this instead be a collection of
>       predicates that test the frame for various attributes?  */
> -  enum frame_type type;
> +  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;
> -  /* 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_class m_unwinder_class;
> +
> +  const frame_data *m_unwind_data;

... as is this.  I know none of this stuff was commented before, but
given we're giving this code a spring clean, it would be nice to comment
the data members.

> +};
> +
> +/* 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
> +{
> +public:

I'd normally use 'struct frame_unwind_legacy : ...' as this removes the
need to immediately add the 'public:' visibility tag.  There seems to be
a mix of the two in GDB, so you don't need to change this.  Just hoping
to make another 'struct' over 'class' convert :)

With the missing comments added, I think this looks great.

Approved-By: Andrew Burgess <aburgess@redhat.com>

Thanks,
Andrew

> +  frame_unwind_legacy (const char *name, frame_type type,
> +		       frame_unwind_class uclass,
> +		       frame_unwind_stop_reason_ftype *stop_reason_func,
> +		       frame_this_id_ftype *this_id_func,
> +		       frame_prev_register_ftype *prev_register_func,
> +		       const struct frame_data *data,
> +		       frame_sniffer_ftype *sniffer_func,
> +		       frame_dealloc_cache_ftype *dealloc_cache_func = nullptr,
> +		       frame_prev_arch_ftype *prev_arch_func = nullptr)
> +  : frame_unwind (name, type, uclass, data), m_stop_reason (stop_reason_func),
> +    m_this_id (this_id_func), m_prev_register (prev_register_func),
> +    m_sniffer (sniffer_func), m_dealloc_cache (dealloc_cache_func),
> +    m_prev_arch (prev_arch_func)
> +  { }
> +
> +  /* 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 sniff (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.  */
> +
> +  struct gdbarch *prev_arch (const frame_info_ptr &this_frame,
> +			     void **this_prologue_cache) const override;
> +
> +private:
> +
> +  frame_unwind_stop_reason_ftype *m_stop_reason;
> +  frame_this_id_ftype *m_this_id;
> +  frame_prev_register_ftype *m_prev_register;
> +  frame_sniffer_ftype *m_sniffer;
> +  frame_dealloc_cache_ftype *m_dealloc_cache;
> +  frame_prev_arch_ftype *m_prev_arch;
>  };
>  
>  /* Register a frame unwinder, _prepending_ it to the front of the
> diff --git a/gdb/frame.c b/gdb/frame.c
> index a6900b28072..e930aaf2e1d 100644
> --- a/gdb/frame.c
> +++ b/gdb/frame.c
> @@ -269,12 +269,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);
>  }
>  
> @@ -487,12 +485,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>,";
>  
> @@ -2363,7 +2361,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))))
> @@ -2991,7 +2989,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 *
> @@ -3072,11 +3070,8 @@ 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);
> +      arch = next_frame->unwind->prev_arch (next_frame,
> +					    &next_frame->prologue_cache);
>  
>        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 7a7c4904475..4941e4a0741 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 a1845a25750..b022cc75f85 100644
> --- a/gdb/frv-tdep.c
> +++ b/gdb/frv-tdep.c
> @@ -1404,7 +1404,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,
> @@ -1413,7 +1413,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 e3827ad96e7..2ef77a271b4 100644
> --- a/gdb/ft32-tdep.c
> +++ b/gdb/ft32-tdep.c
> @@ -525,8 +525,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,
> @@ -535,7 +534,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 5b4466ed316..a845900d299 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 2619b60655a..f43a5e73856 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 2447b87ebae..1fb68f261c6 100644
> --- a/gdb/hppa-tdep.c
> +++ b/gdb/hppa-tdep.c
> @@ -2282,8 +2282,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,
> @@ -2292,7 +2291,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
> @@ -2396,8 +2395,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,
> @@ -2406,7 +2404,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
> @@ -2479,7 +2477,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,
> @@ -2488,7 +2486,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 1d2b1a09d16..ea8addce599 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 6defd225bdb..75d175ad863 100644
> --- a/gdb/i386-tdep.c
> +++ b/gdb/i386-tdep.c
> @@ -2139,8 +2139,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,
> @@ -2149,7 +2148,7 @@ static const struct frame_unwind i386_frame_unwind =
>    i386_frame_prev_register,
>    NULL,
>    default_frame_sniffer
> -};
> +);
>  
>  /* Normal frames, but in a function epilogue.  */
>  
> @@ -2295,8 +2294,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,
> @@ -2305,10 +2303,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,
> @@ -2317,7 +2314,7 @@ static const struct frame_unwind i386_epilogue_frame_unwind =
>    i386_epilogue_frame_prev_register,
>    NULL,
>    i386_epilogue_frame_sniffer
> -};
> +);
>  \f
>  
>  /* Stack-based trampolines.  */
> @@ -2390,8 +2387,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,
> @@ -2400,7 +2396,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.  */
>  
> @@ -2540,8 +2536,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,
> @@ -2550,7 +2545,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 b10cc251bf1..11375d5bc69 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 8b3057159dc..1426ae2faf6 100644
> --- a/gdb/inline-frame.c
> +++ b/gdb/inline-frame.c
> @@ -269,7 +269,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,
> @@ -278,7 +278,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 3ddc9cf2335..96412154f6a 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 e0db1b0de4e..094f627e37b 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 33a19d2ba4d..e37f3072470 100644
> --- a/gdb/jit.c
> +++ b/gdb/jit.c
> @@ -1108,8 +1108,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,
> @@ -1119,7 +1118,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 4eb5f2ad426..c94aef4d839 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 4f00deb677e..1cab29bbecb 100644
> --- a/gdb/loongarch-tdep.c
> +++ b/gdb/loongarch-tdep.c
> @@ -454,7 +454,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,
> @@ -464,8 +464,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 bce12c5fae7..376b3622840 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 11a150a92b8..72c18337c4a 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 aadb8c00d86..8300528364e 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 e14ac622fda..84a44f525c2 100644
> --- a/gdb/m68hc11-tdep.c
> +++ b/gdb/m68hc11-tdep.c
> @@ -933,7 +933,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,
> @@ -942,7 +942,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 7cca4f86394..f7388d952e9 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 2ff507e7816..4318484a5c5 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 7dd6371f995..9b6f491c840 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 7a56471a4dd..99c0fc5abc4 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 18cb9485639..783e6fd35ab 100644
> --- a/gdb/mips-sde-tdep.c
> +++ b/gdb/mips-sde-tdep.c
> @@ -160,8 +160,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,
> @@ -170,7 +169,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 f5eb2093c8b..4091e5d7a44 100644
> --- a/gdb/mips-tdep.c
> +++ b/gdb/mips-tdep.c
> @@ -2925,8 +2925,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,
> @@ -2935,7 +2934,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,
> @@ -3362,8 +3361,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,
> @@ -3372,7 +3370,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,
> @@ -3742,8 +3740,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,
> @@ -3752,7 +3749,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,
> @@ -3859,8 +3856,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,
> @@ -3869,7 +3865,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 565300afd3c..f989bd6c75b 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 82d3478ed68..f69810f83c3 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 6d7fee230c1..17ebddf386a 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 910a6f7a1d0..e08ead24b4f 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/or1k-tdep.c b/gdb/or1k-tdep.c
> index 6e0466c3408..bdfad9bfb03 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 12eb396a0e3..0403b5662b8 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 ceb8b5d85db..37be2d18032 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 ab32f5057f9..e10553c8a93 100644
> --- a/gdb/python/py-unwind.c
> +++ b/gdb/python/py-unwind.c
> @@ -785,11 +785,35 @@ pending_framepy_level (PyObject *self, PyObject *args)
>    return gdb_py_object_from_longest (level).release ();
>  }
>  
> +/* 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 sniff (const frame_info_ptr &this_frame,
> +	     void **this_prologue_cache) const override;
> +
> +  void this_id (const frame_info_ptr &this_frame, void **this_prologue_cache,
> +		struct frame_id *id) const override;
> +
> +  struct value *prev_register (const frame_info_ptr &this_frame,
> +			       void **this_prologue_cache,
> +			       int regnum) const override;
> +
> +  void dealloc_cache (frame_info *self, void *this_cache) const override;
> +};
> +
>  /* frame_unwind.this_id method.  */
>  
> -static void
> -pyuw_this_id (const frame_info_ptr &this_frame, void **cache_ptr,
> -	      struct frame_id *this_id)
> +void
> +frame_unwind_python::this_id (const frame_info_ptr &this_frame,
> +			      void **cache_ptr,
> +			      struct frame_id *this_id) const
>  {
>    *this_id = ((cached_frame_info *) *cache_ptr)->frame_id;
>    pyuw_debug_printf ("frame_id: %s", this_id->to_string ().c_str ());
> @@ -797,9 +821,9 @@ pyuw_this_id (const frame_info_ptr &this_frame, void **cache_ptr,
>  
>  /* frame_unwind.prev_register.  */
>  
> -static struct value *
> -pyuw_prev_register (const frame_info_ptr &this_frame, void **cache_ptr,
> -		    int regnum)
> +struct value *
> +frame_unwind_python::prev_register (const frame_info_ptr &this_frame,
> +				    void **cache_ptr, int regnum) const
>  {
>    PYUW_SCOPED_DEBUG_ENTER_EXIT;
>  
> @@ -820,13 +844,13 @@ pyuw_prev_register (const frame_info_ptr &this_frame, void **cache_ptr,
>  
>  /* Frame sniffer dispatch.  */
>  
> -static int
> -pyuw_sniffer (const struct frame_unwind *self, const frame_info_ptr &this_frame,
> -	      void **cache_ptr)
> +int
> +frame_unwind_python::sniff (const frame_info_ptr &this_frame,
> +			    void **cache_ptr) const
>  {
>    PYUW_SCOPED_DEBUG_ENTER_EXIT;
>  
> -  struct gdbarch *gdbarch = (struct gdbarch *) (self->unwind_data);
> +  struct gdbarch *gdbarch = (struct gdbarch *) (this->unwind_data ());
>    cached_frame_info *cached_frame;
>  
>    gdbpy_enter enter_py (gdbarch);
> @@ -947,8 +971,8 @@ pyuw_sniffer (const struct frame_unwind *self, const frame_info_ptr &this_frame,
>  
>  /* Frame cache release shim.  */
>  
> -static void
> -pyuw_dealloc_cache (frame_info *this_frame, void *cache)
> +void
> +frame_unwind_python::dealloc_cache (frame_info *this_frame, void *cache) const
>  {
>    PYUW_SCOPED_DEBUG_ENTER_EXIT;
>    cached_frame_info *cached_frame = (cached_frame_info *) cache;
> @@ -980,17 +1004,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 47f2a7f6510..a47b34e887c 100644
> --- a/gdb/record-btrace.c
> +++ b/gdb/record-btrace.c
> @@ -1940,8 +1940,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,
> @@ -1951,10 +1950,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,
> @@ -1964,7 +1962,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 aea6507c947..a5b4082b243 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 2a292cc4ff8..0801d076a38 100644
> --- a/gdb/riscv-tdep.c
> +++ b/gdb/riscv-tdep.c
> @@ -3900,8 +3900,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,
> @@ -3911,8 +3910,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 0fcd5f9f1f3..f4aecce055b 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 f1c31ae27ea..e6255b1d3ba 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 e3bffa8f33d..26a78418edb 100644
> --- a/gdb/rs6000-tdep.c
> +++ b/gdb/rs6000-tdep.c
> @@ -3837,8 +3837,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,
> @@ -3847,7 +3846,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.  */
> @@ -3979,8 +3978,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,
> @@ -3988,7 +3986,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 6cb74c23a3c..18d184851cb 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 7fa73cf6b8c..faef1a42601 100644
> --- a/gdb/s12z-tdep.c
> +++ b/gdb/s12z-tdep.c
> @@ -441,7 +441,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,
> @@ -450,8 +450,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 556fad64926..96d6d446219 100644
> --- a/gdb/s390-linux-tdep.c
> +++ b/gdb/s390-linux-tdep.c
> @@ -541,7 +541,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,
> @@ -550,7 +550,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 f0af9a8796d..516546b317d 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 9fe4036dbbe..c809259920d 100644
> --- a/gdb/sentinel-frame.c
> +++ b/gdb/sentinel-frame.c
> @@ -78,8 +78,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,
> @@ -89,5 +88,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 c8809e3018e..375b796bfc8 100644
> --- a/gdb/sh-tdep.c
> +++ b/gdb/sh-tdep.c
> @@ -1950,7 +1950,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,
> @@ -1959,7 +1959,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)
> @@ -2017,8 +2017,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,
> @@ -2027,7 +2026,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 5b3dd067375..bc058dcbf63 100644
> --- a/gdb/sparc-netbsd-tdep.c
> +++ b/gdb/sparc-netbsd-tdep.c
> @@ -248,8 +248,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,
> @@ -258,7 +257,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 049c315fa14..2bc944efca0 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 bb9c8a549b1..b6ac8011b7b 100644
> --- a/gdb/sparc-sol2-tdep.c
> +++ b/gdb/sparc-sol2-tdep.c
> @@ -179,8 +179,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,
> @@ -189,7 +188,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 d69ab87be09..5fab313132b 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 1d4c075a961..6630a9b51b0 100644
> --- a/gdb/sparc64-fbsd-tdep.c
> +++ b/gdb/sparc64-fbsd-tdep.c
> @@ -196,8 +196,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,
> @@ -206,7 +205,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 82eb99f57c1..53c49fe1130 100644
> --- a/gdb/sparc64-netbsd-tdep.c
> +++ b/gdb/sparc64-netbsd-tdep.c
> @@ -222,8 +222,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,
> @@ -232,7 +231,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 e81afa604e7..8596d2fa069 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 e8d09e2516e..cadbf49788c 100644
> --- a/gdb/sparc64-sol2-tdep.c
> +++ b/gdb/sparc64-sol2-tdep.c
> @@ -182,8 +182,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,
> @@ -192,7 +191,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 90b04fc0ff1..7810b81c03b 100644
> --- a/gdb/sparc64-tdep.c
> +++ b/gdb/sparc64-tdep.c
> @@ -1135,8 +1135,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,
> @@ -1145,7 +1144,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 bac8ba84ed0..c7e42b154db 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 16cd25f4635..e2247d769df 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 6368f67a2e7..9353cf605e1 100644
> --- a/gdb/tramp-frame.c
> +++ b/gdb/tramp-frame.c
> @@ -57,10 +57,41 @@ tramp_frame_cache (const frame_info_ptr &this_frame,
>    return tramp_cache->trad_cache;
>  }
>  
> -static void
> -tramp_frame_this_id (const frame_info_ptr &this_frame,
> -		     void **this_cache,
> -		     struct frame_id *this_id)
> +class frame_unwind_trampoline : public frame_unwind
> +{
> +public:
> +  frame_unwind_trampoline (enum frame_type type, const struct frame_data *data,
> +			   frame_prev_arch_ftype *prev_arch_func)
> +    : frame_unwind ("trampoline", type, FRAME_UNWIND_GDB, data),
> +      m_prev_arch (prev_arch_func)
> +  { }
> +
> +  int sniff (const frame_info_ptr &this_frame,
> +	     void **this_prologue_cache) const override;
> +
> +  void this_id (const frame_info_ptr &this_frame, void **this_prologue_cache,
> +		struct frame_id *id) const override;
> +
> +  value *prev_register (const frame_info_ptr &this_frame,
> +			void **this_prologue_cache,
> +			int regnum) const override;
> +
> +  struct gdbarch *prev_arch (const frame_info_ptr &this_frame,
> +			     void **this_prologue_cache) const override
> +  {
> +    if (m_prev_arch == nullptr)
> +      return frame_unwind::prev_arch (this_frame, this_prologue_cache);
> +    return m_prev_arch (this_frame, this_prologue_cache);
> +  }
> +
> +private:
> +  frame_prev_arch_ftype *m_prev_arch;
> +};
> +
> +void
> +frame_unwind_trampoline::this_id (const frame_info_ptr &this_frame,
> +				  void **this_cache,
> +				  struct frame_id *this_id) const
>  {
>    struct trad_frame_cache *trad_cache
>      = tramp_frame_cache (this_frame, this_cache);
> @@ -68,10 +99,10 @@ tramp_frame_this_id (const frame_info_ptr &this_frame,
>    trad_frame_get_id (trad_cache, this_id);
>  }
>  
> -static struct value *
> -tramp_frame_prev_register (const frame_info_ptr &this_frame,
> -			   void **this_cache,
> -			   int prev_regnum)
> +struct value *
> +frame_unwind_trampoline::prev_register (const frame_info_ptr &this_frame,
> +					void **this_cache,
> +					int prev_regnum) const
>  {
>    struct trad_frame_cache *trad_cache
>      = tramp_frame_cache (this_frame, this_cache);
> @@ -119,12 +150,11 @@ tramp_frame_start (const struct tramp_frame *tramp,
>    return 0;
>  }
>  
> -static int
> -tramp_frame_sniffer (const struct frame_unwind *self,
> -		     const frame_info_ptr &this_frame,
> -		     void **this_cache)
> +int
> +frame_unwind_trampoline::sniff (const frame_info_ptr &this_frame,
> +				void **this_cache) const
>  {
> -  const struct tramp_frame *tramp = self->unwind_data->tramp_frame;
> +  const struct tramp_frame *tramp = unwind_data ()->tramp_frame;
>    CORE_ADDR pc = get_frame_pc (this_frame);
>    CORE_ADDR func;
>    struct tramp_frame_cache *tramp_cache;
> @@ -161,16 +191,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 d3f1af75417..59a29307b14 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 25d07c59376..4660979928c 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 8a29a33042f..a5243dc8715 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,20 +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::sniff (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);
>  
>    CORE_ADDR pc = get_frame_pc (this_frame);
> @@ -1376,7 +1373,7 @@ cygwin_sigwrapper_frame_sniffer (const struct frame_unwind *self_,
>  		      paddress (gdbarch, end));
>  
>    int tlsoffset;
> -  cygwin_sigwrapper_frame_analyze (gdbarch, start, end, self->patterns_list,
> +  cygwin_sigwrapper_frame_analyze (gdbarch, start, end, patterns_list,
>  				   &tlsoffset);
>    if (tlsoffset == 0)
>      return 0;
> @@ -1396,13 +1393,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..84206fb0c89 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 sniff (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 e01da4ecab4..e7f4e6fe600 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 d12dfdc7347..c87940c1cfc 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 66a12cd3be7..c75b6ff3160 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.47.0


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

* Re: [PATCH v8 1/5] gdb: make gdbarch store a vector of frame unwinders
  2025-01-14 14:28   ` Andrew Burgess
@ 2025-01-14 20:34     ` Guinevere Larsen
  0 siblings, 0 replies; 24+ messages in thread
From: Guinevere Larsen @ 2025-01-14 20:34 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Thiago Jung Bauermann

On 1/14/25 11:28 AM, Andrew Burgess wrote:
> Guinevere Larsen <guinevere@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 destructors.
>>
>> Seeing as a future patch of this series wants to refactor the
>> frame_unwind struct to use inheritance, and we'd like to not restrict
>> the future derived classes on what destructors are allowed. In
>> preparation for that change, this commit changes the registry in gdbarch
>> to instead store an std::vector, which doesn't require using an obstack
>> and doesn't rely on a linked list.
>>
>> There should be no user-visible changes.
>>
>> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
>> ---
>>   gdb/frame-unwind.c | 107 +++++++++++++++------------------------------
>>   1 file changed, 36 insertions(+), 71 deletions(-)
>>
>> diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
>> index 352779fcdcc..e61f6244913 100644
>> --- a/gdb/frame-unwind.c
>> +++ b/gdb/frame-unwind.c
>> @@ -31,61 +31,42 @@
>>   #include "cli/cli-cmds.h"
>>   #include "inferior.h"
>>   
>> -struct frame_unwind_table_entry
>> +/* Default sniffers, that must always be the first in the unwinder list,
>> +   no matter the architecture.  */
>> +static constexpr auto standard_unwinders =
> I'm not a huge fan of `auto` when the type is well known.  My thinking
> is write once, read many times.  So I'd rather have the type information
> available when I read the code.  For me:
>
>    static constexpr std::initializer_list<const frame_unwind *> standard_unwinders =
>
> tells me what's going on...
>
>>   {
>> -  const struct frame_unwind *unwinder;
>> -  struct frame_unwind_table_entry *next;
>> +  &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
>> +     before the tailcall sniffer, resulting in confusion.  This is
>> +     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.  */
>> +  &dwarf2_tailcall_frame_unwind,
>> +  &inline_frame_unwind,
>>   };
>>   
>> -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 constexpr int prepend_unwinder_index = standard_unwinders.size ();
>>   
>> -static const registry<gdbarch>::key<struct frame_unwind_table>
>> +static const registry<gdbarch>::key<std::vector<const frame_unwind *>>
>>        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 *
>> +/* Retrieve 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)
> Given you're changing this code anyway, how about returning a reference
> rather than a pointer, i.e.:
>
>    static std::vector<const frame_unwind *> &
>    get_frame_unwind_table (struct gdbarch *gdbarch)
>    { ... }
>
> The users of get_frame_unwind_table will need to be updated to match.
>
>>   {
>> -  struct frame_unwind_table *table = frame_unwind_data.get (gdbarch);
>> +  std::vector<const frame_unwind *> *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;
>> +  table = new std::vector<const frame_unwind *>;
>> +  table->insert (table->begin (), standard_unwinders.begin (),
>> +		 standard_unwinders.end ());
>>   
>> -  struct obstack *obstack = gdbarch_obstack (gdbarch);
>> -  link = add_unwinder (obstack, &dummy_frame_unwind, link);
>> -  /* 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
>> -     before the tailcall sniffer, resulting in confusion.  This is
>> -     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);
>> -
>> -  /* The insertion point for OSABI sniffers.  */
>> -  table->osabi_head = link;
>>     frame_unwind_data.set (gdbarch, table);
>>   
>>     return table;
>> @@ -95,27 +76,16 @@ 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);
>> +
>> +  table->insert (table->begin () + prepend_unwinder_index, unwinder);
>>   }
>>   
>>   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;
>> -
>> -  /* 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;
>> +  get_frame_unwind_table (gdbarch)->push_back (unwinder);
>>   }
>>   
>>   /* Call SNIFFER from UNWINDER.  If it succeeded set UNWINDER for
>> @@ -188,9 +158,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 +172,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)
> I think you can use:
>
>    for (const auto &unwinder : *table)
>
> here.  The 'const' is just good style as you're not going to modify it.
> The '&' is not really necessary as the type is actually a pointer.
> Better still might be:
>
>    for (const frame_unwind *unwinder : *table)
>
> But I don't mind the 'auto' here too much as the type is on the line
> immediately above.  But I do think, if you're sticking with 'auto' here
> then throwing the '&' in is a good idea.
I decided to go with the "auto" version, since table is always declared 
close to the usage, and that doesn't use auto.
>
>> +    if (frame_unwind_try_unwinder (this_frame, this_cache, unwinder))
>>         return;
>>   
>>     internal_error (_("frame_unwind_find_by_frame failed"));
>> @@ -347,7 +316,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,15 +324,11 @@ 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 (auto unwinder : *table)
> See the comments above.
>
> If you're happy making this changes I suggest then:
>
> Approved-By: Andrew Burgess <aburgess@redhat.com>
Thanks for the review, I went along with the changes and added your 
approval!

-- 
Cheers,
Guinevere Larsen
She/Her/Hers

>
> Thanks,
> Andrew
>
>>       {
>> -      const char *name = entry->unwinder->name;
>> -      const char *type = frame_type_str (entry->unwinder->type);
>> -
>>         ui_out_emit_list tuple_emitter (uiout, nullptr);
>> -      uiout->field_string ("name", name);
>> -      uiout->field_string ("type", type);
>> +      uiout->field_string ("name", unwinder->name);
>> +      uiout->field_string ("type", frame_type_str (unwinder->type));
>>         uiout->text ("\n");
>>       }
>>   }
>> -- 
>> 2.47.0


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

* Re: [PATCH v8 4/5] gdb: introduce ability to disable frame unwinders
  2024-12-10 19:51 ` [PATCH v8 4/5] gdb: introduce ability to disable frame unwinders Guinevere Larsen
@ 2025-01-16 12:06   ` Andrew Burgess
  2025-01-17 12:40     ` Guinevere Larsen
  2025-01-16 16:22   ` Andrew Burgess
  1 sibling, 1 reply; 24+ messages in thread
From: Andrew Burgess @ 2025-01-16 12:06 UTC (permalink / raw)
  To: Guinevere Larsen, gdb-patches
  Cc: Guinevere Larsen, Eli Zaretskii, Thiago Jung Bauermann

Guinevere Larsen <guinevere@redhat.com> writes:

> 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. This was requested back in 2003
> in PR backtrace/8434.
>
> 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.
>
> This commit is a more generic version of commit 3c3bb0580be0,
> and so, based on the final paragraph of the commit message:
>     gdb: Add switch to disable DWARF stack unwinders
> <...>
>     If in the future we find ourselves adding more switches to disable
>     different unwinders, then we should probably move to a more generic
>     solution, and remove this patch.
> this patch also reverts 3c3bb0580be0
>
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=8434
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> ---
>  gdb/NEWS                                      |  17 ++
>  gdb/doc/gdb.texinfo                           |  49 ++--
>  gdb/dwarf2/frame-tailcall.c                   |   3 -
>  gdb/dwarf2/frame.c                            |  30 ---
>  gdb/dwarf2/frame.h                            |   6 -
>  gdb/frame-unwind.c                            | 237 +++++++++++++++++-
>  gdb/frame-unwind.h                            |   9 +
>  .../gdb.base/frame-info-consistent.exp        |   8 +-
>  gdb/testsuite/gdb.base/frame-unwind-disable.c |  22 ++
>  .../gdb.base/frame-unwind-disable.exp         | 137 ++++++++++
>  gdb/testsuite/gdb.base/maint.exp              |   4 -
>  11 files changed, 437 insertions(+), 85 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 245b355669a..ef3317c11cb 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -125,6 +125,13 @@ maintenance info blocks [ADDRESS]
>    are listed starting at the inner global block out to the most inner
>    block.
>  
> +maintenance frame-unwinder disable [-all | -name NAME | [-class] CLASS]
> +maintenance frame-unwinder enable [-all | -name NAME | [-class] CLASS]
> +  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.
> +
>  info missing-objfile-handlers
>    List all the registered missing-objfile handlers.
>  
> @@ -167,6 +174,16 @@ maintenance print remote-registers
>  mainenance info frame-unwinders
>    Add a CLASS column to the output.  This class is a somewhat arbitrary
>    grouping of unwinders, based on which area of GDB adds the unwinder.
> +  Also add an ENABLED column, that will show if the unwinder is enabled
> +  or not.
> +
> +maintenance set dwarf unwinders (on|off)
> +  This command has been removed because the same functionality can be
> +  achieved with maint frame-unwinder (enable|disable) DEBUGINFO.
> +
> +maintenance show dwarf unwinders
> +  This command has been removed since the functionality can be achieved
> +  by checking the last column of maint info frame-unwinders.
>  
>  show configuration
>    Now includes the version of GNU Readline library that GDB is using.
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 115c1f46b7f..10e6654a03b 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -42210,31 +42210,6 @@ On hosts without threading, or where worker threads have been disabled
>  at runtime, this setting has no effect, as DWARF reading is always
>  done on the main thread, and is therefore always synchronous.
>  
> -@kindex maint set dwarf unwinders
> -@kindex maint show dwarf unwinders
> -@item maint set dwarf unwinders
> -@itemx maint show dwarf unwinders
> -Control use of the DWARF frame unwinders.
> -
> -@cindex DWARF frame unwinders
> -Many targets that support DWARF debugging use @value{GDBN}'s DWARF
> -frame unwinders to build the backtrace.  Many of these targets will
> -also have a second mechanism for building the backtrace for use in
> -cases where DWARF information is not available, this second mechanism
> -is often an analysis of a function's prologue.
> -
> -In order to extend testing coverage of the second level stack
> -unwinding mechanisms it is helpful to be able to disable the DWARF
> -stack unwinders, this can be done with this switch.
> -
> -In normal use of @value{GDBN} disabling the DWARF unwinders is not
> -advisable, there are cases that are better handled through DWARF than
> -prologue analysis, and the debug experience is likely to be better
> -with the DWARF frame unwinders enabled.
> -
> -If DWARF frame unwinders are not supported for a particular target
> -architecture, then enabling this flag does not cause them to be used.
> -
>  @kindex maint info frame-unwinders
>  @item maint info frame-unwinders
>  List the frame unwinders currently in effect, starting with the highest
> @@ -42252,6 +42227,30 @@ Unwinders installed by debug information readers.
>  Unwinders installed by the architecture specific code.
>  @end table
>  
> +@kindex maint frame-unwinder disable
> +@kindex maint frame-unwinder enable
> +@item maint frame-unwinder disable [@code{-all} | @code{-name} @var{name} | [@code{-class}] @var{class}]
> +@item maint frame-unwinder enable [@code{-all} | @code{-name} @var{name} | [@code{-class}] @var{class}]
> +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 each of the invocations are as follows:
> +
> +@table @samp
> +@item @code{-all}
> +Disable or enable all unwinders.
> +@item @code{-name} @var{name}
> +@var{name} is the exact name of the unwinder to be disabled or enabled.
> +@item [@code{-class}] @var{class}
> +@var{class} is the class of frame unwinders to be disabled or enabled.
> +The class may include the prefix @code{FRAME_UNWINDER_}, but it is not
> +required.
> +@end table
> +
> +@var{name} and @var{class} are always case insensitive.  If no option
> +starting wth @code{-} is given, @value{GDBN} assumes @code{-class}.
> +
>  @kindex maint set worker-threads
>  @kindex maint show worker-threads
>  @item maint set worker-threads
> diff --git a/gdb/dwarf2/frame-tailcall.c b/gdb/dwarf2/frame-tailcall.c
> index 54813d00b03..2d7ab740f62 100644
> --- a/gdb/dwarf2/frame-tailcall.c
> +++ b/gdb/dwarf2/frame-tailcall.c
> @@ -321,9 +321,6 @@ tailcall_frame_sniffer (const struct frame_unwind *self,
>    int next_levels;
>    struct tailcall_cache *cache;
>  
> -  if (!dwarf2_frame_unwinders_enabled_p)
> -    return 0;
> -
>    /* Inner tail call element does not make sense for a sentinel frame.  */
>    next_frame = get_next_frame (this_frame);
>    if (next_frame == NULL)
> diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
> index 85e1d59bc09..e0e8eb5dbec 100644
> --- a/gdb/dwarf2/frame.c
> +++ b/gdb/dwarf2/frame.c
> @@ -183,9 +183,6 @@ static ULONGEST read_encoded_value (struct comp_unit *unit, gdb_byte encoding,
>  				    unrelocated_addr func_base);
>  \f
>  
> -/* See dwarf2/frame.h.  */
> -bool dwarf2_frame_unwinders_enabled_p = true;
> -
>  /* Store the length the expression for the CFA in the `cfa_reg' field,
>     which is unused in that case.  */
>  #define cfa_exp_len cfa_reg
> @@ -1306,9 +1303,6 @@ static int
>  dwarf2_frame_sniffer (const struct frame_unwind *self,
>  		      const frame_info_ptr &this_frame, void **this_cache)
>  {
> -  if (!dwarf2_frame_unwinders_enabled_p)
> -    return 0;
> -
>    /* Grab an address that is guaranteed to reside somewhere within the
>       function.  get_frame_pc(), with a no-return next function, can
>       end up returning something past the end of this function's body.
> @@ -2253,34 +2247,10 @@ dwarf2_build_frame_info (struct objfile *objfile)
>    set_comp_unit (objfile, unit.release ());
>  }
>  
> -/* Handle 'maintenance show dwarf unwinders'.  */
> -
> -static void
> -show_dwarf_unwinders_enabled_p (struct ui_file *file, int from_tty,
> -				struct cmd_list_element *c,
> -				const char *value)
> -{
> -  gdb_printf (file,
> -	      _("The DWARF stack unwinders are currently %s.\n"),
> -	      value);
> -}
> -
>  void _initialize_dwarf2_frame ();
>  void
>  _initialize_dwarf2_frame ()
>  {
> -  add_setshow_boolean_cmd ("unwinders", class_obscure,
> -			   &dwarf2_frame_unwinders_enabled_p , _("\
> -Set whether the DWARF stack frame unwinders are used."), _("\
> -Show whether the DWARF stack frame unwinders are used."), _("\
> -When enabled the DWARF stack frame unwinders can be used for architectures\n\
> -that support the DWARF unwinders.  Enabling the DWARF unwinders for an\n\
> -architecture that doesn't support them will have no effect."),
> -			   NULL,
> -			   show_dwarf_unwinders_enabled_p,
> -			   &set_dwarf_cmdlist,
> -			   &show_dwarf_cmdlist);
> -
>  #if GDB_SELF_TEST
>    selftests::register_test_foreach_arch ("execute_cfa_program",
>  					 selftests::execute_cfa_program_test);
> diff --git a/gdb/dwarf2/frame.h b/gdb/dwarf2/frame.h
> index 2167310fbdf..fe2d669a983 100644
> --- a/gdb/dwarf2/frame.h
> +++ b/gdb/dwarf2/frame.h
> @@ -198,12 +198,6 @@ struct dwarf2_frame_state
>    bool armcc_cfa_offsets_reversed = false;
>  };
>  
> -/* When this is true the DWARF frame unwinders can be used if they are
> -   registered with the gdbarch.  Not all architectures can or do use the
> -   DWARF unwinders.  Setting this to true on a target that does not
> -   otherwise support the DWARF unwinders has no effect.  */
> -extern bool dwarf2_frame_unwinders_enabled_p;
> -
>  /* Set the architecture-specific register state initialization
>     function for GDBARCH to INIT_REG.  */
>  
> diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
> index 3ab6af1c4da..968bb846402 100644
> --- a/gdb/frame-unwind.c
> +++ b/gdb/frame-unwind.c
> @@ -29,6 +29,7 @@
>  #include "gdbarch.h"
>  #include "dwarf2/frame-tailcall.h"
>  #include "cli/cli-cmds.h"
> +#include "cli/cli-option.h"
>  #include "inferior.h"
>  
>  /* Conversion list between the enum for frame_unwind_class and
> @@ -89,6 +90,20 @@ frame_unwinder_class_str (frame_unwind_class uclass)
>    return unwind_class_conversion[uclass];
>  }
>  
> +/* Case insensitive search for a frame_unwind_class based on the given
> +   string.  */
> +static enum frame_unwind_class
> +str_to_frame_unwind_class (const char *class_str)
> +{
> +  for (int i = 0; i < UNWIND_CLASS_NUMBER; i++)
> +    {
> +      if (strcasecmp (unwind_class_conversion[i], class_str) == 0)
> +	return (frame_unwind_class) i;
> +    }
> +
> +  error (_("Unknown frame unwind class: %s"), class_str);
> +}
> +
>  void
>  frame_unwind_prepend_unwinder (struct gdbarch *gdbarch,
>  				const struct frame_unwind *unwinder)
> @@ -175,27 +190,46 @@ 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));
>  
> -  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 internal_error at the end of this function.  */
> +  bool seen_disabled_unwinder = false;
> +  /* Lambda to factor out the logic of checking if an unwinder is enabled,
> +     testing it and otherwise recording if we saw a disable unwinder.  */
> +  auto test_unwinder = [&] (const struct frame_unwind *unwinder)
> +    {
> +      if (unwinder == nullptr)
> +	return false;
> +
> +      if (!unwinder->enabled ())
> +	{
> +	  seen_disabled_unwinder = true;
> +	  return false;
> +	}
> +
> +      return frame_unwind_try_unwinder (this_frame,
> +					this_cache,
> +					unwinder);
> +    };
>  
> -  unwinder_from_target = target_get_unwinder ();
> -  if (unwinder_from_target != NULL
> -      && frame_unwind_try_unwinder (this_frame, this_cache,
> -				   unwinder_from_target))
> +  if (test_unwinder (target_get_unwinder ()))
>      return;
>  
> -  unwinder_from_target = target_get_tailcall_unwinder ();
> -  if (unwinder_from_target != NULL
> -      && frame_unwind_try_unwinder (this_frame, this_cache,
> -				   unwinder_from_target))
> +  if (test_unwinder (target_get_tailcall_unwinder ()))
>      return;
>  
>    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 (test_unwinder (unwinder))
> +	return;
> +    }
>  
> -  internal_error (_("frame_unwind_find_by_frame failed"));
> +  if (seen_disabled_unwinder)
> +    error (_("Required frame unwinder may have been disabled"
> +	     ", see 'maint info frame-unwinders'"));
> +  else
> +    internal_error (_("frame_unwind_find_by_frame failed"));
>  }
>  
>  /* A default frame sniffer which always accepts the frame.  Used by
> @@ -394,10 +428,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 (9, ui_left, "class", "Class");
> +  uiout->table_header (8, ui_left, "enabled", "Enabled");
>    uiout->table_body ();
>  
>    for (auto unwinder : *table)
> @@ -407,10 +442,145 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
>        uiout->field_string ("type", frame_type_str (unwinder->type ()));
>        uiout->field_string ("class", frame_unwinder_class_str (
>  					unwinder->unwinder_class ()));
> +      uiout->field_string ("enabled", unwinder->enabled () ? "Y" : "N");
>        uiout->text ("\n");
>      }
>  }
>  
> +/* Options for disabling frame unwinders.  */
> +struct maint_frame_unwind_options
> +{
> +  std::string unwinder_name;
> +  const char *unwinder_class = nullptr;
> +  bool all = false;
> +};
> +
> +static const gdb::option::option_def maint_frame_unwind_opt_defs[] = {
> +
> +  gdb::option::flag_option_def<maint_frame_unwind_options> {
> +    "all",
> +    [] (maint_frame_unwind_options *opt) { return &opt->all; },
> +    nullptr,
> +    N_("Change the state of all unwinders")
> +  },
> +  gdb::option::string_option_def<maint_frame_unwind_options> {
> +    "name",
> +    [] (maint_frame_unwind_options *opt) { return &opt->unwinder_name; },
> +    nullptr,
> +    N_("The name of the unwinder to have its state changed")
> +  },
> +  gdb::option::enum_option_def<maint_frame_unwind_options> {
> +    "class",
> +    unwind_class_conversion,
> +    [] (maint_frame_unwind_options *opt) { return &opt->unwinder_class; },
> +    nullptr,
> +    N_("The class of unwinders to have their states changed")
> +  }
> +};
> +
> +static inline gdb::option::option_def_group
> +make_frame_unwind_enable_disable_options (maint_frame_unwind_options *opts)
> +{
> +  return {{maint_frame_unwind_opt_defs}, opts};
> +}
> +
> +/* 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)
> +{
> +  if (args == nullptr)
> +    {
> +      if (enable)
> +	error (_("Specify which frame unwinder(s) should be enabled"));
> +      else
> +	error (_("Specify which frame unwinder(s) should be disabled"));
> +    }
> +
> +  struct gdbarch* gdbarch = current_inferior ()->arch ();
> +  std::vector<const frame_unwind *> *unwinder_list
> +    = get_frame_unwind_table (gdbarch);
> +
> +  maint_frame_unwind_options opts;
> +  gdb::option::process_options
> +    (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR,
> +     make_frame_unwind_enable_disable_options (&opts));
> +
> +  if ((opts.all && !opts.unwinder_name.empty ())
> +      || (opts.all && opts.unwinder_class != nullptr)
> +      || (!opts.unwinder_name.empty () && opts.unwinder_class != nullptr))
> +    error (_("Options are mutually exclusive"));
> +
> +  /* First see if the user wants to change all unwinders.  */
> +  if (opts.all)
> +    {
> +      for (const frame_unwind *u : *unwinder_list)
> +	u->set_enabled (enable);
> +
> +      reinit_frame_cache ();
> +      return;
> +    }
> +
> +  /* If user entered a specific unwinder name, handle it here.  If the
> +     unwinder is already at the expected state, error out.  */
> +  if (!opts.unwinder_name.empty ())
> +    {
> +      bool did_something = false;
> +      for (const frame_unwind *unwinder : *unwinder_list)
> +	{
> +	  if (strcasecmp (unwinder->name (),
> +			  opts.unwinder_name.c_str ()) == 0)
> +	    {
> +	      if (unwinder->enabled () == enable)
> +		{
> +		  if (unwinder->enabled ())
> +		    error (_("unwinder %s is already enabled"),
> +			     unwinder->name ());
> +		  else
> +		    error (_("unwinder %s is already disabled"),
> +			     unwinder->name ());
> +		}
> +	      unwinder->set_enabled (enable);
> +
> +	      did_something = true;
> +	      break;
> +	    }
> +	}
> +      if (!did_something)
> +	error (_("couldn't find unwinder named %s"),
> +	       opts.unwinder_name.c_str ());
> +    }
> +  else
> +    {
> +      if (opts.unwinder_class == nullptr)
> +	opts.unwinder_class = args;
> +      enum frame_unwind_class dclass = str_to_frame_unwind_class
> +	(opts.unwinder_class);
> +      for (auto unwinder: *unwinder_list)

I think you should stick with the form you use earlier:

  (const frame_unwind *unwinder : *unwinder_list)

But if you really want to stick with auto, then I think:

 (const auto &unwinder : *unwinder_list)

would be OK.


> +	{
> +	  if (unwinder->unwinder_class () == dclass)
> +	    unwinder->set_enabled (enable);
> +	}
> +    }
> +
> +  reinit_frame_cache ();
> +}
> +
> +/* Implement "maint frame-unwinder 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-unwinder enable" 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 ()
> @@ -423,4 +593,45 @@ _initialize_frame_unwind ()
>  List the frame unwinders currently in effect.\n\
>  Unwinders are listed 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 [-all | -name NAME | [-class] CLASS]\n\
> +\n\
> +These are the meanings of the options:\n\
> +\t-all    - All available unwinders will be disabled\n\
> +\t-name   - NAME is the exact name of the frame unwinder to be disabled\n\
> +\t-class  - CLASS is the class of unwinders to be disabled. Valid classes are:\n\
> +\t\tGDB       - Unwinders added by GDB core;\n\
> +\t\tEXTENSION - Unwinders added by extension languages;\n\
> +\t\tDEBUGINFO - Unwinders that handle debug information;\n\
> +\t\tARCH      - Unwinders that use architecture-specific information;\n\
> +\n\
> +UNWINDER and NAME are 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 [-all | -name NAME | [-class] CLASS]\n\
> +\n\
> +These are the meanings of the options:\n\
> +\t-all    - All available unwinders will be enabled\n\
> +\t-name   - NAME is the exact name of the frame unwinder to be enabled\n\
> +\t-class  - CLASS is the class of unwinders to be enabled. Valid classes are:\n\
> +\t\tGDB       - Unwinders added by GDB core;\n\
> +\t\tEXTENSION - Unwinders added by extension languages;\n\
> +\t\tDEBUGINFO - Unwinders that handle debug information;\n\
> +\t\tARCH      - Unwinders that use architecture-specific information;\n\
> +\n\
> +UNWINDER and NAME are case insensitive."),
> +	   &maint_frame_unwinder);
>  }

At the bottom of this email you'll find a small patch which applies on
top of this and adds some basic command completion.  It doesn't complete
the NAMEs, I'm not sure if there's a good way to do that (yet).  It also
doesn't switch the help text to use the %OPTIONS% mechanism, I tried
that, but I think there may be some bugs with the formatting (I'll look
into it), so I've left that off.

You don't have to, we can add completion later, but I do think it would
be good to merge the patch below with this one.  But it is up to you.
Take a look and see what you think.

> diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
> index 13f18618d24..adf8b47982c 100644
> --- a/gdb/frame-unwind.h
> +++ b/gdb/frame-unwind.h
> @@ -192,6 +192,12 @@ class frame_unwind
>    const frame_data *unwind_data () const
>    { return m_unwind_data; }
>  
> +  bool enabled () const
> +  { return m_enabled; }
> +
> +  void set_enabled (bool state) const
> +  { m_enabled = state; }
> +
>    /* Default stop_reason implementation.  It reports NO_REASON, unless the
>       frame is the outermost.  */
>  
> @@ -245,6 +251,9 @@ class frame_unwind
>    frame_unwind_class m_unwinder_class;
>  
>    const frame_data *m_unwind_data;
> +
> +  /* Whether this unwinder can be used when sniffing.  */
> +  mutable bool m_enabled = true;
>  };
>  
>  /* This is a legacy version of the frame unwinder.  The original struct
> diff --git a/gdb/testsuite/gdb.base/frame-info-consistent.exp b/gdb/testsuite/gdb.base/frame-info-consistent.exp
> index fe0cfad95bc..4f483111a91 100644
> --- a/gdb/testsuite/gdb.base/frame-info-consistent.exp
> +++ b/gdb/testsuite/gdb.base/frame-info-consistent.exp
> @@ -95,11 +95,11 @@ proc compare_frames {frames} {
>      }
>  }
>  
> -proc test {dwarf_unwinder} {
> +proc test {enable} {
>  
>      clean_restart $::binfile
>  
> -    gdb_test_no_output "maint set dwarf unwinder $dwarf_unwinder"
> +    gdb_test_no_output "maint frame-unwinder $enable DEBUGINFO"
>  
>      if {![runto_main]} {
>  	return 0
> @@ -134,6 +134,6 @@ proc test {dwarf_unwinder} {
>      }
>  }
>  
> -foreach_with_prefix dwarf {"off" "on"} {
> -    test $dwarf
> +foreach_with_prefix action {"disable" "enable"} {
> +    test $action
>  }
> 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..bbcfb01316e
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/frame-unwind-disable.c
> @@ -0,0 +1,22 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2024 Free Software Foundation, Inc.

Remember to update the copyright date when rebasing.

> +
> +   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..f4cad9a47fd
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/frame-unwind-disable.exp
> @@ -0,0 +1,137 @@
> +# Copyright 2024 Free Software Foundation, Inc.

And again with the date.

> +
> +# 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

You can drop '.c' here, it's the default.

> +
> +# 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 ""} } {
> +    set should_pass false
> +    set command "maint info frame-unwinders"
> +    if {${testname} == ""} {
> +	set testname "checking state ${state} for ${unwinder_name}"
> +    }
> +    gdb_test_multiple "${command}" "${testname}" -lbl {
> +	-re "${unwinder_name}\\s+\\w+\\s+\\w+\\s+${state}\\s+(?=\r\n)" {
> +	    set should_pass true
> +	    exp_continue
> +	}
> +	-re "${command}" {
> +	    exp_continue
> +	}
> +	-re "\\w+\\s+\\w+\\s+\\w+\\s+\\w+\\s+(?=\r\n)" {
> +	    exp_continue
> +	}
> +	-re -wrap "" {
> +	    gdb_assert ${should_pass} "${gdb_test_name}"

I don't think the quotes around "${gdb_test_name}" are needed here?  In
fact, there seem to be a few places where you place quotes around
expansion of a single variable (e.g. "${command}" on the
gdb_test_multiple line), I don't think any of these are needed.

With these changes (and with or without the completion patch):

Approved-By: Andrew Burgess <aburgess@redhat.com>

Thanks,
Andrew

> +	}
> +    }
> +}
> +
> +# Check if all unwinders of class UNWINDER_CLASS are in the state STATE.
> +# STATE can be either Y or N.
> +# UNWINDER_CLASS can be one of: GDB, ARCH, EXTENSION, DEBUGINFO.  It can
> +# also be \\w+ if checking all unwinders.
> +proc check_unwinder_class { unwinder_class state {testname ""} } {
> +    set command "maint info frame-unwinders"
> +    set should_pass true
> +    if {$testname == ""} {
> +	set testname "checking if ${unwinder_class} state is ${state}"
> +    }
> +    gdb_test_multiple "${command}" "${testname}" -lbl {
> +	-re "^\[^\r\n\]+\\s+\\w+\\s+${unwinder_class}\\s+\(\[YN\]\)\\s+(?=\r\n)" {
> +	    # The unwinder name may have multiple words, so we need to use the
> +	    # more generic [^\r\n] pattern to match the unwinders.
> +	    set cur_state $expect_out(1,string)
> +	    if {$cur_state == $state} {
> +		set should_pass false
> +	    }
> +	    exp_continue
> +	}
> +	-re "${command}" {
> +	    exp_continue
> +	}
> +	-re -wrap "" {
> +	    gdb_assert ${should_pass} "${gdb_test_name}"
> +	}
> +    }
> +}
> +
> +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 "\\w+" "Y" \
> +    "all unwinders enabled before any changes"
> +gdb_test_no_output "maint frame-unwinder disable -all"
> +check_unwinder_class "\\w+" "N" \
> +    "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" \
> +    ".*Required frame unwinder may have been disabled, see 'maint info frame-unwinders'.*" \
> +    "no suitable unwinders should be found"
> +
> +# Reenable all unwinders.
> +gdb_test_no_output "maint frame-unwinder enable -all"
> +check_unwinder_class "\\w+" "Y" \
> +    "all unwinders should be re-enabled"
> +
> +# Check that we are able to get backtraces once again.
> +gdb_test "backtrace" ".0\\s+main .. at.*" \
> +    "we can get usable backtraces again"
> +
> +# 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" "dummy should continue 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 "${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" "dummy should continue 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 "${class}" "Y"
> +}
> diff --git a/gdb/testsuite/gdb.base/maint.exp b/gdb/testsuite/gdb.base/maint.exp
> index 2c58ffa36c5..ae7ad0787e6 100644
> --- a/gdb/testsuite/gdb.base/maint.exp
> +++ b/gdb/testsuite/gdb.base/maint.exp
> @@ -454,10 +454,6 @@ gdb_test_no_output "maint info line-table xxx.c" \
>  
>  set timeout $oldtimeout
>  
> -# Just check that the DWARF unwinders control flag is visible.
> -gdb_test "maint show dwarf unwinders" \
> -    "The DWARF stack unwinders are currently (on|off)\\."
> -
>  #============test help on maint commands
>  
>  test_prefix_command_help {"maint info" "maintenance info"} {
> -- 
> 2.47.0

---

diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
index 1417651108f..f3019b7ec18 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -40,6 +40,7 @@ static const char * unwind_class_conversion[] =
   "EXTENSION",
   "DEBUGINFO",
   "ARCH",
+  nullptr
 };
 
 /* Default sniffers, that must always be the first in the unwinder list,
@@ -579,6 +580,29 @@ enable_disable_frame_unwinders (const char *args, int from_tty, bool enable)
   reinit_frame_cache ();
 }
 
+/* Completer for the "maint frame-unwinder enable|disable" commands.  */
+
+static void
+enable_disable_frame_unwinders_completer (struct cmd_list_element *ignore,
+					  completion_tracker &tracker,
+					  const char *text,
+					  const char */*word*/)
+{
+  maint_frame_unwind_options opts;
+  const auto group = make_frame_unwind_enable_disable_options (&opts);
+
+  const char *start = text;
+  if (gdb::option::complete_options
+      (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
+    return;
+
+  /* Only complete a class name as a stand-alone operand when no options
+     are given.  */
+  if (start == text)
+    complete_on_enum (tracker, unwind_class_conversion, text, text);
+  return;
+}
+
 /* Implement "maint frame-unwinder disable" command.  */
 static void
 maintenance_disable_frame_unwinders (const char *args, int from_tty)
@@ -613,8 +637,9 @@ Unwinders are listed starting with the highest priority."),
 			_("Commands handling frame unwinders."),
 			&maint_frame_unwinder, 0, &maintenancelist);
 
-  add_cmd ("disable", class_maintenance, maintenance_disable_frame_unwinders,
-	   _("\
+  cmd_list_element *c
+    = add_cmd ("disable", class_maintenance, maintenance_disable_frame_unwinders,
+	       _("\
 Disable one or more frame unwinder(s).\n\
 Usage: maint frame-unwinder disable [-all | -name NAME | [-class] CLASS]\n\
 \n\
@@ -628,10 +653,13 @@ These are the meanings of the options:\n\
 \t\tARCH      - Unwinders that use architecture-specific information;\n\
 \n\
 UNWINDER and NAME are case insensitive."),
-	   &maint_frame_unwinder);
+	       &maint_frame_unwinder);
+  set_cmd_completer_handle_brkchars (c,
+				     enable_disable_frame_unwinders_completer);
 
-  add_cmd ("enable", class_maintenance, maintenance_enable_frame_unwinders,
-	   _("\
+  c =
+    add_cmd ("enable", class_maintenance, maintenance_enable_frame_unwinders,
+	     _("\
 Enable one or more frame unwinder(s).\n\
 Usage: maint frame-unwinder enable [-all | -name NAME | [-class] CLASS]\n\
 \n\
@@ -645,5 +673,7 @@ These are the meanings of the options:\n\
 \t\tARCH      - Unwinders that use architecture-specific information;\n\
 \n\
 UNWINDER and NAME are case insensitive."),
-	   &maint_frame_unwinder);
+	     &maint_frame_unwinder);
+  set_cmd_completer_handle_brkchars (c,
+				     enable_disable_frame_unwinders_completer);
 }


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

* Re: [PATCH v8 5/5] gdb/testsuite: Test for a backtrace through object without debuginfo
  2024-12-10 19:51 ` [PATCH v8 5/5] gdb/testsuite: Test for a backtrace through object without debuginfo Guinevere Larsen
@ 2025-01-16 14:37   ` Andrew Burgess
  2025-01-16 18:42     ` Guinevere Larsen
  2025-01-18  8:07     ` Tom de Vries
  0 siblings, 2 replies; 24+ messages in thread
From: Andrew Burgess @ 2025-01-16 14:37 UTC (permalink / raw)
  To: Guinevere Larsen, gdb-patches
  Cc: Guinevere Larsen, Jan Kratochvil, Thiago Jung Bauermann

Guinevere Larsen <guinevere@redhat.com> writes:

> Fedora has been carrying this test since back in the Project Archer
> days. A change back then caused GDB to stop being able to backtrace when
> only some of the object files had debug information. Even though the
> changed code never seems to have made its way into the main GDB project,
> I think it makes sense to bring the test along to ensure something like
> this doesn't pass unnoticed.
>
> Co-Authored-By: Jan Kratochvil <jan@jankratochvil.net>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> ---
>  .../backtrace-through-cu-nodebug-caller.c     | 28 ++++++
>  .../backtrace-through-cu-nodebug-main.c       | 32 +++++++
>  .../gdb.base/backtrace-through-cu-nodebug.exp | 95 +++++++++++++++++++
>  3 files changed, 155 insertions(+)
>  create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
>  create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
>  create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
>
> diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
> new file mode 100644
> index 00000000000..3a63d72a468
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
> @@ -0,0 +1,28 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2005-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/>.  */
> +
> +typedef int (*callback_t) (void);
> +
> +int
> +caller (callback_t callback)
> +{
> +  /* Ensure some frame content to push away the return address.  */
> +  volatile const long one = 1;
> +
> +  /* Modify the return value to prevent any tail-call optimization.  */
> +  return (*callback) () - one;
> +}
> diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
> new file mode 100644
> index 00000000000..3e7ac57a166
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
> @@ -0,0 +1,32 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2005-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/>.  */
> +
> +typedef int (*callback_t) (void);
> +
> +extern int caller (callback_t callback);
> +
> +int
> +callback (void)
> +{
> +  return 1;
> +}
> +
> +int
> +main (void)
> +{
> +  return caller (callback);
> +}
> diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
> new file mode 100644
> index 00000000000..c0940b406a8
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
> @@ -0,0 +1,95 @@
> +# Copyright 2010-2024 Free Software Foundation, Inc.

Remember to update the copyright year throughout.

> +
> +# 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 that GDB can generate accurate backtraces even if some of the stack
> +# trace goes through a function with no debug information.
> +
> +standard_testfile -caller.c -main.c
> +set objmainfile ${testfile}-main.o
> +set objcallerfile ${testfile}-caller.o
> +
> +# recompile the inferior with or without CFI information, then run the
> +# inferior until the point where the important test starts
> +# returns TRUE on an ERROR.

Needs converting to two sentences ('.' after 'starts').  Plus capital
'R' for each sentence.

I wonder if it would be better to return TRUE on success, and FALSE on
error, because .... see below ...

> +proc prepare_test {has_cfi} {
> +    global srcdir subdir srcfile srcfile2 objmainfile objcallerfile binfile
> +    if {$has_cfi} {
> +	set extension "cfi"
> +	if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
> +	     "${srcdir}/${subdir}/${objcallerfile}" \
> +	     object [list {additional_flags=-fomit-frame-pointer \
> +		 -funwind-tables -fasynchronous-unwind-tables}]] != "" } {
> +	    untested "couldn't compile with cfi"
> +	    return true
> +      }
> +    } else {
> +	set extension "no-cfi"
> +	if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
> +	     "${srcdir}/${subdir}/${objcallerfile}" \
> +	     object [list {additional_flags=-fomit-frame-pointer \
> +		 -fno-unwind-tables \
> +		 -fno-asynchronous-unwind-tables}]] != "" } {
> +	    untested "couldn't compile without cfi"
> +	    return true
> +      }
> +    }
> +    if {[gdb_compile [list "${srcdir}/${subdir}/${objmainfile}" \
> +	    "${srcdir}/${subdir}/${objcallerfile}"] \
> +	    "${binfile}-${extension}" binfile {}] != ""} {
> +	untested "couldn't link object files"
> +	return true
> +    }
> +
> +    clean_restart "$binfile-${extension}"
> +
> +    with_test_prefix "${extension}" {
> +
> +	if ![runto callback] then {
> +	   fail "has_cfi=$has_cfi: Can't run to callback"
> +	   return true
> +	}
> +	gdb_test_no_output "maint frame-unwinder disable ARCH"
> +	return false
> +    }
> +}
> +
> +if {[gdb_compile "${srcdir}/${subdir}/${srcfile2}" \
> +	"${srcdir}/${subdir}/${objmainfile}" \
> +	object {debug}] != "" } {
> +    untested "couldn't compile main file"
> +    return
> +}
> +
> +if { [prepare_test false] } {
> +     untested ${testfile}.exp
> +} else {
> +    gdb_test "bt" "Required frame unwinder may have been disabled.*" \
> +	"verify unwind fail without CFI"
> +}

When something goes wrong with the prepare_test call we've already
emitted an 'untested' or 'fail' message, thus I think we can just write:

 if { [prepare_test false] } {
   ... perform the test ...
 }

Of course, this assumes that the true/false return value for
prepare_test have been switched.  Currently you'd write:

 if { ![prepare_test false] } {
   ... perform the test ...
 }

which just seems weird.

Also, the test here says "verify unwind fail without CFI".  And we _do_
check for the error message about disabled unwinders.  But if the `bt`
worked this test would still pass just fine.  We should probably be
checking that we only print frame #0.  Something like:

    gdb_test "bt" \
	[multi_line \
	     "\[^\r\n\]+Required frame unwinder may have been disabled, \[^\r\n\]+" \
	     "#0\\s+callback \\(\\) \[^\r\n\]+"] \
	"verify unwind fail without CFI"

should do the job I think.

> +
> +if { [prepare_test true] } {
> +     untested ${testfile}.exp
> +} else {

Same suggestion here about avoiding the extra 'untested' call.

> +    if { [istarget "arm*-*-*"] } {
> +	setup_kfail backtrace/31950 *-*-*
> +    }
> +    set text {[^\r\n]+}
> +    # #0  callback () at ...
> +    # #1  0x00000000004004e9 in caller ()
> +    # #2  0x00000000004004cd in main () at ...
> +    gdb_test "bt" \
> +	"#0 +callback $text\r\n#1 $text in caller $text\r\n#2 $text in main $text" \
> +	"verify unwinding works for CFI without DIEs"

The test name here seems weird.  Maybe: 'verify unwinding works for CUs
without CFI' would be better?

OK with these fixes.

Approved-By: Andrew Burgess <aburgess@redhat.com>

Thanks,
Andrew

> +}
> -- 
> 2.47.0


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

* Re: [PATCH v8 4/5] gdb: introduce ability to disable frame unwinders
  2024-12-10 19:51 ` [PATCH v8 4/5] gdb: introduce ability to disable frame unwinders Guinevere Larsen
  2025-01-16 12:06   ` Andrew Burgess
@ 2025-01-16 16:22   ` Andrew Burgess
  1 sibling, 0 replies; 24+ messages in thread
From: Andrew Burgess @ 2025-01-16 16:22 UTC (permalink / raw)
  To: Guinevere Larsen, gdb-patches
  Cc: Guinevere Larsen, Eli Zaretskii, Thiago Jung Bauermann

Guinevere Larsen <guinevere@redhat.com> writes:

> 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. This was requested back in 2003
> in PR backtrace/8434.
>
> 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.
>
> This commit is a more generic version of commit 3c3bb0580be0,
> and so, based on the final paragraph of the commit message:
>     gdb: Add switch to disable DWARF stack unwinders
> <...>
>     If in the future we find ourselves adding more switches to disable
>     different unwinders, then we should probably move to a more generic
>     solution, and remove this patch.
> this patch also reverts 3c3bb0580be0
>
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=8434
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> ---
>  gdb/NEWS                                      |  17 ++
>  gdb/doc/gdb.texinfo                           |  49 ++--
>  gdb/dwarf2/frame-tailcall.c                   |   3 -
>  gdb/dwarf2/frame.c                            |  30 ---
>  gdb/dwarf2/frame.h                            |   6 -
>  gdb/frame-unwind.c                            | 237 +++++++++++++++++-
>  gdb/frame-unwind.h                            |   9 +
>  .../gdb.base/frame-info-consistent.exp        |   8 +-
>  gdb/testsuite/gdb.base/frame-unwind-disable.c |  22 ++
>  .../gdb.base/frame-unwind-disable.exp         | 137 ++++++++++
>  gdb/testsuite/gdb.base/maint.exp              |   4 -
>  11 files changed, 437 insertions(+), 85 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 245b355669a..ef3317c11cb 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -125,6 +125,13 @@ maintenance info blocks [ADDRESS]
>    are listed starting at the inner global block out to the most inner
>    block.
>  
> +maintenance frame-unwinder disable [-all | -name NAME | [-class] CLASS]
> +maintenance frame-unwinder enable [-all | -name NAME | [-class] CLASS]
> +  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.
> +
>  info missing-objfile-handlers
>    List all the registered missing-objfile handlers.
>  
> @@ -167,6 +174,16 @@ maintenance print remote-registers
>  mainenance info frame-unwinders
>    Add a CLASS column to the output.  This class is a somewhat arbitrary
>    grouping of unwinders, based on which area of GDB adds the unwinder.
> +  Also add an ENABLED column, that will show if the unwinder is enabled
> +  or not.
> +
> +maintenance set dwarf unwinders (on|off)
> +  This command has been removed because the same functionality can be
> +  achieved with maint frame-unwinder (enable|disable) DEBUGINFO.
> +
> +maintenance show dwarf unwinders
> +  This command has been removed since the functionality can be achieved
> +  by checking the last column of maint info frame-unwinders.
>  
>  show configuration
>    Now includes the version of GNU Readline library that GDB is using.
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 115c1f46b7f..10e6654a03b 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -42210,31 +42210,6 @@ On hosts without threading, or where worker threads have been disabled
>  at runtime, this setting has no effect, as DWARF reading is always
>  done on the main thread, and is therefore always synchronous.
>  
> -@kindex maint set dwarf unwinders
> -@kindex maint show dwarf unwinders
> -@item maint set dwarf unwinders
> -@itemx maint show dwarf unwinders
> -Control use of the DWARF frame unwinders.
> -
> -@cindex DWARF frame unwinders
> -Many targets that support DWARF debugging use @value{GDBN}'s DWARF
> -frame unwinders to build the backtrace.  Many of these targets will
> -also have a second mechanism for building the backtrace for use in
> -cases where DWARF information is not available, this second mechanism
> -is often an analysis of a function's prologue.
> -
> -In order to extend testing coverage of the second level stack
> -unwinding mechanisms it is helpful to be able to disable the DWARF
> -stack unwinders, this can be done with this switch.
> -
> -In normal use of @value{GDBN} disabling the DWARF unwinders is not
> -advisable, there are cases that are better handled through DWARF than
> -prologue analysis, and the debug experience is likely to be better
> -with the DWARF frame unwinders enabled.
> -
> -If DWARF frame unwinders are not supported for a particular target
> -architecture, then enabling this flag does not cause them to be used.
> -
>  @kindex maint info frame-unwinders
>  @item maint info frame-unwinders
>  List the frame unwinders currently in effect, starting with the highest
> @@ -42252,6 +42227,30 @@ Unwinders installed by debug information readers.
>  Unwinders installed by the architecture specific code.
>  @end table
>  
> +@kindex maint frame-unwinder disable
> +@kindex maint frame-unwinder enable
> +@item maint frame-unwinder disable [@code{-all} | @code{-name} @var{name} | [@code{-class}] @var{class}]
> +@item maint frame-unwinder enable [@code{-all} | @code{-name} @var{name} | [@code{-class}] @var{class}]
> +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 each of the invocations are as follows:
> +
> +@table @samp
> +@item @code{-all}
> +Disable or enable all unwinders.
> +@item @code{-name} @var{name}
> +@var{name} is the exact name of the unwinder to be disabled or enabled.
> +@item [@code{-class}] @var{class}
> +@var{class} is the class of frame unwinders to be disabled or enabled.
> +The class may include the prefix @code{FRAME_UNWINDER_}, but it is not
> +required.
> +@end table
> +
> +@var{name} and @var{class} are always case insensitive.  If no option
> +starting wth @code{-} is given, @value{GDBN} assumes @code{-class}.
> +
>  @kindex maint set worker-threads
>  @kindex maint show worker-threads
>  @item maint set worker-threads
> diff --git a/gdb/dwarf2/frame-tailcall.c b/gdb/dwarf2/frame-tailcall.c
> index 54813d00b03..2d7ab740f62 100644
> --- a/gdb/dwarf2/frame-tailcall.c
> +++ b/gdb/dwarf2/frame-tailcall.c
> @@ -321,9 +321,6 @@ tailcall_frame_sniffer (const struct frame_unwind *self,
>    int next_levels;
>    struct tailcall_cache *cache;
>  
> -  if (!dwarf2_frame_unwinders_enabled_p)
> -    return 0;
> -
>    /* Inner tail call element does not make sense for a sentinel frame.  */
>    next_frame = get_next_frame (this_frame);
>    if (next_frame == NULL)
> diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
> index 85e1d59bc09..e0e8eb5dbec 100644
> --- a/gdb/dwarf2/frame.c
> +++ b/gdb/dwarf2/frame.c
> @@ -183,9 +183,6 @@ static ULONGEST read_encoded_value (struct comp_unit *unit, gdb_byte encoding,
>  				    unrelocated_addr func_base);
>  \f
>  
> -/* See dwarf2/frame.h.  */
> -bool dwarf2_frame_unwinders_enabled_p = true;
> -
>  /* Store the length the expression for the CFA in the `cfa_reg' field,
>     which is unused in that case.  */
>  #define cfa_exp_len cfa_reg
> @@ -1306,9 +1303,6 @@ static int
>  dwarf2_frame_sniffer (const struct frame_unwind *self,
>  		      const frame_info_ptr &this_frame, void **this_cache)
>  {
> -  if (!dwarf2_frame_unwinders_enabled_p)
> -    return 0;
> -
>    /* Grab an address that is guaranteed to reside somewhere within the
>       function.  get_frame_pc(), with a no-return next function, can
>       end up returning something past the end of this function's body.
> @@ -2253,34 +2247,10 @@ dwarf2_build_frame_info (struct objfile *objfile)
>    set_comp_unit (objfile, unit.release ());
>  }
>  
> -/* Handle 'maintenance show dwarf unwinders'.  */
> -
> -static void
> -show_dwarf_unwinders_enabled_p (struct ui_file *file, int from_tty,
> -				struct cmd_list_element *c,
> -				const char *value)
> -{
> -  gdb_printf (file,
> -	      _("The DWARF stack unwinders are currently %s.\n"),
> -	      value);
> -}
> -
>  void _initialize_dwarf2_frame ();
>  void
>  _initialize_dwarf2_frame ()
>  {
> -  add_setshow_boolean_cmd ("unwinders", class_obscure,
> -			   &dwarf2_frame_unwinders_enabled_p , _("\
> -Set whether the DWARF stack frame unwinders are used."), _("\
> -Show whether the DWARF stack frame unwinders are used."), _("\
> -When enabled the DWARF stack frame unwinders can be used for architectures\n\
> -that support the DWARF unwinders.  Enabling the DWARF unwinders for an\n\
> -architecture that doesn't support them will have no effect."),
> -			   NULL,
> -			   show_dwarf_unwinders_enabled_p,
> -			   &set_dwarf_cmdlist,
> -			   &show_dwarf_cmdlist);
> -
>  #if GDB_SELF_TEST
>    selftests::register_test_foreach_arch ("execute_cfa_program",
>  					 selftests::execute_cfa_program_test);
> diff --git a/gdb/dwarf2/frame.h b/gdb/dwarf2/frame.h
> index 2167310fbdf..fe2d669a983 100644
> --- a/gdb/dwarf2/frame.h
> +++ b/gdb/dwarf2/frame.h
> @@ -198,12 +198,6 @@ struct dwarf2_frame_state
>    bool armcc_cfa_offsets_reversed = false;
>  };
>  
> -/* When this is true the DWARF frame unwinders can be used if they are
> -   registered with the gdbarch.  Not all architectures can or do use the
> -   DWARF unwinders.  Setting this to true on a target that does not
> -   otherwise support the DWARF unwinders has no effect.  */
> -extern bool dwarf2_frame_unwinders_enabled_p;
> -
>  /* Set the architecture-specific register state initialization
>     function for GDBARCH to INIT_REG.  */
>  
> diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
> index 3ab6af1c4da..968bb846402 100644
> --- a/gdb/frame-unwind.c
> +++ b/gdb/frame-unwind.c
> @@ -29,6 +29,7 @@
>  #include "gdbarch.h"
>  #include "dwarf2/frame-tailcall.h"
>  #include "cli/cli-cmds.h"
> +#include "cli/cli-option.h"
>  #include "inferior.h"
>  
>  /* Conversion list between the enum for frame_unwind_class and
> @@ -89,6 +90,20 @@ frame_unwinder_class_str (frame_unwind_class uclass)
>    return unwind_class_conversion[uclass];
>  }
>  
> +/* Case insensitive search for a frame_unwind_class based on the given
> +   string.  */
> +static enum frame_unwind_class
> +str_to_frame_unwind_class (const char *class_str)
> +{
> +  for (int i = 0; i < UNWIND_CLASS_NUMBER; i++)
> +    {
> +      if (strcasecmp (unwind_class_conversion[i], class_str) == 0)
> +	return (frame_unwind_class) i;
> +    }
> +
> +  error (_("Unknown frame unwind class: %s"), class_str);
> +}
> +
>  void
>  frame_unwind_prepend_unwinder (struct gdbarch *gdbarch,
>  				const struct frame_unwind *unwinder)
> @@ -175,27 +190,46 @@ 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));
>  
> -  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 internal_error at the end of this function.  */
> +  bool seen_disabled_unwinder = false;
> +  /* Lambda to factor out the logic of checking if an unwinder is enabled,
> +     testing it and otherwise recording if we saw a disable unwinder.  */
> +  auto test_unwinder = [&] (const struct frame_unwind *unwinder)
> +    {
> +      if (unwinder == nullptr)
> +	return false;
> +
> +      if (!unwinder->enabled ())
> +	{
> +	  seen_disabled_unwinder = true;
> +	  return false;
> +	}
> +
> +      return frame_unwind_try_unwinder (this_frame,
> +					this_cache,
> +					unwinder);
> +    };
>  
> -  unwinder_from_target = target_get_unwinder ();
> -  if (unwinder_from_target != NULL
> -      && frame_unwind_try_unwinder (this_frame, this_cache,
> -				   unwinder_from_target))
> +  if (test_unwinder (target_get_unwinder ()))
>      return;
>  
> -  unwinder_from_target = target_get_tailcall_unwinder ();
> -  if (unwinder_from_target != NULL
> -      && frame_unwind_try_unwinder (this_frame, this_cache,
> -				   unwinder_from_target))
> +  if (test_unwinder (target_get_tailcall_unwinder ()))
>      return;
>  
>    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 (test_unwinder (unwinder))
> +	return;
> +    }
>  
> -  internal_error (_("frame_unwind_find_by_frame failed"));
> +  if (seen_disabled_unwinder)
> +    error (_("Required frame unwinder may have been disabled"
> +	     ", see 'maint info frame-unwinders'"));
> +  else
> +    internal_error (_("frame_unwind_find_by_frame failed"));
>  }
>  
>  /* A default frame sniffer which always accepts the frame.  Used by
> @@ -394,10 +428,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 (9, ui_left, "class", "Class");
> +  uiout->table_header (8, ui_left, "enabled", "Enabled");
>    uiout->table_body ();
>  
>    for (auto unwinder : *table)
> @@ -407,10 +442,145 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
>        uiout->field_string ("type", frame_type_str (unwinder->type ()));
>        uiout->field_string ("class", frame_unwinder_class_str (
>  					unwinder->unwinder_class ()));
> +      uiout->field_string ("enabled", unwinder->enabled () ? "Y" : "N");
>        uiout->text ("\n");
>      }
>  }
>  
> +/* Options for disabling frame unwinders.  */
> +struct maint_frame_unwind_options
> +{
> +  std::string unwinder_name;
> +  const char *unwinder_class = nullptr;
> +  bool all = false;
> +};
> +
> +static const gdb::option::option_def maint_frame_unwind_opt_defs[] = {
> +
> +  gdb::option::flag_option_def<maint_frame_unwind_options> {
> +    "all",
> +    [] (maint_frame_unwind_options *opt) { return &opt->all; },
> +    nullptr,
> +    N_("Change the state of all unwinders")

I think for flag_option_def, this should be:

  gdb::option::flag_option_def<maint_frame_unwind_options> {
    "all",
    [] (maint_frame_unwind_options *opt) { return &opt->all; },
    N_("Change the state of all unwinders")
  }

dropping the 'nullptr'.  If/when we use %OPTIONS% this will then print
that 'Change the state of all unwinders' text as the description of this
option.

Thanks,
Andrew


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

* Re: [PATCH v8 5/5] gdb/testsuite: Test for a backtrace through object without debuginfo
  2025-01-16 14:37   ` Andrew Burgess
@ 2025-01-16 18:42     ` Guinevere Larsen
  2025-01-17 13:58       ` Andrew Burgess
  2025-01-18  8:07     ` Tom de Vries
  1 sibling, 1 reply; 24+ messages in thread
From: Guinevere Larsen @ 2025-01-16 18:42 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Jan Kratochvil, Thiago Jung Bauermann

On 1/16/25 11:37 AM, Andrew Burgess wrote:
> Guinevere Larsen <guinevere@redhat.com> writes:
>
>> Fedora has been carrying this test since back in the Project Archer
>> days. A change back then caused GDB to stop being able to backtrace when
>> only some of the object files had debug information. Even though the
>> changed code never seems to have made its way into the main GDB project,
>> I think it makes sense to bring the test along to ensure something like
>> this doesn't pass unnoticed.
>>
>> Co-Authored-By: Jan Kratochvil <jan@jankratochvil.net>
>> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
>> ---
>>   .../backtrace-through-cu-nodebug-caller.c     | 28 ++++++
>>   .../backtrace-through-cu-nodebug-main.c       | 32 +++++++
>>   .../gdb.base/backtrace-through-cu-nodebug.exp | 95 +++++++++++++++++++
>>   3 files changed, 155 insertions(+)
>>   create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
>>   create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
>>   create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
>>
>> diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
>> new file mode 100644
>> index 00000000000..3a63d72a468
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
>> @@ -0,0 +1,28 @@
>> +/* This testcase is part of GDB, the GNU debugger.
>> +
>> +   Copyright 2005-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/>.  */
>> +
>> +typedef int (*callback_t) (void);
>> +
>> +int
>> +caller (callback_t callback)
>> +{
>> +  /* Ensure some frame content to push away the return address.  */
>> +  volatile const long one = 1;
>> +
>> +  /* Modify the return value to prevent any tail-call optimization.  */
>> +  return (*callback) () - one;
>> +}
>> diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
>> new file mode 100644
>> index 00000000000..3e7ac57a166
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
>> @@ -0,0 +1,32 @@
>> +/* This testcase is part of GDB, the GNU debugger.
>> +
>> +   Copyright 2005-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/>.  */
>> +
>> +typedef int (*callback_t) (void);
>> +
>> +extern int caller (callback_t callback);
>> +
>> +int
>> +callback (void)
>> +{
>> +  return 1;
>> +}
>> +
>> +int
>> +main (void)
>> +{
>> +  return caller (callback);
>> +}
>> diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
>> new file mode 100644
>> index 00000000000..c0940b406a8
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
>> @@ -0,0 +1,95 @@
>> +# Copyright 2010-2024 Free Software Foundation, Inc.
> Remember to update the copyright year throughout.
>
>> +
>> +# 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 that GDB can generate accurate backtraces even if some of the stack
>> +# trace goes through a function with no debug information.
>> +
>> +standard_testfile -caller.c -main.c
>> +set objmainfile ${testfile}-main.o
>> +set objcallerfile ${testfile}-caller.o
>> +
>> +# recompile the inferior with or without CFI information, then run the
>> +# inferior until the point where the important test starts
>> +# returns TRUE on an ERROR.
> Needs converting to two sentences ('.' after 'starts').  Plus capital
> 'R' for each sentence.
>
> I wonder if it would be better to return TRUE on success, and FALSE on
> error, because .... see below ...
>
>> +proc prepare_test {has_cfi} {
>> +    global srcdir subdir srcfile srcfile2 objmainfile objcallerfile binfile
>> +    if {$has_cfi} {
>> +	set extension "cfi"
>> +	if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
>> +	     "${srcdir}/${subdir}/${objcallerfile}" \
>> +	     object [list {additional_flags=-fomit-frame-pointer \
>> +		 -funwind-tables -fasynchronous-unwind-tables}]] != "" } {
>> +	    untested "couldn't compile with cfi"
>> +	    return true
>> +      }
>> +    } else {
>> +	set extension "no-cfi"
>> +	if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
>> +	     "${srcdir}/${subdir}/${objcallerfile}" \
>> +	     object [list {additional_flags=-fomit-frame-pointer \
>> +		 -fno-unwind-tables \
>> +		 -fno-asynchronous-unwind-tables}]] != "" } {
>> +	    untested "couldn't compile without cfi"
>> +	    return true
>> +      }
>> +    }
>> +    if {[gdb_compile [list "${srcdir}/${subdir}/${objmainfile}" \
>> +	    "${srcdir}/${subdir}/${objcallerfile}"] \
>> +	    "${binfile}-${extension}" binfile {}] != ""} {
>> +	untested "couldn't link object files"
>> +	return true
>> +    }
>> +
>> +    clean_restart "$binfile-${extension}"
>> +
>> +    with_test_prefix "${extension}" {
>> +
>> +	if ![runto callback] then {
>> +	   fail "has_cfi=$has_cfi: Can't run to callback"
>> +	   return true
>> +	}
>> +	gdb_test_no_output "maint frame-unwinder disable ARCH"
>> +	return false
>> +    }
>> +}
>> +
>> +if {[gdb_compile "${srcdir}/${subdir}/${srcfile2}" \
>> +	"${srcdir}/${subdir}/${objmainfile}" \
>> +	object {debug}] != "" } {
>> +    untested "couldn't compile main file"
>> +    return
>> +}
>> +
>> +if { [prepare_test false] } {
>> +     untested ${testfile}.exp
>> +} else {
>> +    gdb_test "bt" "Required frame unwinder may have been disabled.*" \
>> +	"verify unwind fail without CFI"
>> +}
> When something goes wrong with the prepare_test call we've already
> emitted an 'untested' or 'fail' message, thus I think we can just write:
>
>   if { [prepare_test false] } {
>     ... perform the test ...
>   }
>
> Of course, this assumes that the true/false return value for
> prepare_test have been switched.  Currently you'd write:
>
>   if { ![prepare_test false] } {
>     ... perform the test ...
>   }
>
> which just seems weird.
>
> Also, the test here says "verify unwind fail without CFI".  And we _do_
> check for the error message about disabled unwinders.  But if the `bt`
> worked this test would still pass just fine.  We should probably be
> checking that we only print frame #0.  Something like:
>
>      gdb_test "bt" \
> 	[multi_line \
> 	     "\[^\r\n\]+Required frame unwinder may have been disabled, \[^\r\n\]+" \
> 	     "#0\\s+callback \\(\\) \[^\r\n\]+"] \
> 	"verify unwind fail without CFI"
>
> should do the job I think.
>
>> +
>> +if { [prepare_test true] } {
>> +     untested ${testfile}.exp
>> +} else {
> Same suggestion here about avoiding the extra 'untested' call.
>
>> +    if { [istarget "arm*-*-*"] } {
>> +	setup_kfail backtrace/31950 *-*-*
>> +    }
>> +    set text {[^\r\n]+}
>> +    # #0  callback () at ...
>> +    # #1  0x00000000004004e9 in caller ()
>> +    # #2  0x00000000004004cd in main () at ...
>> +    gdb_test "bt" \
>> +	"#0 +callback $text\r\n#1 $text in caller $text\r\n#2 $text in main $text" \
>> +	"verify unwinding works for CFI without DIEs"
> The test name here seems weird.  Maybe: 'verify unwinding works for CUs
> without CFI' would be better?

I applied all other suggestions.

This naming came directly from the downstream patch, the point is to 
parse a range described by a CFI but not by debug information.

What do you think about this wording:
'Verify unwinding works based only on CFI information'

?

>
> OK with these fixes.
>
> Approved-By: Andrew Burgess <aburgess@redhat.com>
>
> Thanks,
> Andrew
>
>> +}
>> -- 
>> 2.47.0


-- 
Cheers,
Guinevere Larsen
She/Her/Hers


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

* Re: [PATCH v8 4/5] gdb: introduce ability to disable frame unwinders
  2025-01-16 12:06   ` Andrew Burgess
@ 2025-01-17 12:40     ` Guinevere Larsen
  2025-01-17 13:55       ` Andrew Burgess
  0 siblings, 1 reply; 24+ messages in thread
From: Guinevere Larsen @ 2025-01-17 12:40 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Eli Zaretskii, Thiago Jung Bauermann

On 1/16/25 9:06 AM, Andrew Burgess wrote:
> Guinevere Larsen <guinevere@redhat.com> writes:
>
>> 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. This was requested back in 2003
>> in PR backtrace/8434.
>>
>> 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.
>>
>> This commit is a more generic version of commit 3c3bb0580be0,
>> and so, based on the final paragraph of the commit message:
>>      gdb: Add switch to disable DWARF stack unwinders
>> <...>
>>      If in the future we find ourselves adding more switches to disable
>>      different unwinders, then we should probably move to a more generic
>>      solution, and remove this patch.
>> this patch also reverts 3c3bb0580be0
>>
>> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=8434
>> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
>> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
>> ---
>>   gdb/NEWS                                      |  17 ++
>>   gdb/doc/gdb.texinfo                           |  49 ++--
>>   gdb/dwarf2/frame-tailcall.c                   |   3 -
>>   gdb/dwarf2/frame.c                            |  30 ---
>>   gdb/dwarf2/frame.h                            |   6 -
>>   gdb/frame-unwind.c                            | 237 +++++++++++++++++-
>>   gdb/frame-unwind.h                            |   9 +
>>   .../gdb.base/frame-info-consistent.exp        |   8 +-
>>   gdb/testsuite/gdb.base/frame-unwind-disable.c |  22 ++
>>   .../gdb.base/frame-unwind-disable.exp         | 137 ++++++++++
>>   gdb/testsuite/gdb.base/maint.exp              |   4 -
>>   11 files changed, 437 insertions(+), 85 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 245b355669a..ef3317c11cb 100644
>> --- a/gdb/NEWS
>> +++ b/gdb/NEWS
>> @@ -125,6 +125,13 @@ maintenance info blocks [ADDRESS]
>>     are listed starting at the inner global block out to the most inner
>>     block.
>>   
>> +maintenance frame-unwinder disable [-all | -name NAME | [-class] CLASS]
>> +maintenance frame-unwinder enable [-all | -name NAME | [-class] CLASS]
>> +  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.
>> +
>>   info missing-objfile-handlers
>>     List all the registered missing-objfile handlers.
>>   
>> @@ -167,6 +174,16 @@ maintenance print remote-registers
>>   mainenance info frame-unwinders
>>     Add a CLASS column to the output.  This class is a somewhat arbitrary
>>     grouping of unwinders, based on which area of GDB adds the unwinder.
>> +  Also add an ENABLED column, that will show if the unwinder is enabled
>> +  or not.
>> +
>> +maintenance set dwarf unwinders (on|off)
>> +  This command has been removed because the same functionality can be
>> +  achieved with maint frame-unwinder (enable|disable) DEBUGINFO.
>> +
>> +maintenance show dwarf unwinders
>> +  This command has been removed since the functionality can be achieved
>> +  by checking the last column of maint info frame-unwinders.
>>   
>>   show configuration
>>     Now includes the version of GNU Readline library that GDB is using.
>> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
>> index 115c1f46b7f..10e6654a03b 100644
>> --- a/gdb/doc/gdb.texinfo
>> +++ b/gdb/doc/gdb.texinfo
>> @@ -42210,31 +42210,6 @@ On hosts without threading, or where worker threads have been disabled
>>   at runtime, this setting has no effect, as DWARF reading is always
>>   done on the main thread, and is therefore always synchronous.
>>   
>> -@kindex maint set dwarf unwinders
>> -@kindex maint show dwarf unwinders
>> -@item maint set dwarf unwinders
>> -@itemx maint show dwarf unwinders
>> -Control use of the DWARF frame unwinders.
>> -
>> -@cindex DWARF frame unwinders
>> -Many targets that support DWARF debugging use @value{GDBN}'s DWARF
>> -frame unwinders to build the backtrace.  Many of these targets will
>> -also have a second mechanism for building the backtrace for use in
>> -cases where DWARF information is not available, this second mechanism
>> -is often an analysis of a function's prologue.
>> -
>> -In order to extend testing coverage of the second level stack
>> -unwinding mechanisms it is helpful to be able to disable the DWARF
>> -stack unwinders, this can be done with this switch.
>> -
>> -In normal use of @value{GDBN} disabling the DWARF unwinders is not
>> -advisable, there are cases that are better handled through DWARF than
>> -prologue analysis, and the debug experience is likely to be better
>> -with the DWARF frame unwinders enabled.
>> -
>> -If DWARF frame unwinders are not supported for a particular target
>> -architecture, then enabling this flag does not cause them to be used.
>> -
>>   @kindex maint info frame-unwinders
>>   @item maint info frame-unwinders
>>   List the frame unwinders currently in effect, starting with the highest
>> @@ -42252,6 +42227,30 @@ Unwinders installed by debug information readers.
>>   Unwinders installed by the architecture specific code.
>>   @end table
>>   
>> +@kindex maint frame-unwinder disable
>> +@kindex maint frame-unwinder enable
>> +@item maint frame-unwinder disable [@code{-all} | @code{-name} @var{name} | [@code{-class}] @var{class}]
>> +@item maint frame-unwinder enable [@code{-all} | @code{-name} @var{name} | [@code{-class}] @var{class}]
>> +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 each of the invocations are as follows:
>> +
>> +@table @samp
>> +@item @code{-all}
>> +Disable or enable all unwinders.
>> +@item @code{-name} @var{name}
>> +@var{name} is the exact name of the unwinder to be disabled or enabled.
>> +@item [@code{-class}] @var{class}
>> +@var{class} is the class of frame unwinders to be disabled or enabled.
>> +The class may include the prefix @code{FRAME_UNWINDER_}, but it is not
>> +required.
>> +@end table
>> +
>> +@var{name} and @var{class} are always case insensitive.  If no option
>> +starting wth @code{-} is given, @value{GDBN} assumes @code{-class}.
>> +
>>   @kindex maint set worker-threads
>>   @kindex maint show worker-threads
>>   @item maint set worker-threads
>> diff --git a/gdb/dwarf2/frame-tailcall.c b/gdb/dwarf2/frame-tailcall.c
>> index 54813d00b03..2d7ab740f62 100644
>> --- a/gdb/dwarf2/frame-tailcall.c
>> +++ b/gdb/dwarf2/frame-tailcall.c
>> @@ -321,9 +321,6 @@ tailcall_frame_sniffer (const struct frame_unwind *self,
>>     int next_levels;
>>     struct tailcall_cache *cache;
>>   
>> -  if (!dwarf2_frame_unwinders_enabled_p)
>> -    return 0;
>> -
>>     /* Inner tail call element does not make sense for a sentinel frame.  */
>>     next_frame = get_next_frame (this_frame);
>>     if (next_frame == NULL)
>> diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
>> index 85e1d59bc09..e0e8eb5dbec 100644
>> --- a/gdb/dwarf2/frame.c
>> +++ b/gdb/dwarf2/frame.c
>> @@ -183,9 +183,6 @@ static ULONGEST read_encoded_value (struct comp_unit *unit, gdb_byte encoding,
>>   				    unrelocated_addr func_base);
>>   \f
>>   
>> -/* See dwarf2/frame.h.  */
>> -bool dwarf2_frame_unwinders_enabled_p = true;
>> -
>>   /* Store the length the expression for the CFA in the `cfa_reg' field,
>>      which is unused in that case.  */
>>   #define cfa_exp_len cfa_reg
>> @@ -1306,9 +1303,6 @@ static int
>>   dwarf2_frame_sniffer (const struct frame_unwind *self,
>>   		      const frame_info_ptr &this_frame, void **this_cache)
>>   {
>> -  if (!dwarf2_frame_unwinders_enabled_p)
>> -    return 0;
>> -
>>     /* Grab an address that is guaranteed to reside somewhere within the
>>        function.  get_frame_pc(), with a no-return next function, can
>>        end up returning something past the end of this function's body.
>> @@ -2253,34 +2247,10 @@ dwarf2_build_frame_info (struct objfile *objfile)
>>     set_comp_unit (objfile, unit.release ());
>>   }
>>   
>> -/* Handle 'maintenance show dwarf unwinders'.  */
>> -
>> -static void
>> -show_dwarf_unwinders_enabled_p (struct ui_file *file, int from_tty,
>> -				struct cmd_list_element *c,
>> -				const char *value)
>> -{
>> -  gdb_printf (file,
>> -	      _("The DWARF stack unwinders are currently %s.\n"),
>> -	      value);
>> -}
>> -
>>   void _initialize_dwarf2_frame ();
>>   void
>>   _initialize_dwarf2_frame ()
>>   {
>> -  add_setshow_boolean_cmd ("unwinders", class_obscure,
>> -			   &dwarf2_frame_unwinders_enabled_p , _("\
>> -Set whether the DWARF stack frame unwinders are used."), _("\
>> -Show whether the DWARF stack frame unwinders are used."), _("\
>> -When enabled the DWARF stack frame unwinders can be used for architectures\n\
>> -that support the DWARF unwinders.  Enabling the DWARF unwinders for an\n\
>> -architecture that doesn't support them will have no effect."),
>> -			   NULL,
>> -			   show_dwarf_unwinders_enabled_p,
>> -			   &set_dwarf_cmdlist,
>> -			   &show_dwarf_cmdlist);
>> -
>>   #if GDB_SELF_TEST
>>     selftests::register_test_foreach_arch ("execute_cfa_program",
>>   					 selftests::execute_cfa_program_test);
>> diff --git a/gdb/dwarf2/frame.h b/gdb/dwarf2/frame.h
>> index 2167310fbdf..fe2d669a983 100644
>> --- a/gdb/dwarf2/frame.h
>> +++ b/gdb/dwarf2/frame.h
>> @@ -198,12 +198,6 @@ struct dwarf2_frame_state
>>     bool armcc_cfa_offsets_reversed = false;
>>   };
>>   
>> -/* When this is true the DWARF frame unwinders can be used if they are
>> -   registered with the gdbarch.  Not all architectures can or do use the
>> -   DWARF unwinders.  Setting this to true on a target that does not
>> -   otherwise support the DWARF unwinders has no effect.  */
>> -extern bool dwarf2_frame_unwinders_enabled_p;
>> -
>>   /* Set the architecture-specific register state initialization
>>      function for GDBARCH to INIT_REG.  */
>>   
>> diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
>> index 3ab6af1c4da..968bb846402 100644
>> --- a/gdb/frame-unwind.c
>> +++ b/gdb/frame-unwind.c
>> @@ -29,6 +29,7 @@
>>   #include "gdbarch.h"
>>   #include "dwarf2/frame-tailcall.h"
>>   #include "cli/cli-cmds.h"
>> +#include "cli/cli-option.h"
>>   #include "inferior.h"
>>   
>>   /* Conversion list between the enum for frame_unwind_class and
>> @@ -89,6 +90,20 @@ frame_unwinder_class_str (frame_unwind_class uclass)
>>     return unwind_class_conversion[uclass];
>>   }
>>   
>> +/* Case insensitive search for a frame_unwind_class based on the given
>> +   string.  */
>> +static enum frame_unwind_class
>> +str_to_frame_unwind_class (const char *class_str)
>> +{
>> +  for (int i = 0; i < UNWIND_CLASS_NUMBER; i++)
>> +    {
>> +      if (strcasecmp (unwind_class_conversion[i], class_str) == 0)
>> +	return (frame_unwind_class) i;
>> +    }
>> +
>> +  error (_("Unknown frame unwind class: %s"), class_str);
>> +}
>> +
>>   void
>>   frame_unwind_prepend_unwinder (struct gdbarch *gdbarch,
>>   				const struct frame_unwind *unwinder)
>> @@ -175,27 +190,46 @@ 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));
>>   
>> -  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 internal_error at the end of this function.  */
>> +  bool seen_disabled_unwinder = false;
>> +  /* Lambda to factor out the logic of checking if an unwinder is enabled,
>> +     testing it and otherwise recording if we saw a disable unwinder.  */
>> +  auto test_unwinder = [&] (const struct frame_unwind *unwinder)
>> +    {
>> +      if (unwinder == nullptr)
>> +	return false;
>> +
>> +      if (!unwinder->enabled ())
>> +	{
>> +	  seen_disabled_unwinder = true;
>> +	  return false;
>> +	}
>> +
>> +      return frame_unwind_try_unwinder (this_frame,
>> +					this_cache,
>> +					unwinder);
>> +    };
>>   
>> -  unwinder_from_target = target_get_unwinder ();
>> -  if (unwinder_from_target != NULL
>> -      && frame_unwind_try_unwinder (this_frame, this_cache,
>> -				   unwinder_from_target))
>> +  if (test_unwinder (target_get_unwinder ()))
>>       return;
>>   
>> -  unwinder_from_target = target_get_tailcall_unwinder ();
>> -  if (unwinder_from_target != NULL
>> -      && frame_unwind_try_unwinder (this_frame, this_cache,
>> -				   unwinder_from_target))
>> +  if (test_unwinder (target_get_tailcall_unwinder ()))
>>       return;
>>   
>>     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 (test_unwinder (unwinder))
>> +	return;
>> +    }
>>   
>> -  internal_error (_("frame_unwind_find_by_frame failed"));
>> +  if (seen_disabled_unwinder)
>> +    error (_("Required frame unwinder may have been disabled"
>> +	     ", see 'maint info frame-unwinders'"));
>> +  else
>> +    internal_error (_("frame_unwind_find_by_frame failed"));
>>   }
>>   
>>   /* A default frame sniffer which always accepts the frame.  Used by
>> @@ -394,10 +428,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 (9, ui_left, "class", "Class");
>> +  uiout->table_header (8, ui_left, "enabled", "Enabled");
>>     uiout->table_body ();
>>   
>>     for (auto unwinder : *table)
>> @@ -407,10 +442,145 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
>>         uiout->field_string ("type", frame_type_str (unwinder->type ()));
>>         uiout->field_string ("class", frame_unwinder_class_str (
>>   					unwinder->unwinder_class ()));
>> +      uiout->field_string ("enabled", unwinder->enabled () ? "Y" : "N");
>>         uiout->text ("\n");
>>       }
>>   }
>>   
>> +/* Options for disabling frame unwinders.  */
>> +struct maint_frame_unwind_options
>> +{
>> +  std::string unwinder_name;
>> +  const char *unwinder_class = nullptr;
>> +  bool all = false;
>> +};
>> +
>> +static const gdb::option::option_def maint_frame_unwind_opt_defs[] = {
>> +
>> +  gdb::option::flag_option_def<maint_frame_unwind_options> {
>> +    "all",
>> +    [] (maint_frame_unwind_options *opt) { return &opt->all; },
>> +    nullptr,
>> +    N_("Change the state of all unwinders")
>> +  },
>> +  gdb::option::string_option_def<maint_frame_unwind_options> {
>> +    "name",
>> +    [] (maint_frame_unwind_options *opt) { return &opt->unwinder_name; },
>> +    nullptr,
>> +    N_("The name of the unwinder to have its state changed")
>> +  },
>> +  gdb::option::enum_option_def<maint_frame_unwind_options> {
>> +    "class",
>> +    unwind_class_conversion,
>> +    [] (maint_frame_unwind_options *opt) { return &opt->unwinder_class; },
>> +    nullptr,
>> +    N_("The class of unwinders to have their states changed")
>> +  }
>> +};
>> +
>> +static inline gdb::option::option_def_group
>> +make_frame_unwind_enable_disable_options (maint_frame_unwind_options *opts)
>> +{
>> +  return {{maint_frame_unwind_opt_defs}, opts};
>> +}
>> +
>> +/* 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)
>> +{
>> +  if (args == nullptr)
>> +    {
>> +      if (enable)
>> +	error (_("Specify which frame unwinder(s) should be enabled"));
>> +      else
>> +	error (_("Specify which frame unwinder(s) should be disabled"));
>> +    }
>> +
>> +  struct gdbarch* gdbarch = current_inferior ()->arch ();
>> +  std::vector<const frame_unwind *> *unwinder_list
>> +    = get_frame_unwind_table (gdbarch);
>> +
>> +  maint_frame_unwind_options opts;
>> +  gdb::option::process_options
>> +    (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR,
>> +     make_frame_unwind_enable_disable_options (&opts));
>> +
>> +  if ((opts.all && !opts.unwinder_name.empty ())
>> +      || (opts.all && opts.unwinder_class != nullptr)
>> +      || (!opts.unwinder_name.empty () && opts.unwinder_class != nullptr))
>> +    error (_("Options are mutually exclusive"));
>> +
>> +  /* First see if the user wants to change all unwinders.  */
>> +  if (opts.all)
>> +    {
>> +      for (const frame_unwind *u : *unwinder_list)
>> +	u->set_enabled (enable);
>> +
>> +      reinit_frame_cache ();
>> +      return;
>> +    }
>> +
>> +  /* If user entered a specific unwinder name, handle it here.  If the
>> +     unwinder is already at the expected state, error out.  */
>> +  if (!opts.unwinder_name.empty ())
>> +    {
>> +      bool did_something = false;
>> +      for (const frame_unwind *unwinder : *unwinder_list)
>> +	{
>> +	  if (strcasecmp (unwinder->name (),
>> +			  opts.unwinder_name.c_str ()) == 0)
>> +	    {
>> +	      if (unwinder->enabled () == enable)
>> +		{
>> +		  if (unwinder->enabled ())
>> +		    error (_("unwinder %s is already enabled"),
>> +			     unwinder->name ());
>> +		  else
>> +		    error (_("unwinder %s is already disabled"),
>> +			     unwinder->name ());
>> +		}
>> +	      unwinder->set_enabled (enable);
>> +
>> +	      did_something = true;
>> +	      break;
>> +	    }
>> +	}
>> +      if (!did_something)
>> +	error (_("couldn't find unwinder named %s"),
>> +	       opts.unwinder_name.c_str ());
>> +    }
>> +  else
>> +    {
>> +      if (opts.unwinder_class == nullptr)
>> +	opts.unwinder_class = args;
>> +      enum frame_unwind_class dclass = str_to_frame_unwind_class
>> +	(opts.unwinder_class);
>> +      for (auto unwinder: *unwinder_list)
> I think you should stick with the form you use earlier:
>
>    (const frame_unwind *unwinder : *unwinder_list)
>
> But if you really want to stick with auto, then I think:
>
>   (const auto &unwinder : *unwinder_list)
>
> would be OK.
>
>
>> +	{
>> +	  if (unwinder->unwinder_class () == dclass)
>> +	    unwinder->set_enabled (enable);
>> +	}
>> +    }
>> +
>> +  reinit_frame_cache ();
>> +}
>> +
>> +/* Implement "maint frame-unwinder 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-unwinder enable" 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 ()
>> @@ -423,4 +593,45 @@ _initialize_frame_unwind ()
>>   List the frame unwinders currently in effect.\n\
>>   Unwinders are listed 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 [-all | -name NAME | [-class] CLASS]\n\
>> +\n\
>> +These are the meanings of the options:\n\
>> +\t-all    - All available unwinders will be disabled\n\
>> +\t-name   - NAME is the exact name of the frame unwinder to be disabled\n\
>> +\t-class  - CLASS is the class of unwinders to be disabled. Valid classes are:\n\
>> +\t\tGDB       - Unwinders added by GDB core;\n\
>> +\t\tEXTENSION - Unwinders added by extension languages;\n\
>> +\t\tDEBUGINFO - Unwinders that handle debug information;\n\
>> +\t\tARCH      - Unwinders that use architecture-specific information;\n\
>> +\n\
>> +UNWINDER and NAME are 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 [-all | -name NAME | [-class] CLASS]\n\
>> +\n\
>> +These are the meanings of the options:\n\
>> +\t-all    - All available unwinders will be enabled\n\
>> +\t-name   - NAME is the exact name of the frame unwinder to be enabled\n\
>> +\t-class  - CLASS is the class of unwinders to be enabled. Valid classes are:\n\
>> +\t\tGDB       - Unwinders added by GDB core;\n\
>> +\t\tEXTENSION - Unwinders added by extension languages;\n\
>> +\t\tDEBUGINFO - Unwinders that handle debug information;\n\
>> +\t\tARCH      - Unwinders that use architecture-specific information;\n\
>> +\n\
>> +UNWINDER and NAME are case insensitive."),
>> +	   &maint_frame_unwinder);
>>   }
> At the bottom of this email you'll find a small patch which applies on
> top of this and adds some basic command completion.  It doesn't complete
> the NAMEs, I'm not sure if there's a good way to do that (yet).  It also
> doesn't switch the help text to use the %OPTIONS% mechanism, I tried
> that, but I think there may be some bugs with the formatting (I'll look
> into it), so I've left that off.
>
> You don't have to, we can add completion later, but I do think it would
> be good to merge the patch below with this one.  But it is up to you.
> Take a look and see what you think.
>
>> diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
>> index 13f18618d24..adf8b47982c 100644
>> --- a/gdb/frame-unwind.h
>> +++ b/gdb/frame-unwind.h
>> @@ -192,6 +192,12 @@ class frame_unwind
>>     const frame_data *unwind_data () const
>>     { return m_unwind_data; }
>>   
>> +  bool enabled () const
>> +  { return m_enabled; }
>> +
>> +  void set_enabled (bool state) const
>> +  { m_enabled = state; }
>> +
>>     /* Default stop_reason implementation.  It reports NO_REASON, unless the
>>        frame is the outermost.  */
>>   
>> @@ -245,6 +251,9 @@ class frame_unwind
>>     frame_unwind_class m_unwinder_class;
>>   
>>     const frame_data *m_unwind_data;
>> +
>> +  /* Whether this unwinder can be used when sniffing.  */
>> +  mutable bool m_enabled = true;
>>   };
>>   
>>   /* This is a legacy version of the frame unwinder.  The original struct
>> diff --git a/gdb/testsuite/gdb.base/frame-info-consistent.exp b/gdb/testsuite/gdb.base/frame-info-consistent.exp
>> index fe0cfad95bc..4f483111a91 100644
>> --- a/gdb/testsuite/gdb.base/frame-info-consistent.exp
>> +++ b/gdb/testsuite/gdb.base/frame-info-consistent.exp
>> @@ -95,11 +95,11 @@ proc compare_frames {frames} {
>>       }
>>   }
>>   
>> -proc test {dwarf_unwinder} {
>> +proc test {enable} {
>>   
>>       clean_restart $::binfile
>>   
>> -    gdb_test_no_output "maint set dwarf unwinder $dwarf_unwinder"
>> +    gdb_test_no_output "maint frame-unwinder $enable DEBUGINFO"
>>   
>>       if {![runto_main]} {
>>   	return 0
>> @@ -134,6 +134,6 @@ proc test {dwarf_unwinder} {
>>       }
>>   }
>>   
>> -foreach_with_prefix dwarf {"off" "on"} {
>> -    test $dwarf
>> +foreach_with_prefix action {"disable" "enable"} {
>> +    test $action
>>   }
>> 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..bbcfb01316e
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.base/frame-unwind-disable.c
>> @@ -0,0 +1,22 @@
>> +/* This testcase is part of GDB, the GNU debugger.
>> +
>> +   Copyright 2024 Free Software Foundation, Inc.
> Remember to update the copyright date when rebasing.
>
>> +
>> +   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..f4cad9a47fd
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.base/frame-unwind-disable.exp
>> @@ -0,0 +1,137 @@
>> +# Copyright 2024 Free Software Foundation, Inc.
> And again with the date.
>
>> +
>> +# 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
> You can drop '.c' here, it's the default.
>
>> +
>> +# 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 ""} } {
>> +    set should_pass false
>> +    set command "maint info frame-unwinders"
>> +    if {${testname} == ""} {
>> +	set testname "checking state ${state} for ${unwinder_name}"
>> +    }
>> +    gdb_test_multiple "${command}" "${testname}" -lbl {
>> +	-re "${unwinder_name}\\s+\\w+\\s+\\w+\\s+${state}\\s+(?=\r\n)" {
>> +	    set should_pass true
>> +	    exp_continue
>> +	}
>> +	-re "${command}" {
>> +	    exp_continue
>> +	}
>> +	-re "\\w+\\s+\\w+\\s+\\w+\\s+\\w+\\s+(?=\r\n)" {
>> +	    exp_continue
>> +	}
>> +	-re -wrap "" {
>> +	    gdb_assert ${should_pass} "${gdb_test_name}"
> I don't think the quotes around "${gdb_test_name}" are needed here?  In
> fact, there seem to be a few places where you place quotes around
> expansion of a single variable (e.g. "${command}" on the
> gdb_test_multiple line), I don't think any of these are needed.
>
> With these changes (and with or without the completion patch):

I applied all the changes, and I like the idea of adding completion, but 
I wonder: should we add a test for completion? If so, I think this is 
better saved for a later patch, so that we can work on a good test and 
further improve the completion, because it would be nice to also 
complete for "-class", "-name" and "-all", but I'm not sure how that'd 
work yet.

If a test isn't all that important we can add it now and do more work 
later on. I also have an inlined styling question

>
> Approved-By: Andrew Burgess <aburgess@redhat.com>
>
> Thanks,
> Andrew
>
>> +	}
>> +    }
>> +}
>> +
>> +# Check if all unwinders of class UNWINDER_CLASS are in the state STATE.
>> +# STATE can be either Y or N.
>> +# UNWINDER_CLASS can be one of: GDB, ARCH, EXTENSION, DEBUGINFO.  It can
>> +# also be \\w+ if checking all unwinders.
>> +proc check_unwinder_class { unwinder_class state {testname ""} } {
>> +    set command "maint info frame-unwinders"
>> +    set should_pass true
>> +    if {$testname == ""} {
>> +	set testname "checking if ${unwinder_class} state is ${state}"
>> +    }
>> +    gdb_test_multiple "${command}" "${testname}" -lbl {
>> +	-re "^\[^\r\n\]+\\s+\\w+\\s+${unwinder_class}\\s+\(\[YN\]\)\\s+(?=\r\n)" {
>> +	    # The unwinder name may have multiple words, so we need to use the
>> +	    # more generic [^\r\n] pattern to match the unwinders.
>> +	    set cur_state $expect_out(1,string)
>> +	    if {$cur_state == $state} {
>> +		set should_pass false
>> +	    }
>> +	    exp_continue
>> +	}
>> +	-re "${command}" {
>> +	    exp_continue
>> +	}
>> +	-re -wrap "" {
>> +	    gdb_assert ${should_pass} "${gdb_test_name}"
>> +	}
>> +    }
>> +}
>> +
>> +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 "\\w+" "Y" \
>> +    "all unwinders enabled before any changes"
>> +gdb_test_no_output "maint frame-unwinder disable -all"
>> +check_unwinder_class "\\w+" "N" \
>> +    "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" \
>> +    ".*Required frame unwinder may have been disabled, see 'maint info frame-unwinders'.*" \
>> +    "no suitable unwinders should be found"
>> +
>> +# Reenable all unwinders.
>> +gdb_test_no_output "maint frame-unwinder enable -all"
>> +check_unwinder_class "\\w+" "Y" \
>> +    "all unwinders should be re-enabled"
>> +
>> +# Check that we are able to get backtraces once again.
>> +gdb_test "backtrace" ".0\\s+main .. at.*" \
>> +    "we can get usable backtraces again"
>> +
>> +# 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" "dummy should continue 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 "${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" "dummy should continue 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 "${class}" "Y"
>> +}
>> diff --git a/gdb/testsuite/gdb.base/maint.exp b/gdb/testsuite/gdb.base/maint.exp
>> index 2c58ffa36c5..ae7ad0787e6 100644
>> --- a/gdb/testsuite/gdb.base/maint.exp
>> +++ b/gdb/testsuite/gdb.base/maint.exp
>> @@ -454,10 +454,6 @@ gdb_test_no_output "maint info line-table xxx.c" \
>>   
>>   set timeout $oldtimeout
>>   
>> -# Just check that the DWARF unwinders control flag is visible.
>> -gdb_test "maint show dwarf unwinders" \
>> -    "The DWARF stack unwinders are currently (on|off)\\."
>> -
>>   #============test help on maint commands
>>   
>>   test_prefix_command_help {"maint info" "maintenance info"} {
>> -- 
>> 2.47.0
> ---
>
> diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
> index 1417651108f..f3019b7ec18 100644
> --- a/gdb/frame-unwind.c
> +++ b/gdb/frame-unwind.c
> @@ -40,6 +40,7 @@ static const char * unwind_class_conversion[] =
>     "EXTENSION",
>     "DEBUGINFO",
>     "ARCH",
> +  nullptr
>   };
>   
>   /* Default sniffers, that must always be the first in the unwinder list,
> @@ -579,6 +580,29 @@ enable_disable_frame_unwinders (const char *args, int from_tty, bool enable)
>     reinit_frame_cache ();
>   }
>   
> +/* Completer for the "maint frame-unwinder enable|disable" commands.  */
> +
> +static void
> +enable_disable_frame_unwinders_completer (struct cmd_list_element *ignore,
> +					  completion_tracker &tracker,
> +					  const char *text,
> +					  const char */*word*/)
Would it be alright to write "const char * /*word*/" ? the */* is 
confusing my editor and it looks like malformed comments, and if I add 
the space, the red highlights go away.

-- 
Cheers,
Guinevere Larsen
She/Her/Hers

> +{
> +  maint_frame_unwind_options opts;
> +  const auto group = make_frame_unwind_enable_disable_options (&opts);
> +
> +  const char *start = text;
> +  if (gdb::option::complete_options
> +      (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
> +    return;
> +
> +  /* Only complete a class name as a stand-alone operand when no options
> +     are given.  */
> +  if (start == text)
> +    complete_on_enum (tracker, unwind_class_conversion, text, text);
> +  return;
> +}
> +
>   /* Implement "maint frame-unwinder disable" command.  */
>   static void
>   maintenance_disable_frame_unwinders (const char *args, int from_tty)
> @@ -613,8 +637,9 @@ Unwinders are listed starting with the highest priority."),
>   			_("Commands handling frame unwinders."),
>   			&maint_frame_unwinder, 0, &maintenancelist);
>   
> -  add_cmd ("disable", class_maintenance, maintenance_disable_frame_unwinders,
> -	   _("\
> +  cmd_list_element *c
> +    = add_cmd ("disable", class_maintenance, maintenance_disable_frame_unwinders,
> +	       _("\
>   Disable one or more frame unwinder(s).\n\
>   Usage: maint frame-unwinder disable [-all | -name NAME | [-class] CLASS]\n\
>   \n\
> @@ -628,10 +653,13 @@ These are the meanings of the options:\n\
>   \t\tARCH      - Unwinders that use architecture-specific information;\n\
>   \n\
>   UNWINDER and NAME are case insensitive."),
> -	   &maint_frame_unwinder);
> +	       &maint_frame_unwinder);
> +  set_cmd_completer_handle_brkchars (c,
> +				     enable_disable_frame_unwinders_completer);
>   
> -  add_cmd ("enable", class_maintenance, maintenance_enable_frame_unwinders,
> -	   _("\
> +  c =
> +    add_cmd ("enable", class_maintenance, maintenance_enable_frame_unwinders,
> +	     _("\
>   Enable one or more frame unwinder(s).\n\
>   Usage: maint frame-unwinder enable [-all | -name NAME | [-class] CLASS]\n\
>   \n\
> @@ -645,5 +673,7 @@ These are the meanings of the options:\n\
>   \t\tARCH      - Unwinders that use architecture-specific information;\n\
>   \n\
>   UNWINDER and NAME are case insensitive."),
> -	   &maint_frame_unwinder);
> +	     &maint_frame_unwinder);
> +  set_cmd_completer_handle_brkchars (c,
> +				     enable_disable_frame_unwinders_completer);
>   }
>


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

* Re: [PATCH v8 4/5] gdb: introduce ability to disable frame unwinders
  2025-01-17 12:40     ` Guinevere Larsen
@ 2025-01-17 13:55       ` Andrew Burgess
  2025-01-17 14:47         ` Guinevere Larsen
  0 siblings, 1 reply; 24+ messages in thread
From: Andrew Burgess @ 2025-01-17 13:55 UTC (permalink / raw)
  To: Guinevere Larsen, gdb-patches; +Cc: Eli Zaretskii, Thiago Jung Bauermann

Guinevere Larsen <guinevere@redhat.com> writes:

> On 1/16/25 9:06 AM, Andrew Burgess wrote:
>> Guinevere Larsen <guinevere@redhat.com> writes:
>>
>>> 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. This was requested back in 2003
>>> in PR backtrace/8434.
>>>
>>> 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.
>>>
>>> This commit is a more generic version of commit 3c3bb0580be0,
>>> and so, based on the final paragraph of the commit message:
>>>      gdb: Add switch to disable DWARF stack unwinders
>>> <...>
>>>      If in the future we find ourselves adding more switches to disable
>>>      different unwinders, then we should probably move to a more generic
>>>      solution, and remove this patch.
>>> this patch also reverts 3c3bb0580be0
>>>
>>> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=8434
>>> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
>>> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
>>> ---
>>>   gdb/NEWS                                      |  17 ++
>>>   gdb/doc/gdb.texinfo                           |  49 ++--
>>>   gdb/dwarf2/frame-tailcall.c                   |   3 -
>>>   gdb/dwarf2/frame.c                            |  30 ---
>>>   gdb/dwarf2/frame.h                            |   6 -
>>>   gdb/frame-unwind.c                            | 237 +++++++++++++++++-
>>>   gdb/frame-unwind.h                            |   9 +
>>>   .../gdb.base/frame-info-consistent.exp        |   8 +-
>>>   gdb/testsuite/gdb.base/frame-unwind-disable.c |  22 ++
>>>   .../gdb.base/frame-unwind-disable.exp         | 137 ++++++++++
>>>   gdb/testsuite/gdb.base/maint.exp              |   4 -
>>>   11 files changed, 437 insertions(+), 85 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 245b355669a..ef3317c11cb 100644
>>> --- a/gdb/NEWS
>>> +++ b/gdb/NEWS
>>> @@ -125,6 +125,13 @@ maintenance info blocks [ADDRESS]
>>>     are listed starting at the inner global block out to the most inner
>>>     block.
>>>   
>>> +maintenance frame-unwinder disable [-all | -name NAME | [-class] CLASS]
>>> +maintenance frame-unwinder enable [-all | -name NAME | [-class] CLASS]
>>> +  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.
>>> +
>>>   info missing-objfile-handlers
>>>     List all the registered missing-objfile handlers.
>>>   
>>> @@ -167,6 +174,16 @@ maintenance print remote-registers
>>>   mainenance info frame-unwinders
>>>     Add a CLASS column to the output.  This class is a somewhat arbitrary
>>>     grouping of unwinders, based on which area of GDB adds the unwinder.
>>> +  Also add an ENABLED column, that will show if the unwinder is enabled
>>> +  or not.
>>> +
>>> +maintenance set dwarf unwinders (on|off)
>>> +  This command has been removed because the same functionality can be
>>> +  achieved with maint frame-unwinder (enable|disable) DEBUGINFO.
>>> +
>>> +maintenance show dwarf unwinders
>>> +  This command has been removed since the functionality can be achieved
>>> +  by checking the last column of maint info frame-unwinders.
>>>   
>>>   show configuration
>>>     Now includes the version of GNU Readline library that GDB is using.
>>> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
>>> index 115c1f46b7f..10e6654a03b 100644
>>> --- a/gdb/doc/gdb.texinfo
>>> +++ b/gdb/doc/gdb.texinfo
>>> @@ -42210,31 +42210,6 @@ On hosts without threading, or where worker threads have been disabled
>>>   at runtime, this setting has no effect, as DWARF reading is always
>>>   done on the main thread, and is therefore always synchronous.
>>>   
>>> -@kindex maint set dwarf unwinders
>>> -@kindex maint show dwarf unwinders
>>> -@item maint set dwarf unwinders
>>> -@itemx maint show dwarf unwinders
>>> -Control use of the DWARF frame unwinders.
>>> -
>>> -@cindex DWARF frame unwinders
>>> -Many targets that support DWARF debugging use @value{GDBN}'s DWARF
>>> -frame unwinders to build the backtrace.  Many of these targets will
>>> -also have a second mechanism for building the backtrace for use in
>>> -cases where DWARF information is not available, this second mechanism
>>> -is often an analysis of a function's prologue.
>>> -
>>> -In order to extend testing coverage of the second level stack
>>> -unwinding mechanisms it is helpful to be able to disable the DWARF
>>> -stack unwinders, this can be done with this switch.
>>> -
>>> -In normal use of @value{GDBN} disabling the DWARF unwinders is not
>>> -advisable, there are cases that are better handled through DWARF than
>>> -prologue analysis, and the debug experience is likely to be better
>>> -with the DWARF frame unwinders enabled.
>>> -
>>> -If DWARF frame unwinders are not supported for a particular target
>>> -architecture, then enabling this flag does not cause them to be used.
>>> -
>>>   @kindex maint info frame-unwinders
>>>   @item maint info frame-unwinders
>>>   List the frame unwinders currently in effect, starting with the highest
>>> @@ -42252,6 +42227,30 @@ Unwinders installed by debug information readers.
>>>   Unwinders installed by the architecture specific code.
>>>   @end table
>>>   
>>> +@kindex maint frame-unwinder disable
>>> +@kindex maint frame-unwinder enable
>>> +@item maint frame-unwinder disable [@code{-all} | @code{-name} @var{name} | [@code{-class}] @var{class}]
>>> +@item maint frame-unwinder enable [@code{-all} | @code{-name} @var{name} | [@code{-class}] @var{class}]
>>> +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 each of the invocations are as follows:
>>> +
>>> +@table @samp
>>> +@item @code{-all}
>>> +Disable or enable all unwinders.
>>> +@item @code{-name} @var{name}
>>> +@var{name} is the exact name of the unwinder to be disabled or enabled.
>>> +@item [@code{-class}] @var{class}
>>> +@var{class} is the class of frame unwinders to be disabled or enabled.
>>> +The class may include the prefix @code{FRAME_UNWINDER_}, but it is not
>>> +required.
>>> +@end table
>>> +
>>> +@var{name} and @var{class} are always case insensitive.  If no option
>>> +starting wth @code{-} is given, @value{GDBN} assumes @code{-class}.
>>> +
>>>   @kindex maint set worker-threads
>>>   @kindex maint show worker-threads
>>>   @item maint set worker-threads
>>> diff --git a/gdb/dwarf2/frame-tailcall.c b/gdb/dwarf2/frame-tailcall.c
>>> index 54813d00b03..2d7ab740f62 100644
>>> --- a/gdb/dwarf2/frame-tailcall.c
>>> +++ b/gdb/dwarf2/frame-tailcall.c
>>> @@ -321,9 +321,6 @@ tailcall_frame_sniffer (const struct frame_unwind *self,
>>>     int next_levels;
>>>     struct tailcall_cache *cache;
>>>   
>>> -  if (!dwarf2_frame_unwinders_enabled_p)
>>> -    return 0;
>>> -
>>>     /* Inner tail call element does not make sense for a sentinel frame.  */
>>>     next_frame = get_next_frame (this_frame);
>>>     if (next_frame == NULL)
>>> diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
>>> index 85e1d59bc09..e0e8eb5dbec 100644
>>> --- a/gdb/dwarf2/frame.c
>>> +++ b/gdb/dwarf2/frame.c
>>> @@ -183,9 +183,6 @@ static ULONGEST read_encoded_value (struct comp_unit *unit, gdb_byte encoding,
>>>   				    unrelocated_addr func_base);
>>>   \f
>>>   
>>> -/* See dwarf2/frame.h.  */
>>> -bool dwarf2_frame_unwinders_enabled_p = true;
>>> -
>>>   /* Store the length the expression for the CFA in the `cfa_reg' field,
>>>      which is unused in that case.  */
>>>   #define cfa_exp_len cfa_reg
>>> @@ -1306,9 +1303,6 @@ static int
>>>   dwarf2_frame_sniffer (const struct frame_unwind *self,
>>>   		      const frame_info_ptr &this_frame, void **this_cache)
>>>   {
>>> -  if (!dwarf2_frame_unwinders_enabled_p)
>>> -    return 0;
>>> -
>>>     /* Grab an address that is guaranteed to reside somewhere within the
>>>        function.  get_frame_pc(), with a no-return next function, can
>>>        end up returning something past the end of this function's body.
>>> @@ -2253,34 +2247,10 @@ dwarf2_build_frame_info (struct objfile *objfile)
>>>     set_comp_unit (objfile, unit.release ());
>>>   }
>>>   
>>> -/* Handle 'maintenance show dwarf unwinders'.  */
>>> -
>>> -static void
>>> -show_dwarf_unwinders_enabled_p (struct ui_file *file, int from_tty,
>>> -				struct cmd_list_element *c,
>>> -				const char *value)
>>> -{
>>> -  gdb_printf (file,
>>> -	      _("The DWARF stack unwinders are currently %s.\n"),
>>> -	      value);
>>> -}
>>> -
>>>   void _initialize_dwarf2_frame ();
>>>   void
>>>   _initialize_dwarf2_frame ()
>>>   {
>>> -  add_setshow_boolean_cmd ("unwinders", class_obscure,
>>> -			   &dwarf2_frame_unwinders_enabled_p , _("\
>>> -Set whether the DWARF stack frame unwinders are used."), _("\
>>> -Show whether the DWARF stack frame unwinders are used."), _("\
>>> -When enabled the DWARF stack frame unwinders can be used for architectures\n\
>>> -that support the DWARF unwinders.  Enabling the DWARF unwinders for an\n\
>>> -architecture that doesn't support them will have no effect."),
>>> -			   NULL,
>>> -			   show_dwarf_unwinders_enabled_p,
>>> -			   &set_dwarf_cmdlist,
>>> -			   &show_dwarf_cmdlist);
>>> -
>>>   #if GDB_SELF_TEST
>>>     selftests::register_test_foreach_arch ("execute_cfa_program",
>>>   					 selftests::execute_cfa_program_test);
>>> diff --git a/gdb/dwarf2/frame.h b/gdb/dwarf2/frame.h
>>> index 2167310fbdf..fe2d669a983 100644
>>> --- a/gdb/dwarf2/frame.h
>>> +++ b/gdb/dwarf2/frame.h
>>> @@ -198,12 +198,6 @@ struct dwarf2_frame_state
>>>     bool armcc_cfa_offsets_reversed = false;
>>>   };
>>>   
>>> -/* When this is true the DWARF frame unwinders can be used if they are
>>> -   registered with the gdbarch.  Not all architectures can or do use the
>>> -   DWARF unwinders.  Setting this to true on a target that does not
>>> -   otherwise support the DWARF unwinders has no effect.  */
>>> -extern bool dwarf2_frame_unwinders_enabled_p;
>>> -
>>>   /* Set the architecture-specific register state initialization
>>>      function for GDBARCH to INIT_REG.  */
>>>   
>>> diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
>>> index 3ab6af1c4da..968bb846402 100644
>>> --- a/gdb/frame-unwind.c
>>> +++ b/gdb/frame-unwind.c
>>> @@ -29,6 +29,7 @@
>>>   #include "gdbarch.h"
>>>   #include "dwarf2/frame-tailcall.h"
>>>   #include "cli/cli-cmds.h"
>>> +#include "cli/cli-option.h"
>>>   #include "inferior.h"
>>>   
>>>   /* Conversion list between the enum for frame_unwind_class and
>>> @@ -89,6 +90,20 @@ frame_unwinder_class_str (frame_unwind_class uclass)
>>>     return unwind_class_conversion[uclass];
>>>   }
>>>   
>>> +/* Case insensitive search for a frame_unwind_class based on the given
>>> +   string.  */
>>> +static enum frame_unwind_class
>>> +str_to_frame_unwind_class (const char *class_str)
>>> +{
>>> +  for (int i = 0; i < UNWIND_CLASS_NUMBER; i++)
>>> +    {
>>> +      if (strcasecmp (unwind_class_conversion[i], class_str) == 0)
>>> +	return (frame_unwind_class) i;
>>> +    }
>>> +
>>> +  error (_("Unknown frame unwind class: %s"), class_str);
>>> +}
>>> +
>>>   void
>>>   frame_unwind_prepend_unwinder (struct gdbarch *gdbarch,
>>>   				const struct frame_unwind *unwinder)
>>> @@ -175,27 +190,46 @@ 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));
>>>   
>>> -  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 internal_error at the end of this function.  */
>>> +  bool seen_disabled_unwinder = false;
>>> +  /* Lambda to factor out the logic of checking if an unwinder is enabled,
>>> +     testing it and otherwise recording if we saw a disable unwinder.  */
>>> +  auto test_unwinder = [&] (const struct frame_unwind *unwinder)
>>> +    {
>>> +      if (unwinder == nullptr)
>>> +	return false;
>>> +
>>> +      if (!unwinder->enabled ())
>>> +	{
>>> +	  seen_disabled_unwinder = true;
>>> +	  return false;
>>> +	}
>>> +
>>> +      return frame_unwind_try_unwinder (this_frame,
>>> +					this_cache,
>>> +					unwinder);
>>> +    };
>>>   
>>> -  unwinder_from_target = target_get_unwinder ();
>>> -  if (unwinder_from_target != NULL
>>> -      && frame_unwind_try_unwinder (this_frame, this_cache,
>>> -				   unwinder_from_target))
>>> +  if (test_unwinder (target_get_unwinder ()))
>>>       return;
>>>   
>>> -  unwinder_from_target = target_get_tailcall_unwinder ();
>>> -  if (unwinder_from_target != NULL
>>> -      && frame_unwind_try_unwinder (this_frame, this_cache,
>>> -				   unwinder_from_target))
>>> +  if (test_unwinder (target_get_tailcall_unwinder ()))
>>>       return;
>>>   
>>>     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 (test_unwinder (unwinder))
>>> +	return;
>>> +    }
>>>   
>>> -  internal_error (_("frame_unwind_find_by_frame failed"));
>>> +  if (seen_disabled_unwinder)
>>> +    error (_("Required frame unwinder may have been disabled"
>>> +	     ", see 'maint info frame-unwinders'"));
>>> +  else
>>> +    internal_error (_("frame_unwind_find_by_frame failed"));
>>>   }
>>>   
>>>   /* A default frame sniffer which always accepts the frame.  Used by
>>> @@ -394,10 +428,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 (9, ui_left, "class", "Class");
>>> +  uiout->table_header (8, ui_left, "enabled", "Enabled");
>>>     uiout->table_body ();
>>>   
>>>     for (auto unwinder : *table)
>>> @@ -407,10 +442,145 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
>>>         uiout->field_string ("type", frame_type_str (unwinder->type ()));
>>>         uiout->field_string ("class", frame_unwinder_class_str (
>>>   					unwinder->unwinder_class ()));
>>> +      uiout->field_string ("enabled", unwinder->enabled () ? "Y" : "N");
>>>         uiout->text ("\n");
>>>       }
>>>   }
>>>   
>>> +/* Options for disabling frame unwinders.  */
>>> +struct maint_frame_unwind_options
>>> +{
>>> +  std::string unwinder_name;
>>> +  const char *unwinder_class = nullptr;
>>> +  bool all = false;
>>> +};
>>> +
>>> +static const gdb::option::option_def maint_frame_unwind_opt_defs[] = {
>>> +
>>> +  gdb::option::flag_option_def<maint_frame_unwind_options> {
>>> +    "all",
>>> +    [] (maint_frame_unwind_options *opt) { return &opt->all; },
>>> +    nullptr,
>>> +    N_("Change the state of all unwinders")
>>> +  },
>>> +  gdb::option::string_option_def<maint_frame_unwind_options> {
>>> +    "name",
>>> +    [] (maint_frame_unwind_options *opt) { return &opt->unwinder_name; },
>>> +    nullptr,
>>> +    N_("The name of the unwinder to have its state changed")
>>> +  },
>>> +  gdb::option::enum_option_def<maint_frame_unwind_options> {
>>> +    "class",
>>> +    unwind_class_conversion,
>>> +    [] (maint_frame_unwind_options *opt) { return &opt->unwinder_class; },
>>> +    nullptr,
>>> +    N_("The class of unwinders to have their states changed")
>>> +  }
>>> +};
>>> +
>>> +static inline gdb::option::option_def_group
>>> +make_frame_unwind_enable_disable_options (maint_frame_unwind_options *opts)
>>> +{
>>> +  return {{maint_frame_unwind_opt_defs}, opts};
>>> +}
>>> +
>>> +/* 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)
>>> +{
>>> +  if (args == nullptr)
>>> +    {
>>> +      if (enable)
>>> +	error (_("Specify which frame unwinder(s) should be enabled"));
>>> +      else
>>> +	error (_("Specify which frame unwinder(s) should be disabled"));
>>> +    }
>>> +
>>> +  struct gdbarch* gdbarch = current_inferior ()->arch ();
>>> +  std::vector<const frame_unwind *> *unwinder_list
>>> +    = get_frame_unwind_table (gdbarch);
>>> +
>>> +  maint_frame_unwind_options opts;
>>> +  gdb::option::process_options
>>> +    (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR,
>>> +     make_frame_unwind_enable_disable_options (&opts));
>>> +
>>> +  if ((opts.all && !opts.unwinder_name.empty ())
>>> +      || (opts.all && opts.unwinder_class != nullptr)
>>> +      || (!opts.unwinder_name.empty () && opts.unwinder_class != nullptr))
>>> +    error (_("Options are mutually exclusive"));
>>> +
>>> +  /* First see if the user wants to change all unwinders.  */
>>> +  if (opts.all)
>>> +    {
>>> +      for (const frame_unwind *u : *unwinder_list)
>>> +	u->set_enabled (enable);
>>> +
>>> +      reinit_frame_cache ();
>>> +      return;
>>> +    }
>>> +
>>> +  /* If user entered a specific unwinder name, handle it here.  If the
>>> +     unwinder is already at the expected state, error out.  */
>>> +  if (!opts.unwinder_name.empty ())
>>> +    {
>>> +      bool did_something = false;
>>> +      for (const frame_unwind *unwinder : *unwinder_list)
>>> +	{
>>> +	  if (strcasecmp (unwinder->name (),
>>> +			  opts.unwinder_name.c_str ()) == 0)
>>> +	    {
>>> +	      if (unwinder->enabled () == enable)
>>> +		{
>>> +		  if (unwinder->enabled ())
>>> +		    error (_("unwinder %s is already enabled"),
>>> +			     unwinder->name ());
>>> +		  else
>>> +		    error (_("unwinder %s is already disabled"),
>>> +			     unwinder->name ());
>>> +		}
>>> +	      unwinder->set_enabled (enable);
>>> +
>>> +	      did_something = true;
>>> +	      break;
>>> +	    }
>>> +	}
>>> +      if (!did_something)
>>> +	error (_("couldn't find unwinder named %s"),
>>> +	       opts.unwinder_name.c_str ());
>>> +    }
>>> +  else
>>> +    {
>>> +      if (opts.unwinder_class == nullptr)
>>> +	opts.unwinder_class = args;
>>> +      enum frame_unwind_class dclass = str_to_frame_unwind_class
>>> +	(opts.unwinder_class);
>>> +      for (auto unwinder: *unwinder_list)
>> I think you should stick with the form you use earlier:
>>
>>    (const frame_unwind *unwinder : *unwinder_list)
>>
>> But if you really want to stick with auto, then I think:
>>
>>   (const auto &unwinder : *unwinder_list)
>>
>> would be OK.
>>
>>
>>> +	{
>>> +	  if (unwinder->unwinder_class () == dclass)
>>> +	    unwinder->set_enabled (enable);
>>> +	}
>>> +    }
>>> +
>>> +  reinit_frame_cache ();
>>> +}
>>> +
>>> +/* Implement "maint frame-unwinder 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-unwinder enable" 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 ()
>>> @@ -423,4 +593,45 @@ _initialize_frame_unwind ()
>>>   List the frame unwinders currently in effect.\n\
>>>   Unwinders are listed 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 [-all | -name NAME | [-class] CLASS]\n\
>>> +\n\
>>> +These are the meanings of the options:\n\
>>> +\t-all    - All available unwinders will be disabled\n\
>>> +\t-name   - NAME is the exact name of the frame unwinder to be disabled\n\
>>> +\t-class  - CLASS is the class of unwinders to be disabled. Valid classes are:\n\
>>> +\t\tGDB       - Unwinders added by GDB core;\n\
>>> +\t\tEXTENSION - Unwinders added by extension languages;\n\
>>> +\t\tDEBUGINFO - Unwinders that handle debug information;\n\
>>> +\t\tARCH      - Unwinders that use architecture-specific information;\n\
>>> +\n\
>>> +UNWINDER and NAME are 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 [-all | -name NAME | [-class] CLASS]\n\
>>> +\n\
>>> +These are the meanings of the options:\n\
>>> +\t-all    - All available unwinders will be enabled\n\
>>> +\t-name   - NAME is the exact name of the frame unwinder to be enabled\n\
>>> +\t-class  - CLASS is the class of unwinders to be enabled. Valid classes are:\n\
>>> +\t\tGDB       - Unwinders added by GDB core;\n\
>>> +\t\tEXTENSION - Unwinders added by extension languages;\n\
>>> +\t\tDEBUGINFO - Unwinders that handle debug information;\n\
>>> +\t\tARCH      - Unwinders that use architecture-specific information;\n\
>>> +\n\
>>> +UNWINDER and NAME are case insensitive."),
>>> +	   &maint_frame_unwinder);
>>>   }
>> At the bottom of this email you'll find a small patch which applies on
>> top of this and adds some basic command completion.  It doesn't complete
>> the NAMEs, I'm not sure if there's a good way to do that (yet).  It also
>> doesn't switch the help text to use the %OPTIONS% mechanism, I tried
>> that, but I think there may be some bugs with the formatting (I'll look
>> into it), so I've left that off.
>>
>> You don't have to, we can add completion later, but I do think it would
>> be good to merge the patch below with this one.  But it is up to you.
>> Take a look and see what you think.
>>
>>> diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
>>> index 13f18618d24..adf8b47982c 100644
>>> --- a/gdb/frame-unwind.h
>>> +++ b/gdb/frame-unwind.h
>>> @@ -192,6 +192,12 @@ class frame_unwind
>>>     const frame_data *unwind_data () const
>>>     { return m_unwind_data; }
>>>   
>>> +  bool enabled () const
>>> +  { return m_enabled; }
>>> +
>>> +  void set_enabled (bool state) const
>>> +  { m_enabled = state; }
>>> +
>>>     /* Default stop_reason implementation.  It reports NO_REASON, unless the
>>>        frame is the outermost.  */
>>>   
>>> @@ -245,6 +251,9 @@ class frame_unwind
>>>     frame_unwind_class m_unwinder_class;
>>>   
>>>     const frame_data *m_unwind_data;
>>> +
>>> +  /* Whether this unwinder can be used when sniffing.  */
>>> +  mutable bool m_enabled = true;
>>>   };
>>>   
>>>   /* This is a legacy version of the frame unwinder.  The original struct
>>> diff --git a/gdb/testsuite/gdb.base/frame-info-consistent.exp b/gdb/testsuite/gdb.base/frame-info-consistent.exp
>>> index fe0cfad95bc..4f483111a91 100644
>>> --- a/gdb/testsuite/gdb.base/frame-info-consistent.exp
>>> +++ b/gdb/testsuite/gdb.base/frame-info-consistent.exp
>>> @@ -95,11 +95,11 @@ proc compare_frames {frames} {
>>>       }
>>>   }
>>>   
>>> -proc test {dwarf_unwinder} {
>>> +proc test {enable} {
>>>   
>>>       clean_restart $::binfile
>>>   
>>> -    gdb_test_no_output "maint set dwarf unwinder $dwarf_unwinder"
>>> +    gdb_test_no_output "maint frame-unwinder $enable DEBUGINFO"
>>>   
>>>       if {![runto_main]} {
>>>   	return 0
>>> @@ -134,6 +134,6 @@ proc test {dwarf_unwinder} {
>>>       }
>>>   }
>>>   
>>> -foreach_with_prefix dwarf {"off" "on"} {
>>> -    test $dwarf
>>> +foreach_with_prefix action {"disable" "enable"} {
>>> +    test $action
>>>   }
>>> 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..bbcfb01316e
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.base/frame-unwind-disable.c
>>> @@ -0,0 +1,22 @@
>>> +/* This testcase is part of GDB, the GNU debugger.
>>> +
>>> +   Copyright 2024 Free Software Foundation, Inc.
>> Remember to update the copyright date when rebasing.
>>
>>> +
>>> +   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..f4cad9a47fd
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.base/frame-unwind-disable.exp
>>> @@ -0,0 +1,137 @@
>>> +# Copyright 2024 Free Software Foundation, Inc.
>> And again with the date.
>>
>>> +
>>> +# 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
>> You can drop '.c' here, it's the default.
>>
>>> +
>>> +# 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 ""} } {
>>> +    set should_pass false
>>> +    set command "maint info frame-unwinders"
>>> +    if {${testname} == ""} {
>>> +	set testname "checking state ${state} for ${unwinder_name}"
>>> +    }
>>> +    gdb_test_multiple "${command}" "${testname}" -lbl {
>>> +	-re "${unwinder_name}\\s+\\w+\\s+\\w+\\s+${state}\\s+(?=\r\n)" {
>>> +	    set should_pass true
>>> +	    exp_continue
>>> +	}
>>> +	-re "${command}" {
>>> +	    exp_continue
>>> +	}
>>> +	-re "\\w+\\s+\\w+\\s+\\w+\\s+\\w+\\s+(?=\r\n)" {
>>> +	    exp_continue
>>> +	}
>>> +	-re -wrap "" {
>>> +	    gdb_assert ${should_pass} "${gdb_test_name}"
>> I don't think the quotes around "${gdb_test_name}" are needed here?  In
>> fact, there seem to be a few places where you place quotes around
>> expansion of a single variable (e.g. "${command}" on the
>> gdb_test_multiple line), I don't think any of these are needed.
>>
>> With these changes (and with or without the completion patch):
>
> I applied all the changes, and I like the idea of adding completion, but 
> I wonder: should we add a test for completion? If so, I think this is 
> better saved for a later patch, so that we can work on a good test and 
> further improve the completion, because it would be nice to also 
> complete for "-class", "-name" and "-all", but I'm not sure how that'd 
> work yet.
>
> If a test isn't all that important we can add it now and do more work 
> later on. I also have an inlined styling question

I'm not sure every function with completion has tests.  We do have some
general tests that cover completion though.

If it bothers you, then feel free to leave it out for now, I certainly
don't want to delay this work going in just for completion of a
maintenance command, you already switched over to the option system for
me, which means we're better placed to add completion now or later, so
I'm grateful for that.

The function I proposed should be able to complete the '-class',
'-name', and '-all' option names.  Additionally, the '-class' value
should complete.  And the class name as an operand, if there are no
options given.

Completion of -name values is trickier, as the possible set of names can
change by architecture, and the set of extensions loaded (I think).
What we really need is for -name to be a "dynamic" enum option, where
the dynamic property is provided by a callback which gets the list of
possible enum values.  But such dynamic enums don't currently exist.
But I might look into adding that feature once this patch lands.

Anyway, feel free to add the completion, or not.  If not I'll follow up
afterwards.

>
>>
>> Approved-By: Andrew Burgess <aburgess@redhat.com>
>>
>> Thanks,
>> Andrew
>>
>>> +	}
>>> +    }
>>> +}
>>> +
>>> +# Check if all unwinders of class UNWINDER_CLASS are in the state STATE.
>>> +# STATE can be either Y or N.
>>> +# UNWINDER_CLASS can be one of: GDB, ARCH, EXTENSION, DEBUGINFO.  It can
>>> +# also be \\w+ if checking all unwinders.
>>> +proc check_unwinder_class { unwinder_class state {testname ""} } {
>>> +    set command "maint info frame-unwinders"
>>> +    set should_pass true
>>> +    if {$testname == ""} {
>>> +	set testname "checking if ${unwinder_class} state is ${state}"
>>> +    }
>>> +    gdb_test_multiple "${command}" "${testname}" -lbl {
>>> +	-re "^\[^\r\n\]+\\s+\\w+\\s+${unwinder_class}\\s+\(\[YN\]\)\\s+(?=\r\n)" {
>>> +	    # The unwinder name may have multiple words, so we need to use the
>>> +	    # more generic [^\r\n] pattern to match the unwinders.
>>> +	    set cur_state $expect_out(1,string)
>>> +	    if {$cur_state == $state} {
>>> +		set should_pass false
>>> +	    }
>>> +	    exp_continue
>>> +	}
>>> +	-re "${command}" {
>>> +	    exp_continue
>>> +	}
>>> +	-re -wrap "" {
>>> +	    gdb_assert ${should_pass} "${gdb_test_name}"
>>> +	}
>>> +    }
>>> +}
>>> +
>>> +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 "\\w+" "Y" \
>>> +    "all unwinders enabled before any changes"
>>> +gdb_test_no_output "maint frame-unwinder disable -all"
>>> +check_unwinder_class "\\w+" "N" \
>>> +    "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" \
>>> +    ".*Required frame unwinder may have been disabled, see 'maint info frame-unwinders'.*" \
>>> +    "no suitable unwinders should be found"
>>> +
>>> +# Reenable all unwinders.
>>> +gdb_test_no_output "maint frame-unwinder enable -all"
>>> +check_unwinder_class "\\w+" "Y" \
>>> +    "all unwinders should be re-enabled"
>>> +
>>> +# Check that we are able to get backtraces once again.
>>> +gdb_test "backtrace" ".0\\s+main .. at.*" \
>>> +    "we can get usable backtraces again"
>>> +
>>> +# 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" "dummy should continue 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 "${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" "dummy should continue 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 "${class}" "Y"
>>> +}
>>> diff --git a/gdb/testsuite/gdb.base/maint.exp b/gdb/testsuite/gdb.base/maint.exp
>>> index 2c58ffa36c5..ae7ad0787e6 100644
>>> --- a/gdb/testsuite/gdb.base/maint.exp
>>> +++ b/gdb/testsuite/gdb.base/maint.exp
>>> @@ -454,10 +454,6 @@ gdb_test_no_output "maint info line-table xxx.c" \
>>>   
>>>   set timeout $oldtimeout
>>>   
>>> -# Just check that the DWARF unwinders control flag is visible.
>>> -gdb_test "maint show dwarf unwinders" \
>>> -    "The DWARF stack unwinders are currently (on|off)\\."
>>> -
>>>   #============test help on maint commands
>>>   
>>>   test_prefix_command_help {"maint info" "maintenance info"} {
>>> -- 
>>> 2.47.0
>> ---
>>
>> diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
>> index 1417651108f..f3019b7ec18 100644
>> --- a/gdb/frame-unwind.c
>> +++ b/gdb/frame-unwind.c
>> @@ -40,6 +40,7 @@ static const char * unwind_class_conversion[] =
>>     "EXTENSION",
>>     "DEBUGINFO",
>>     "ARCH",
>> +  nullptr
>>   };
>>   
>>   /* Default sniffers, that must always be the first in the unwinder list,
>> @@ -579,6 +580,29 @@ enable_disable_frame_unwinders (const char *args, int from_tty, bool enable)
>>     reinit_frame_cache ();
>>   }
>>   
>> +/* Completer for the "maint frame-unwinder enable|disable" commands.  */
>> +
>> +static void
>> +enable_disable_frame_unwinders_completer (struct cmd_list_element *ignore,
>> +					  completion_tracker &tracker,
>> +					  const char *text,
>> +					  const char */*word*/)
> Would it be alright to write "const char * /*word*/" ? the */* is 
> confusing my editor and it looks like malformed comments, and if I add 
> the space, the red highlights go away.

Sure.  I just copied this from elsewhere in GDB :)

Thanks,
Andrew


>
> -- 
> Cheers,
> Guinevere Larsen
> She/Her/Hers
>
>> +{
>> +  maint_frame_unwind_options opts;
>> +  const auto group = make_frame_unwind_enable_disable_options (&opts);
>> +
>> +  const char *start = text;
>> +  if (gdb::option::complete_options
>> +      (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
>> +    return;
>> +
>> +  /* Only complete a class name as a stand-alone operand when no options
>> +     are given.  */
>> +  if (start == text)
>> +    complete_on_enum (tracker, unwind_class_conversion, text, text);
>> +  return;
>> +}
>> +
>>   /* Implement "maint frame-unwinder disable" command.  */
>>   static void
>>   maintenance_disable_frame_unwinders (const char *args, int from_tty)
>> @@ -613,8 +637,9 @@ Unwinders are listed starting with the highest priority."),
>>   			_("Commands handling frame unwinders."),
>>   			&maint_frame_unwinder, 0, &maintenancelist);
>>   
>> -  add_cmd ("disable", class_maintenance, maintenance_disable_frame_unwinders,
>> -	   _("\
>> +  cmd_list_element *c
>> +    = add_cmd ("disable", class_maintenance, maintenance_disable_frame_unwinders,
>> +	       _("\
>>   Disable one or more frame unwinder(s).\n\
>>   Usage: maint frame-unwinder disable [-all | -name NAME | [-class] CLASS]\n\
>>   \n\
>> @@ -628,10 +653,13 @@ These are the meanings of the options:\n\
>>   \t\tARCH      - Unwinders that use architecture-specific information;\n\
>>   \n\
>>   UNWINDER and NAME are case insensitive."),
>> -	   &maint_frame_unwinder);
>> +	       &maint_frame_unwinder);
>> +  set_cmd_completer_handle_brkchars (c,
>> +				     enable_disable_frame_unwinders_completer);
>>   
>> -  add_cmd ("enable", class_maintenance, maintenance_enable_frame_unwinders,
>> -	   _("\
>> +  c =
>> +    add_cmd ("enable", class_maintenance, maintenance_enable_frame_unwinders,
>> +	     _("\
>>   Enable one or more frame unwinder(s).\n\
>>   Usage: maint frame-unwinder enable [-all | -name NAME | [-class] CLASS]\n\
>>   \n\
>> @@ -645,5 +673,7 @@ These are the meanings of the options:\n\
>>   \t\tARCH      - Unwinders that use architecture-specific information;\n\
>>   \n\
>>   UNWINDER and NAME are case insensitive."),
>> -	   &maint_frame_unwinder);
>> +	     &maint_frame_unwinder);
>> +  set_cmd_completer_handle_brkchars (c,
>> +				     enable_disable_frame_unwinders_completer);
>>   }
>>


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

* Re: [PATCH v8 5/5] gdb/testsuite: Test for a backtrace through object without debuginfo
  2025-01-16 18:42     ` Guinevere Larsen
@ 2025-01-17 13:58       ` Andrew Burgess
  0 siblings, 0 replies; 24+ messages in thread
From: Andrew Burgess @ 2025-01-17 13:58 UTC (permalink / raw)
  To: Guinevere Larsen, gdb-patches; +Cc: Jan Kratochvil, Thiago Jung Bauermann

Guinevere Larsen <guinevere@redhat.com> writes:

> On 1/16/25 11:37 AM, Andrew Burgess wrote:
>> Guinevere Larsen <guinevere@redhat.com> writes:
>>
>>> Fedora has been carrying this test since back in the Project Archer
>>> days. A change back then caused GDB to stop being able to backtrace when
>>> only some of the object files had debug information. Even though the
>>> changed code never seems to have made its way into the main GDB project,
>>> I think it makes sense to bring the test along to ensure something like
>>> this doesn't pass unnoticed.
>>>
>>> Co-Authored-By: Jan Kratochvil <jan@jankratochvil.net>
>>> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
>>> ---
>>>   .../backtrace-through-cu-nodebug-caller.c     | 28 ++++++
>>>   .../backtrace-through-cu-nodebug-main.c       | 32 +++++++
>>>   .../gdb.base/backtrace-through-cu-nodebug.exp | 95 +++++++++++++++++++
>>>   3 files changed, 155 insertions(+)
>>>   create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
>>>   create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
>>>   create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
>>>
>>> diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
>>> new file mode 100644
>>> index 00000000000..3a63d72a468
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
>>> @@ -0,0 +1,28 @@
>>> +/* This testcase is part of GDB, the GNU debugger.
>>> +
>>> +   Copyright 2005-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/>.  */
>>> +
>>> +typedef int (*callback_t) (void);
>>> +
>>> +int
>>> +caller (callback_t callback)
>>> +{
>>> +  /* Ensure some frame content to push away the return address.  */
>>> +  volatile const long one = 1;
>>> +
>>> +  /* Modify the return value to prevent any tail-call optimization.  */
>>> +  return (*callback) () - one;
>>> +}
>>> diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
>>> new file mode 100644
>>> index 00000000000..3e7ac57a166
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
>>> @@ -0,0 +1,32 @@
>>> +/* This testcase is part of GDB, the GNU debugger.
>>> +
>>> +   Copyright 2005-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/>.  */
>>> +
>>> +typedef int (*callback_t) (void);
>>> +
>>> +extern int caller (callback_t callback);
>>> +
>>> +int
>>> +callback (void)
>>> +{
>>> +  return 1;
>>> +}
>>> +
>>> +int
>>> +main (void)
>>> +{
>>> +  return caller (callback);
>>> +}
>>> diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
>>> new file mode 100644
>>> index 00000000000..c0940b406a8
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
>>> @@ -0,0 +1,95 @@
>>> +# Copyright 2010-2024 Free Software Foundation, Inc.
>> Remember to update the copyright year throughout.
>>
>>> +
>>> +# 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 that GDB can generate accurate backtraces even if some of the stack
>>> +# trace goes through a function with no debug information.
>>> +
>>> +standard_testfile -caller.c -main.c
>>> +set objmainfile ${testfile}-main.o
>>> +set objcallerfile ${testfile}-caller.o
>>> +
>>> +# recompile the inferior with or without CFI information, then run the
>>> +# inferior until the point where the important test starts
>>> +# returns TRUE on an ERROR.
>> Needs converting to two sentences ('.' after 'starts').  Plus capital
>> 'R' for each sentence.
>>
>> I wonder if it would be better to return TRUE on success, and FALSE on
>> error, because .... see below ...
>>
>>> +proc prepare_test {has_cfi} {
>>> +    global srcdir subdir srcfile srcfile2 objmainfile objcallerfile binfile
>>> +    if {$has_cfi} {
>>> +	set extension "cfi"
>>> +	if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
>>> +	     "${srcdir}/${subdir}/${objcallerfile}" \
>>> +	     object [list {additional_flags=-fomit-frame-pointer \
>>> +		 -funwind-tables -fasynchronous-unwind-tables}]] != "" } {
>>> +	    untested "couldn't compile with cfi"
>>> +	    return true
>>> +      }
>>> +    } else {
>>> +	set extension "no-cfi"
>>> +	if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
>>> +	     "${srcdir}/${subdir}/${objcallerfile}" \
>>> +	     object [list {additional_flags=-fomit-frame-pointer \
>>> +		 -fno-unwind-tables \
>>> +		 -fno-asynchronous-unwind-tables}]] != "" } {
>>> +	    untested "couldn't compile without cfi"
>>> +	    return true
>>> +      }
>>> +    }
>>> +    if {[gdb_compile [list "${srcdir}/${subdir}/${objmainfile}" \
>>> +	    "${srcdir}/${subdir}/${objcallerfile}"] \
>>> +	    "${binfile}-${extension}" binfile {}] != ""} {
>>> +	untested "couldn't link object files"
>>> +	return true
>>> +    }
>>> +
>>> +    clean_restart "$binfile-${extension}"
>>> +
>>> +    with_test_prefix "${extension}" {
>>> +
>>> +	if ![runto callback] then {
>>> +	   fail "has_cfi=$has_cfi: Can't run to callback"
>>> +	   return true
>>> +	}
>>> +	gdb_test_no_output "maint frame-unwinder disable ARCH"
>>> +	return false
>>> +    }
>>> +}
>>> +
>>> +if {[gdb_compile "${srcdir}/${subdir}/${srcfile2}" \
>>> +	"${srcdir}/${subdir}/${objmainfile}" \
>>> +	object {debug}] != "" } {
>>> +    untested "couldn't compile main file"
>>> +    return
>>> +}
>>> +
>>> +if { [prepare_test false] } {
>>> +     untested ${testfile}.exp
>>> +} else {
>>> +    gdb_test "bt" "Required frame unwinder may have been disabled.*" \
>>> +	"verify unwind fail without CFI"
>>> +}
>> When something goes wrong with the prepare_test call we've already
>> emitted an 'untested' or 'fail' message, thus I think we can just write:
>>
>>   if { [prepare_test false] } {
>>     ... perform the test ...
>>   }
>>
>> Of course, this assumes that the true/false return value for
>> prepare_test have been switched.  Currently you'd write:
>>
>>   if { ![prepare_test false] } {
>>     ... perform the test ...
>>   }
>>
>> which just seems weird.
>>
>> Also, the test here says "verify unwind fail without CFI".  And we _do_
>> check for the error message about disabled unwinders.  But if the `bt`
>> worked this test would still pass just fine.  We should probably be
>> checking that we only print frame #0.  Something like:
>>
>>      gdb_test "bt" \
>> 	[multi_line \
>> 	     "\[^\r\n\]+Required frame unwinder may have been disabled, \[^\r\n\]+" \
>> 	     "#0\\s+callback \\(\\) \[^\r\n\]+"] \
>> 	"verify unwind fail without CFI"
>>
>> should do the job I think.
>>
>>> +
>>> +if { [prepare_test true] } {
>>> +     untested ${testfile}.exp
>>> +} else {
>> Same suggestion here about avoiding the extra 'untested' call.
>>
>>> +    if { [istarget "arm*-*-*"] } {
>>> +	setup_kfail backtrace/31950 *-*-*
>>> +    }
>>> +    set text {[^\r\n]+}
>>> +    # #0  callback () at ...
>>> +    # #1  0x00000000004004e9 in caller ()
>>> +    # #2  0x00000000004004cd in main () at ...
>>> +    gdb_test "bt" \
>>> +	"#0 +callback $text\r\n#1 $text in caller $text\r\n#2 $text in main $text" \
>>> +	"verify unwinding works for CFI without DIEs"
>> The test name here seems weird.  Maybe: 'verify unwinding works for CUs
>> without CFI' would be better?
>
> I applied all other suggestions.
>
> This naming came directly from the downstream patch, the point is to 
> parse a range described by a CFI but not by debug information.

Ahh, OK.  Not sure why I didn't grok that meaning yesterday.  Maybe it's
OK to just leave it then?  Or maybe just s/DIEs/debug info/?

>
> What do you think about this wording:
> 'Verify unwinding works based only on CFI information'

Yeah, or that.

Sorry, I think I was just being a bit slow when I read that yesterday!

Thanks,
Andrew

>
> ?
>
>>
>> OK with these fixes.
>>
>> Approved-By: Andrew Burgess <aburgess@redhat.com>
>>
>> Thanks,
>> Andrew
>>
>>> +}
>>> -- 
>>> 2.47.0
>
>
> -- 
> Cheers,
> Guinevere Larsen
> She/Her/Hers


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

* Re: [PATCH v8 4/5] gdb: introduce ability to disable frame unwinders
  2025-01-17 13:55       ` Andrew Burgess
@ 2025-01-17 14:47         ` Guinevere Larsen
  0 siblings, 0 replies; 24+ messages in thread
From: Guinevere Larsen @ 2025-01-17 14:47 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Eli Zaretskii, Thiago Jung Bauermann

On 1/17/25 10:55 AM, Andrew Burgess wrote:
> Guinevere Larsen <guinevere@redhat.com> writes:
>
>> On 1/16/25 9:06 AM, Andrew Burgess wrote:
>>> Guinevere Larsen <guinevere@redhat.com> writes:
>>>
>>>> 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. This was requested back in 2003
>>>> in PR backtrace/8434.
>>>>
>>>> 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.
>>>>
>>>> This commit is a more generic version of commit 3c3bb0580be0,
>>>> and so, based on the final paragraph of the commit message:
>>>>       gdb: Add switch to disable DWARF stack unwinders
>>>> <...>
>>>>       If in the future we find ourselves adding more switches to disable
>>>>       different unwinders, then we should probably move to a more generic
>>>>       solution, and remove this patch.
>>>> this patch also reverts 3c3bb0580be0
>>>>
>>>> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=8434
>>>> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
>>>> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
>>>> ---
>>>>    gdb/NEWS                                      |  17 ++
>>>>    gdb/doc/gdb.texinfo                           |  49 ++--
>>>>    gdb/dwarf2/frame-tailcall.c                   |   3 -
>>>>    gdb/dwarf2/frame.c                            |  30 ---
>>>>    gdb/dwarf2/frame.h                            |   6 -
>>>>    gdb/frame-unwind.c                            | 237 +++++++++++++++++-
>>>>    gdb/frame-unwind.h                            |   9 +
>>>>    .../gdb.base/frame-info-consistent.exp        |   8 +-
>>>>    gdb/testsuite/gdb.base/frame-unwind-disable.c |  22 ++
>>>>    .../gdb.base/frame-unwind-disable.exp         | 137 ++++++++++
>>>>    gdb/testsuite/gdb.base/maint.exp              |   4 -
>>>>    11 files changed, 437 insertions(+), 85 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 245b355669a..ef3317c11cb 100644
>>>> --- a/gdb/NEWS
>>>> +++ b/gdb/NEWS
>>>> @@ -125,6 +125,13 @@ maintenance info blocks [ADDRESS]
>>>>      are listed starting at the inner global block out to the most inner
>>>>      block.
>>>>    
>>>> +maintenance frame-unwinder disable [-all | -name NAME | [-class] CLASS]
>>>> +maintenance frame-unwinder enable [-all | -name NAME | [-class] CLASS]
>>>> +  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.
>>>> +
>>>>    info missing-objfile-handlers
>>>>      List all the registered missing-objfile handlers.
>>>>    
>>>> @@ -167,6 +174,16 @@ maintenance print remote-registers
>>>>    mainenance info frame-unwinders
>>>>      Add a CLASS column to the output.  This class is a somewhat arbitrary
>>>>      grouping of unwinders, based on which area of GDB adds the unwinder.
>>>> +  Also add an ENABLED column, that will show if the unwinder is enabled
>>>> +  or not.
>>>> +
>>>> +maintenance set dwarf unwinders (on|off)
>>>> +  This command has been removed because the same functionality can be
>>>> +  achieved with maint frame-unwinder (enable|disable) DEBUGINFO.
>>>> +
>>>> +maintenance show dwarf unwinders
>>>> +  This command has been removed since the functionality can be achieved
>>>> +  by checking the last column of maint info frame-unwinders.
>>>>    
>>>>    show configuration
>>>>      Now includes the version of GNU Readline library that GDB is using.
>>>> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
>>>> index 115c1f46b7f..10e6654a03b 100644
>>>> --- a/gdb/doc/gdb.texinfo
>>>> +++ b/gdb/doc/gdb.texinfo
>>>> @@ -42210,31 +42210,6 @@ On hosts without threading, or where worker threads have been disabled
>>>>    at runtime, this setting has no effect, as DWARF reading is always
>>>>    done on the main thread, and is therefore always synchronous.
>>>>    
>>>> -@kindex maint set dwarf unwinders
>>>> -@kindex maint show dwarf unwinders
>>>> -@item maint set dwarf unwinders
>>>> -@itemx maint show dwarf unwinders
>>>> -Control use of the DWARF frame unwinders.
>>>> -
>>>> -@cindex DWARF frame unwinders
>>>> -Many targets that support DWARF debugging use @value{GDBN}'s DWARF
>>>> -frame unwinders to build the backtrace.  Many of these targets will
>>>> -also have a second mechanism for building the backtrace for use in
>>>> -cases where DWARF information is not available, this second mechanism
>>>> -is often an analysis of a function's prologue.
>>>> -
>>>> -In order to extend testing coverage of the second level stack
>>>> -unwinding mechanisms it is helpful to be able to disable the DWARF
>>>> -stack unwinders, this can be done with this switch.
>>>> -
>>>> -In normal use of @value{GDBN} disabling the DWARF unwinders is not
>>>> -advisable, there are cases that are better handled through DWARF than
>>>> -prologue analysis, and the debug experience is likely to be better
>>>> -with the DWARF frame unwinders enabled.
>>>> -
>>>> -If DWARF frame unwinders are not supported for a particular target
>>>> -architecture, then enabling this flag does not cause them to be used.
>>>> -
>>>>    @kindex maint info frame-unwinders
>>>>    @item maint info frame-unwinders
>>>>    List the frame unwinders currently in effect, starting with the highest
>>>> @@ -42252,6 +42227,30 @@ Unwinders installed by debug information readers.
>>>>    Unwinders installed by the architecture specific code.
>>>>    @end table
>>>>    
>>>> +@kindex maint frame-unwinder disable
>>>> +@kindex maint frame-unwinder enable
>>>> +@item maint frame-unwinder disable [@code{-all} | @code{-name} @var{name} | [@code{-class}] @var{class}]
>>>> +@item maint frame-unwinder enable [@code{-all} | @code{-name} @var{name} | [@code{-class}] @var{class}]
>>>> +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 each of the invocations are as follows:
>>>> +
>>>> +@table @samp
>>>> +@item @code{-all}
>>>> +Disable or enable all unwinders.
>>>> +@item @code{-name} @var{name}
>>>> +@var{name} is the exact name of the unwinder to be disabled or enabled.
>>>> +@item [@code{-class}] @var{class}
>>>> +@var{class} is the class of frame unwinders to be disabled or enabled.
>>>> +The class may include the prefix @code{FRAME_UNWINDER_}, but it is not
>>>> +required.
>>>> +@end table
>>>> +
>>>> +@var{name} and @var{class} are always case insensitive.  If no option
>>>> +starting wth @code{-} is given, @value{GDBN} assumes @code{-class}.
>>>> +
>>>>    @kindex maint set worker-threads
>>>>    @kindex maint show worker-threads
>>>>    @item maint set worker-threads
>>>> diff --git a/gdb/dwarf2/frame-tailcall.c b/gdb/dwarf2/frame-tailcall.c
>>>> index 54813d00b03..2d7ab740f62 100644
>>>> --- a/gdb/dwarf2/frame-tailcall.c
>>>> +++ b/gdb/dwarf2/frame-tailcall.c
>>>> @@ -321,9 +321,6 @@ tailcall_frame_sniffer (const struct frame_unwind *self,
>>>>      int next_levels;
>>>>      struct tailcall_cache *cache;
>>>>    
>>>> -  if (!dwarf2_frame_unwinders_enabled_p)
>>>> -    return 0;
>>>> -
>>>>      /* Inner tail call element does not make sense for a sentinel frame.  */
>>>>      next_frame = get_next_frame (this_frame);
>>>>      if (next_frame == NULL)
>>>> diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
>>>> index 85e1d59bc09..e0e8eb5dbec 100644
>>>> --- a/gdb/dwarf2/frame.c
>>>> +++ b/gdb/dwarf2/frame.c
>>>> @@ -183,9 +183,6 @@ static ULONGEST read_encoded_value (struct comp_unit *unit, gdb_byte encoding,
>>>>    				    unrelocated_addr func_base);
>>>>    \f
>>>>    
>>>> -/* See dwarf2/frame.h.  */
>>>> -bool dwarf2_frame_unwinders_enabled_p = true;
>>>> -
>>>>    /* Store the length the expression for the CFA in the `cfa_reg' field,
>>>>       which is unused in that case.  */
>>>>    #define cfa_exp_len cfa_reg
>>>> @@ -1306,9 +1303,6 @@ static int
>>>>    dwarf2_frame_sniffer (const struct frame_unwind *self,
>>>>    		      const frame_info_ptr &this_frame, void **this_cache)
>>>>    {
>>>> -  if (!dwarf2_frame_unwinders_enabled_p)
>>>> -    return 0;
>>>> -
>>>>      /* Grab an address that is guaranteed to reside somewhere within the
>>>>         function.  get_frame_pc(), with a no-return next function, can
>>>>         end up returning something past the end of this function's body.
>>>> @@ -2253,34 +2247,10 @@ dwarf2_build_frame_info (struct objfile *objfile)
>>>>      set_comp_unit (objfile, unit.release ());
>>>>    }
>>>>    
>>>> -/* Handle 'maintenance show dwarf unwinders'.  */
>>>> -
>>>> -static void
>>>> -show_dwarf_unwinders_enabled_p (struct ui_file *file, int from_tty,
>>>> -				struct cmd_list_element *c,
>>>> -				const char *value)
>>>> -{
>>>> -  gdb_printf (file,
>>>> -	      _("The DWARF stack unwinders are currently %s.\n"),
>>>> -	      value);
>>>> -}
>>>> -
>>>>    void _initialize_dwarf2_frame ();
>>>>    void
>>>>    _initialize_dwarf2_frame ()
>>>>    {
>>>> -  add_setshow_boolean_cmd ("unwinders", class_obscure,
>>>> -			   &dwarf2_frame_unwinders_enabled_p , _("\
>>>> -Set whether the DWARF stack frame unwinders are used."), _("\
>>>> -Show whether the DWARF stack frame unwinders are used."), _("\
>>>> -When enabled the DWARF stack frame unwinders can be used for architectures\n\
>>>> -that support the DWARF unwinders.  Enabling the DWARF unwinders for an\n\
>>>> -architecture that doesn't support them will have no effect."),
>>>> -			   NULL,
>>>> -			   show_dwarf_unwinders_enabled_p,
>>>> -			   &set_dwarf_cmdlist,
>>>> -			   &show_dwarf_cmdlist);
>>>> -
>>>>    #if GDB_SELF_TEST
>>>>      selftests::register_test_foreach_arch ("execute_cfa_program",
>>>>    					 selftests::execute_cfa_program_test);
>>>> diff --git a/gdb/dwarf2/frame.h b/gdb/dwarf2/frame.h
>>>> index 2167310fbdf..fe2d669a983 100644
>>>> --- a/gdb/dwarf2/frame.h
>>>> +++ b/gdb/dwarf2/frame.h
>>>> @@ -198,12 +198,6 @@ struct dwarf2_frame_state
>>>>      bool armcc_cfa_offsets_reversed = false;
>>>>    };
>>>>    
>>>> -/* When this is true the DWARF frame unwinders can be used if they are
>>>> -   registered with the gdbarch.  Not all architectures can or do use the
>>>> -   DWARF unwinders.  Setting this to true on a target that does not
>>>> -   otherwise support the DWARF unwinders has no effect.  */
>>>> -extern bool dwarf2_frame_unwinders_enabled_p;
>>>> -
>>>>    /* Set the architecture-specific register state initialization
>>>>       function for GDBARCH to INIT_REG.  */
>>>>    
>>>> diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
>>>> index 3ab6af1c4da..968bb846402 100644
>>>> --- a/gdb/frame-unwind.c
>>>> +++ b/gdb/frame-unwind.c
>>>> @@ -29,6 +29,7 @@
>>>>    #include "gdbarch.h"
>>>>    #include "dwarf2/frame-tailcall.h"
>>>>    #include "cli/cli-cmds.h"
>>>> +#include "cli/cli-option.h"
>>>>    #include "inferior.h"
>>>>    
>>>>    /* Conversion list between the enum for frame_unwind_class and
>>>> @@ -89,6 +90,20 @@ frame_unwinder_class_str (frame_unwind_class uclass)
>>>>      return unwind_class_conversion[uclass];
>>>>    }
>>>>    
>>>> +/* Case insensitive search for a frame_unwind_class based on the given
>>>> +   string.  */
>>>> +static enum frame_unwind_class
>>>> +str_to_frame_unwind_class (const char *class_str)
>>>> +{
>>>> +  for (int i = 0; i < UNWIND_CLASS_NUMBER; i++)
>>>> +    {
>>>> +      if (strcasecmp (unwind_class_conversion[i], class_str) == 0)
>>>> +	return (frame_unwind_class) i;
>>>> +    }
>>>> +
>>>> +  error (_("Unknown frame unwind class: %s"), class_str);
>>>> +}
>>>> +
>>>>    void
>>>>    frame_unwind_prepend_unwinder (struct gdbarch *gdbarch,
>>>>    				const struct frame_unwind *unwinder)
>>>> @@ -175,27 +190,46 @@ 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));
>>>>    
>>>> -  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 internal_error at the end of this function.  */
>>>> +  bool seen_disabled_unwinder = false;
>>>> +  /* Lambda to factor out the logic of checking if an unwinder is enabled,
>>>> +     testing it and otherwise recording if we saw a disable unwinder.  */
>>>> +  auto test_unwinder = [&] (const struct frame_unwind *unwinder)
>>>> +    {
>>>> +      if (unwinder == nullptr)
>>>> +	return false;
>>>> +
>>>> +      if (!unwinder->enabled ())
>>>> +	{
>>>> +	  seen_disabled_unwinder = true;
>>>> +	  return false;
>>>> +	}
>>>> +
>>>> +      return frame_unwind_try_unwinder (this_frame,
>>>> +					this_cache,
>>>> +					unwinder);
>>>> +    };
>>>>    
>>>> -  unwinder_from_target = target_get_unwinder ();
>>>> -  if (unwinder_from_target != NULL
>>>> -      && frame_unwind_try_unwinder (this_frame, this_cache,
>>>> -				   unwinder_from_target))
>>>> +  if (test_unwinder (target_get_unwinder ()))
>>>>        return;
>>>>    
>>>> -  unwinder_from_target = target_get_tailcall_unwinder ();
>>>> -  if (unwinder_from_target != NULL
>>>> -      && frame_unwind_try_unwinder (this_frame, this_cache,
>>>> -				   unwinder_from_target))
>>>> +  if (test_unwinder (target_get_tailcall_unwinder ()))
>>>>        return;
>>>>    
>>>>      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 (test_unwinder (unwinder))
>>>> +	return;
>>>> +    }
>>>>    
>>>> -  internal_error (_("frame_unwind_find_by_frame failed"));
>>>> +  if (seen_disabled_unwinder)
>>>> +    error (_("Required frame unwinder may have been disabled"
>>>> +	     ", see 'maint info frame-unwinders'"));
>>>> +  else
>>>> +    internal_error (_("frame_unwind_find_by_frame failed"));
>>>>    }
>>>>    
>>>>    /* A default frame sniffer which always accepts the frame.  Used by
>>>> @@ -394,10 +428,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 (9, ui_left, "class", "Class");
>>>> +  uiout->table_header (8, ui_left, "enabled", "Enabled");
>>>>      uiout->table_body ();
>>>>    
>>>>      for (auto unwinder : *table)
>>>> @@ -407,10 +442,145 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
>>>>          uiout->field_string ("type", frame_type_str (unwinder->type ()));
>>>>          uiout->field_string ("class", frame_unwinder_class_str (
>>>>    					unwinder->unwinder_class ()));
>>>> +      uiout->field_string ("enabled", unwinder->enabled () ? "Y" : "N");
>>>>          uiout->text ("\n");
>>>>        }
>>>>    }
>>>>    
>>>> +/* Options for disabling frame unwinders.  */
>>>> +struct maint_frame_unwind_options
>>>> +{
>>>> +  std::string unwinder_name;
>>>> +  const char *unwinder_class = nullptr;
>>>> +  bool all = false;
>>>> +};
>>>> +
>>>> +static const gdb::option::option_def maint_frame_unwind_opt_defs[] = {
>>>> +
>>>> +  gdb::option::flag_option_def<maint_frame_unwind_options> {
>>>> +    "all",
>>>> +    [] (maint_frame_unwind_options *opt) { return &opt->all; },
>>>> +    nullptr,
>>>> +    N_("Change the state of all unwinders")
>>>> +  },
>>>> +  gdb::option::string_option_def<maint_frame_unwind_options> {
>>>> +    "name",
>>>> +    [] (maint_frame_unwind_options *opt) { return &opt->unwinder_name; },
>>>> +    nullptr,
>>>> +    N_("The name of the unwinder to have its state changed")
>>>> +  },
>>>> +  gdb::option::enum_option_def<maint_frame_unwind_options> {
>>>> +    "class",
>>>> +    unwind_class_conversion,
>>>> +    [] (maint_frame_unwind_options *opt) { return &opt->unwinder_class; },
>>>> +    nullptr,
>>>> +    N_("The class of unwinders to have their states changed")
>>>> +  }
>>>> +};
>>>> +
>>>> +static inline gdb::option::option_def_group
>>>> +make_frame_unwind_enable_disable_options (maint_frame_unwind_options *opts)
>>>> +{
>>>> +  return {{maint_frame_unwind_opt_defs}, opts};
>>>> +}
>>>> +
>>>> +/* 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)
>>>> +{
>>>> +  if (args == nullptr)
>>>> +    {
>>>> +      if (enable)
>>>> +	error (_("Specify which frame unwinder(s) should be enabled"));
>>>> +      else
>>>> +	error (_("Specify which frame unwinder(s) should be disabled"));
>>>> +    }
>>>> +
>>>> +  struct gdbarch* gdbarch = current_inferior ()->arch ();
>>>> +  std::vector<const frame_unwind *> *unwinder_list
>>>> +    = get_frame_unwind_table (gdbarch);
>>>> +
>>>> +  maint_frame_unwind_options opts;
>>>> +  gdb::option::process_options
>>>> +    (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR,
>>>> +     make_frame_unwind_enable_disable_options (&opts));
>>>> +
>>>> +  if ((opts.all && !opts.unwinder_name.empty ())
>>>> +      || (opts.all && opts.unwinder_class != nullptr)
>>>> +      || (!opts.unwinder_name.empty () && opts.unwinder_class != nullptr))
>>>> +    error (_("Options are mutually exclusive"));
>>>> +
>>>> +  /* First see if the user wants to change all unwinders.  */
>>>> +  if (opts.all)
>>>> +    {
>>>> +      for (const frame_unwind *u : *unwinder_list)
>>>> +	u->set_enabled (enable);
>>>> +
>>>> +      reinit_frame_cache ();
>>>> +      return;
>>>> +    }
>>>> +
>>>> +  /* If user entered a specific unwinder name, handle it here.  If the
>>>> +     unwinder is already at the expected state, error out.  */
>>>> +  if (!opts.unwinder_name.empty ())
>>>> +    {
>>>> +      bool did_something = false;
>>>> +      for (const frame_unwind *unwinder : *unwinder_list)
>>>> +	{
>>>> +	  if (strcasecmp (unwinder->name (),
>>>> +			  opts.unwinder_name.c_str ()) == 0)
>>>> +	    {
>>>> +	      if (unwinder->enabled () == enable)
>>>> +		{
>>>> +		  if (unwinder->enabled ())
>>>> +		    error (_("unwinder %s is already enabled"),
>>>> +			     unwinder->name ());
>>>> +		  else
>>>> +		    error (_("unwinder %s is already disabled"),
>>>> +			     unwinder->name ());
>>>> +		}
>>>> +	      unwinder->set_enabled (enable);
>>>> +
>>>> +	      did_something = true;
>>>> +	      break;
>>>> +	    }
>>>> +	}
>>>> +      if (!did_something)
>>>> +	error (_("couldn't find unwinder named %s"),
>>>> +	       opts.unwinder_name.c_str ());
>>>> +    }
>>>> +  else
>>>> +    {
>>>> +      if (opts.unwinder_class == nullptr)
>>>> +	opts.unwinder_class = args;
>>>> +      enum frame_unwind_class dclass = str_to_frame_unwind_class
>>>> +	(opts.unwinder_class);
>>>> +      for (auto unwinder: *unwinder_list)
>>> I think you should stick with the form you use earlier:
>>>
>>>     (const frame_unwind *unwinder : *unwinder_list)
>>>
>>> But if you really want to stick with auto, then I think:
>>>
>>>    (const auto &unwinder : *unwinder_list)
>>>
>>> would be OK.
>>>
>>>
>>>> +	{
>>>> +	  if (unwinder->unwinder_class () == dclass)
>>>> +	    unwinder->set_enabled (enable);
>>>> +	}
>>>> +    }
>>>> +
>>>> +  reinit_frame_cache ();
>>>> +}
>>>> +
>>>> +/* Implement "maint frame-unwinder 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-unwinder enable" 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 ()
>>>> @@ -423,4 +593,45 @@ _initialize_frame_unwind ()
>>>>    List the frame unwinders currently in effect.\n\
>>>>    Unwinders are listed 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 [-all | -name NAME | [-class] CLASS]\n\
>>>> +\n\
>>>> +These are the meanings of the options:\n\
>>>> +\t-all    - All available unwinders will be disabled\n\
>>>> +\t-name   - NAME is the exact name of the frame unwinder to be disabled\n\
>>>> +\t-class  - CLASS is the class of unwinders to be disabled. Valid classes are:\n\
>>>> +\t\tGDB       - Unwinders added by GDB core;\n\
>>>> +\t\tEXTENSION - Unwinders added by extension languages;\n\
>>>> +\t\tDEBUGINFO - Unwinders that handle debug information;\n\
>>>> +\t\tARCH      - Unwinders that use architecture-specific information;\n\
>>>> +\n\
>>>> +UNWINDER and NAME are 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 [-all | -name NAME | [-class] CLASS]\n\
>>>> +\n\
>>>> +These are the meanings of the options:\n\
>>>> +\t-all    - All available unwinders will be enabled\n\
>>>> +\t-name   - NAME is the exact name of the frame unwinder to be enabled\n\
>>>> +\t-class  - CLASS is the class of unwinders to be enabled. Valid classes are:\n\
>>>> +\t\tGDB       - Unwinders added by GDB core;\n\
>>>> +\t\tEXTENSION - Unwinders added by extension languages;\n\
>>>> +\t\tDEBUGINFO - Unwinders that handle debug information;\n\
>>>> +\t\tARCH      - Unwinders that use architecture-specific information;\n\
>>>> +\n\
>>>> +UNWINDER and NAME are case insensitive."),
>>>> +	   &maint_frame_unwinder);
>>>>    }
>>> At the bottom of this email you'll find a small patch which applies on
>>> top of this and adds some basic command completion.  It doesn't complete
>>> the NAMEs, I'm not sure if there's a good way to do that (yet).  It also
>>> doesn't switch the help text to use the %OPTIONS% mechanism, I tried
>>> that, but I think there may be some bugs with the formatting (I'll look
>>> into it), so I've left that off.
>>>
>>> You don't have to, we can add completion later, but I do think it would
>>> be good to merge the patch below with this one.  But it is up to you.
>>> Take a look and see what you think.
>>>
>>>> diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
>>>> index 13f18618d24..adf8b47982c 100644
>>>> --- a/gdb/frame-unwind.h
>>>> +++ b/gdb/frame-unwind.h
>>>> @@ -192,6 +192,12 @@ class frame_unwind
>>>>      const frame_data *unwind_data () const
>>>>      { return m_unwind_data; }
>>>>    
>>>> +  bool enabled () const
>>>> +  { return m_enabled; }
>>>> +
>>>> +  void set_enabled (bool state) const
>>>> +  { m_enabled = state; }
>>>> +
>>>>      /* Default stop_reason implementation.  It reports NO_REASON, unless the
>>>>         frame is the outermost.  */
>>>>    
>>>> @@ -245,6 +251,9 @@ class frame_unwind
>>>>      frame_unwind_class m_unwinder_class;
>>>>    
>>>>      const frame_data *m_unwind_data;
>>>> +
>>>> +  /* Whether this unwinder can be used when sniffing.  */
>>>> +  mutable bool m_enabled = true;
>>>>    };
>>>>    
>>>>    /* This is a legacy version of the frame unwinder.  The original struct
>>>> diff --git a/gdb/testsuite/gdb.base/frame-info-consistent.exp b/gdb/testsuite/gdb.base/frame-info-consistent.exp
>>>> index fe0cfad95bc..4f483111a91 100644
>>>> --- a/gdb/testsuite/gdb.base/frame-info-consistent.exp
>>>> +++ b/gdb/testsuite/gdb.base/frame-info-consistent.exp
>>>> @@ -95,11 +95,11 @@ proc compare_frames {frames} {
>>>>        }
>>>>    }
>>>>    
>>>> -proc test {dwarf_unwinder} {
>>>> +proc test {enable} {
>>>>    
>>>>        clean_restart $::binfile
>>>>    
>>>> -    gdb_test_no_output "maint set dwarf unwinder $dwarf_unwinder"
>>>> +    gdb_test_no_output "maint frame-unwinder $enable DEBUGINFO"
>>>>    
>>>>        if {![runto_main]} {
>>>>    	return 0
>>>> @@ -134,6 +134,6 @@ proc test {dwarf_unwinder} {
>>>>        }
>>>>    }
>>>>    
>>>> -foreach_with_prefix dwarf {"off" "on"} {
>>>> -    test $dwarf
>>>> +foreach_with_prefix action {"disable" "enable"} {
>>>> +    test $action
>>>>    }
>>>> 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..bbcfb01316e
>>>> --- /dev/null
>>>> +++ b/gdb/testsuite/gdb.base/frame-unwind-disable.c
>>>> @@ -0,0 +1,22 @@
>>>> +/* This testcase is part of GDB, the GNU debugger.
>>>> +
>>>> +   Copyright 2024 Free Software Foundation, Inc.
>>> Remember to update the copyright date when rebasing.
>>>
>>>> +
>>>> +   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..f4cad9a47fd
>>>> --- /dev/null
>>>> +++ b/gdb/testsuite/gdb.base/frame-unwind-disable.exp
>>>> @@ -0,0 +1,137 @@
>>>> +# Copyright 2024 Free Software Foundation, Inc.
>>> And again with the date.
>>>
>>>> +
>>>> +# 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
>>> You can drop '.c' here, it's the default.
>>>
>>>> +
>>>> +# 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 ""} } {
>>>> +    set should_pass false
>>>> +    set command "maint info frame-unwinders"
>>>> +    if {${testname} == ""} {
>>>> +	set testname "checking state ${state} for ${unwinder_name}"
>>>> +    }
>>>> +    gdb_test_multiple "${command}" "${testname}" -lbl {
>>>> +	-re "${unwinder_name}\\s+\\w+\\s+\\w+\\s+${state}\\s+(?=\r\n)" {
>>>> +	    set should_pass true
>>>> +	    exp_continue
>>>> +	}
>>>> +	-re "${command}" {
>>>> +	    exp_continue
>>>> +	}
>>>> +	-re "\\w+\\s+\\w+\\s+\\w+\\s+\\w+\\s+(?=\r\n)" {
>>>> +	    exp_continue
>>>> +	}
>>>> +	-re -wrap "" {
>>>> +	    gdb_assert ${should_pass} "${gdb_test_name}"
>>> I don't think the quotes around "${gdb_test_name}" are needed here?  In
>>> fact, there seem to be a few places where you place quotes around
>>> expansion of a single variable (e.g. "${command}" on the
>>> gdb_test_multiple line), I don't think any of these are needed.
>>>
>>> With these changes (and with or without the completion patch):
>> I applied all the changes, and I like the idea of adding completion, but
>> I wonder: should we add a test for completion? If so, I think this is
>> better saved for a later patch, so that we can work on a good test and
>> further improve the completion, because it would be nice to also
>> complete for "-class", "-name" and "-all", but I'm not sure how that'd
>> work yet.
>>
>> If a test isn't all that important we can add it now and do more work
>> later on. I also have an inlined styling question
> I'm not sure every function with completion has tests.  We do have some
> general tests that cover completion though.
>
> If it bothers you, then feel free to leave it out for now, I certainly
> don't want to delay this work going in just for completion of a
> maintenance command, you already switched over to the option system for
> me, which means we're better placed to add completion now or later, so
> I'm grateful for that.
>
> The function I proposed should be able to complete the '-class',
> '-name', and '-all' option names.  Additionally, the '-class' value
> should complete.  And the class name as an operand, if there are no
> options given.
Oh, I must have misread the code then. I'm happy adding the completion then!
>
> Completion of -name values is trickier, as the possible set of names can
> change by architecture, and the set of extensions loaded (I think).
> What we really need is for -name to be a "dynamic" enum option, where
> the dynamic property is provided by a callback which gets the list of
> possible enum values.  But such dynamic enums don't currently exist.
> But I might look into adding that feature once this patch lands.
Yeah, I remember we talked about this. It would be a really nice addition!
>
> Anyway, feel free to add the completion, or not.  If not I'll follow up
> afterwards.
>
>>> Approved-By: Andrew Burgess <aburgess@redhat.com>
>>>
>>> Thanks,
>>> Andrew
>>>
>>>> +	}
>>>> +    }
>>>> +}
>>>> +
>>>> +# Check if all unwinders of class UNWINDER_CLASS are in the state STATE.
>>>> +# STATE can be either Y or N.
>>>> +# UNWINDER_CLASS can be one of: GDB, ARCH, EXTENSION, DEBUGINFO.  It can
>>>> +# also be \\w+ if checking all unwinders.
>>>> +proc check_unwinder_class { unwinder_class state {testname ""} } {
>>>> +    set command "maint info frame-unwinders"
>>>> +    set should_pass true
>>>> +    if {$testname == ""} {
>>>> +	set testname "checking if ${unwinder_class} state is ${state}"
>>>> +    }
>>>> +    gdb_test_multiple "${command}" "${testname}" -lbl {
>>>> +	-re "^\[^\r\n\]+\\s+\\w+\\s+${unwinder_class}\\s+\(\[YN\]\)\\s+(?=\r\n)" {
>>>> +	    # The unwinder name may have multiple words, so we need to use the
>>>> +	    # more generic [^\r\n] pattern to match the unwinders.
>>>> +	    set cur_state $expect_out(1,string)
>>>> +	    if {$cur_state == $state} {
>>>> +		set should_pass false
>>>> +	    }
>>>> +	    exp_continue
>>>> +	}
>>>> +	-re "${command}" {
>>>> +	    exp_continue
>>>> +	}
>>>> +	-re -wrap "" {
>>>> +	    gdb_assert ${should_pass} "${gdb_test_name}"
>>>> +	}
>>>> +    }
>>>> +}
>>>> +
>>>> +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 "\\w+" "Y" \
>>>> +    "all unwinders enabled before any changes"
>>>> +gdb_test_no_output "maint frame-unwinder disable -all"
>>>> +check_unwinder_class "\\w+" "N" \
>>>> +    "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" \
>>>> +    ".*Required frame unwinder may have been disabled, see 'maint info frame-unwinders'.*" \
>>>> +    "no suitable unwinders should be found"
>>>> +
>>>> +# Reenable all unwinders.
>>>> +gdb_test_no_output "maint frame-unwinder enable -all"
>>>> +check_unwinder_class "\\w+" "Y" \
>>>> +    "all unwinders should be re-enabled"
>>>> +
>>>> +# Check that we are able to get backtraces once again.
>>>> +gdb_test "backtrace" ".0\\s+main .. at.*" \
>>>> +    "we can get usable backtraces again"
>>>> +
>>>> +# 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" "dummy should continue 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 "${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" "dummy should continue 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 "${class}" "Y"
>>>> +}
>>>> diff --git a/gdb/testsuite/gdb.base/maint.exp b/gdb/testsuite/gdb.base/maint.exp
>>>> index 2c58ffa36c5..ae7ad0787e6 100644
>>>> --- a/gdb/testsuite/gdb.base/maint.exp
>>>> +++ b/gdb/testsuite/gdb.base/maint.exp
>>>> @@ -454,10 +454,6 @@ gdb_test_no_output "maint info line-table xxx.c" \
>>>>    
>>>>    set timeout $oldtimeout
>>>>    
>>>> -# Just check that the DWARF unwinders control flag is visible.
>>>> -gdb_test "maint show dwarf unwinders" \
>>>> -    "The DWARF stack unwinders are currently (on|off)\\."
>>>> -
>>>>    #============test help on maint commands
>>>>    
>>>>    test_prefix_command_help {"maint info" "maintenance info"} {
>>>> -- 
>>>> 2.47.0
>>> ---
>>>
>>> diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
>>> index 1417651108f..f3019b7ec18 100644
>>> --- a/gdb/frame-unwind.c
>>> +++ b/gdb/frame-unwind.c
>>> @@ -40,6 +40,7 @@ static const char * unwind_class_conversion[] =
>>>      "EXTENSION",
>>>      "DEBUGINFO",
>>>      "ARCH",
>>> +  nullptr
>>>    };
>>>    
>>>    /* Default sniffers, that must always be the first in the unwinder list,
>>> @@ -579,6 +580,29 @@ enable_disable_frame_unwinders (const char *args, int from_tty, bool enable)
>>>      reinit_frame_cache ();
>>>    }
>>>    
>>> +/* Completer for the "maint frame-unwinder enable|disable" commands.  */
>>> +
>>> +static void
>>> +enable_disable_frame_unwinders_completer (struct cmd_list_element *ignore,
>>> +					  completion_tracker &tracker,
>>> +					  const char *text,
>>> +					  const char */*word*/)
>> Would it be alright to write "const char * /*word*/" ? the */* is
>> confusing my editor and it looks like malformed comments, and if I add
>> the space, the red highlights go away.
> Sure.  I just copied this from elsewhere in GDB :)
>
> Thanks,
> Andrew
>
>
>> -- 
>> Cheers,
>> Guinevere Larsen
>> She/Her/Hers
>>
>>> +{
>>> +  maint_frame_unwind_options opts;
>>> +  const auto group = make_frame_unwind_enable_disable_options (&opts);
>>> +
>>> +  const char *start = text;
>>> +  if (gdb::option::complete_options
>>> +      (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
>>> +    return;
>>> +
>>> +  /* Only complete a class name as a stand-alone operand when no options
>>> +     are given.  */
>>> +  if (start == text)
>>> +    complete_on_enum (tracker, unwind_class_conversion, text, text);
>>> +  return;
>>> +}
>>> +
>>>    /* Implement "maint frame-unwinder disable" command.  */
>>>    static void
>>>    maintenance_disable_frame_unwinders (const char *args, int from_tty)
>>> @@ -613,8 +637,9 @@ Unwinders are listed starting with the highest priority."),
>>>    			_("Commands handling frame unwinders."),
>>>    			&maint_frame_unwinder, 0, &maintenancelist);
>>>    
>>> -  add_cmd ("disable", class_maintenance, maintenance_disable_frame_unwinders,
>>> -	   _("\
>>> +  cmd_list_element *c
>>> +    = add_cmd ("disable", class_maintenance, maintenance_disable_frame_unwinders,
>>> +	       _("\
>>>    Disable one or more frame unwinder(s).\n\
>>>    Usage: maint frame-unwinder disable [-all | -name NAME | [-class] CLASS]\n\
>>>    \n\
>>> @@ -628,10 +653,13 @@ These are the meanings of the options:\n\
>>>    \t\tARCH      - Unwinders that use architecture-specific information;\n\
>>>    \n\
>>>    UNWINDER and NAME are case insensitive."),
>>> -	   &maint_frame_unwinder);
>>> +	       &maint_frame_unwinder);
>>> +  set_cmd_completer_handle_brkchars (c,
>>> +				     enable_disable_frame_unwinders_completer);
>>>    
>>> -  add_cmd ("enable", class_maintenance, maintenance_enable_frame_unwinders,
>>> -	   _("\
>>> +  c =
>>> +    add_cmd ("enable", class_maintenance, maintenance_enable_frame_unwinders,
>>> +	     _("\
>>>    Enable one or more frame unwinder(s).\n\
>>>    Usage: maint frame-unwinder enable [-all | -name NAME | [-class] CLASS]\n\
>>>    \n\
>>> @@ -645,5 +673,7 @@ These are the meanings of the options:\n\
>>>    \t\tARCH      - Unwinders that use architecture-specific information;\n\
>>>    \n\
>>>    UNWINDER and NAME are case insensitive."),
>>> -	   &maint_frame_unwinder);
>>> +	     &maint_frame_unwinder);
>>> +  set_cmd_completer_handle_brkchars (c,
>>> +				     enable_disable_frame_unwinders_completer);
>>>    }
>>>

-- 
Cheers,
Guinevere Larsen
She/Her/Hers


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

* Re: [PATCH v8 0/5] Modernize frame unwinders and add disable feature
  2024-12-10 19:51 [PATCH v8 0/5] Modernize frame unwinders and add disable feature Guinevere Larsen
                   ` (5 preceding siblings ...)
  2025-01-07 12:11 ` [PING][PATCH v8 0/5] Modernize frame unwinders and add disable feature Guinevere Larsen
@ 2025-01-17 14:49 ` Guinevere Larsen
  6 siblings, 0 replies; 24+ messages in thread
From: Guinevere Larsen @ 2025-01-17 14:49 UTC (permalink / raw)
  To: gdb-patches

Now that Andrew approved all patches, I pushed this series!

If there are any issues, please let me know!

-- 
Cheers,
Guinevere Larsen
She/Her/Hers

On 12/10/24 4:51 PM, 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.
>
> The first patch is just a minor change, storing frame unwinders in a
> vector instead of through an unwinder table accessible, still accessed
> throught the registry subsystem. This isn't required (like I originally
> thought it was), but it does make the whole system more readable in my
> opinion.
>
> Patch 2 adds a new field for frame unwinders, their "class", which
> roughly correlates to which area of GDB adds that unwinder.  They were
> chosen rather arbitrarily based on my understanding, where the unwinder
> is added and its name. This class will be used to simplify bulk
> disabling of unwinders introduced by patch 4.
>
> 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. Also, as
> noted by Thiago Bauermann, no frame unwinders that use the legacy class
> ever pass a frame_data pointer, so that field was removed from those
> unwinders.
>
> Patch 4 adds the possibility to disable unwinders based on their name or
> class, and disable all unwinders at once if desired. It also reverts
> commit 3c3bb0580be0 since that patch pointed out that if a more generic
> system was implemented it could be reverted.
>
> Patch 5 adds the test I was trying to upstream all along.
>
> All the documentation has been approved by Eli already. Patches 2 and 3
> were already approved by Simon.
>
> Changes for v8:
> * Simplified the loop printing the table for "maint info
>    frame-unwinders".
> * Used gdb::option to parse the options for "maint frame-unwinder
>    disable"
> * Changed error message when no unwinder is found but some unwinders
>    were disabled
> * Cosmetic fixes.
>
> Changes for v7:
> * Moved the std::vector back to being accessed through the registry system
>      Removed the approvals from patch 1 because I think this change is
>      reasonably big, and needs another review.
> * Several cosmetic changes to the code, following feedback from Simon
>    and Thiago.
> * Fixed the test from patch 4, following Thiago's suggestion
> * Users can no longer specify the unwinder class with the
>    FRAME_UNWINDER_ prefix
>
> Changes for v6:
> * Patches 1 and 2 were approved by Simon
> * Several cosmetic changes to patch 3 and 4
> * New dealloc_cache default implementation (noop), and frame_unwind_legacy
>    defaults to it if no function pointer is given.
> * New prev_arch default implementation (getting the current arch), and
>    frame_unwind_legacy defaults to it if no pointer is given
> * Renamed "sniffer" to "sniff", to make it more clear what the method
>    does
> * Fixed some problems with the testcase.
>
> Changes for v5:
> * Reverted commit 3c3bb0580be0, with patch 4 following the advice on
>    that commit
> * on patch 3, some of the original functions were converted into the
>    classes' methods instead of just passing the call along.
> * some methods of frame_unwind_legacy were switched to be purely
>    virtual
>
> Changes for v4:
> * Rebase on current master (No changes needed)
> * improved documentation of unwinder classes follow Eli feedback
> * added KFAIL to new test on patch 5 if running on 32 bit arm
>
> Changes for v3:
> * Fixed Linaro CI issue in 32 bit arm
> * Fixed the few comments from Eli.
> * Documented addition of unwinder class in the patch that adds it.
>
> Changes for v2:
> * Added back the test that checks if GDB can handle a mix of CUs with
>    and without debuginfo in a single backtrace.
> * Fixed all the nitpicks for style.
> * Removed FRAME_UNWIND_ prefix when talking about classes
> * Thoroughly changed the documentation, to be more readable
>
> Guinevere Larsen (5):
>    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/testsuite: Test for a backtrace through object without debuginfo
>
>   gdb/NEWS                                      |  21 +
>   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                             |   7 +-
>   gdb/arc-tdep.c                                |  10 +-
>   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                           |  64 +--
>   gdb/dummy-frame.c                             |   8 +-
>   gdb/dummy-frame.h                             |   2 +-
>   gdb/dwarf2/frame-tailcall.c                   |   9 +-
>   gdb/dwarf2/frame-tailcall.h                   |   2 +-
>   gdb/dwarf2/frame.c                            |  46 +-
>   gdb/dwarf2/frame.h                            |   6 -
>   gdb/frame-unwind.c                            | 420 ++++++++++++++----
>   gdb/frame-unwind.h                            | 173 +++++++-
>   gdb/frame.c                                   |  21 +-
>   gdb/frv-linux-tdep.c                          |   6 +-
>   gdb/frv-tdep.c                                |   5 +-
>   gdb/ft32-tdep.c                               |   6 +-
>   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/or1k-tdep.c                               |   7 +-
>   gdb/ppc-fbsd-tdep.c                           |   5 +-
>   gdb/ppc-obsd-tdep.c                           |   5 +-
>   gdb/python/py-unwind.c                        |  61 ++-
>   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 +-
>   .../backtrace-through-cu-nodebug-caller.c     |  28 ++
>   .../backtrace-through-cu-nodebug-main.c       |  32 ++
>   .../gdb.base/backtrace-through-cu-nodebug.exp |  95 ++++
>   .../gdb.base/frame-info-consistent.exp        |   8 +-
>   gdb/testsuite/gdb.base/frame-unwind-disable.c |  22 +
>   .../gdb.base/frame-unwind-disable.exp         | 137 ++++++
>   gdb/testsuite/gdb.base/maint.exp              |   4 -
>   gdb/tic6x-tdep.c                              |  12 +-
>   gdb/tilegx-tdep.c                             |   5 +-
>   gdb/tramp-frame.c                             |  70 ++-
>   gdb/v850-tdep.c                               |   5 +-
>   gdb/vax-tdep.c                                |   6 +-
>   gdb/windows-tdep.c                            |  35 +-
>   gdb/windows-tdep.h                            |  16 +-
>   gdb/xstormy16-tdep.c                          |   5 +-
>   gdb/xtensa-tdep.c                             |   7 +-
>   gdb/z80-tdep.c                                |   7 +-
>   97 files changed, 1339 insertions(+), 551 deletions(-)
>   create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.c
>   create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.c
>   create mode 100644 gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
>   create mode 100644 gdb/testsuite/gdb.base/frame-unwind-disable.c
>   create mode 100644 gdb/testsuite/gdb.base/frame-unwind-disable.exp
>


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

* Re: [PATCH v8 5/5] gdb/testsuite: Test for a backtrace through object without debuginfo
  2025-01-16 14:37   ` Andrew Burgess
  2025-01-16 18:42     ` Guinevere Larsen
@ 2025-01-18  8:07     ` Tom de Vries
  2025-01-20 12:26       ` [PATCH] gdb/testsuite: Fix file location for gdb.base/backtrace-through-cu-nodebug Guinevere Larsen
  1 sibling, 1 reply; 24+ messages in thread
From: Tom de Vries @ 2025-01-18  8:07 UTC (permalink / raw)
  To: Andrew Burgess, Guinevere Larsen, gdb-patches
  Cc: Jan Kratochvil, Thiago Jung Bauermann

On 1/16/25 15:37, Andrew Burgess wrote:
> +	if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
> +	     "${srcdir}/${subdir}/${objcallerfile}" \


I build gdb and ran the testsuite, and got:
...
$ git sti
On branch master
Your branch is up to date with 'origin/master'.

Ignored files:
   (use "git add -f <file>..." to include in what will be committed)
	gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-caller.o
	gdb/testsuite/gdb.base/backtrace-through-cu-nodebug-main.o

nothing to commit, working tree clean
$
...

The object files should not be generated alongside the sources.

Thanks,
- Tom

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

* [PATCH] gdb/testsuite: Fix file location for gdb.base/backtrace-through-cu-nodebug
  2025-01-18  8:07     ` Tom de Vries
@ 2025-01-20 12:26       ` Guinevere Larsen
  2025-01-20 12:46         ` Tom de Vries
  0 siblings, 1 reply; 24+ messages in thread
From: Guinevere Larsen @ 2025-01-20 12:26 UTC (permalink / raw)
  To: gdb-patches; +Cc: Guinevere Larsen, Tom de Vries

The newly added test gdb.base/backtrace-through-cu-nodebug.exp had a
problem in the call to gdb_comiple, that caused the .o files to be
outputted in the GDB file tree. This commit fixes the issues in the calls.

Reported-By: Tom de Vries <tdevries@suse.de>
---

Hi Tom,

Sorry about that, this patch should fix it. I'm not sure if this counts
as obvious, so I haven't pushed it yet, do let me know if this is ok :)

---
 .../gdb.base/backtrace-through-cu-nodebug.exp      | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
index d6e686539c9..53bf642a92c 100644
--- a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
+++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
@@ -17,8 +17,8 @@
 # trace goes through a function with no debug information.
 
 standard_testfile -caller.c -main.c
-set objmainfile ${testfile}-main.o
-set objcallerfile ${testfile}-caller.o
+set objmainfile [standard_output_file ${testfile}-main.o]
+set objcallerfile [standard_output_file ${testfile}-caller.o]
 
 # Recompile the inferior with or without CFI information, then run the
 # inferior until the point where the important test starts.
@@ -28,7 +28,7 @@ proc prepare_test {has_cfi} {
     if {$has_cfi} {
 	set extension "cfi"
 	if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
-	     "${srcdir}/${subdir}/${objcallerfile}" \
+	     "${objcallerfile}" \
 	     object [list {additional_flags=-fomit-frame-pointer \
 		 -funwind-tables -fasynchronous-unwind-tables}]] != "" } {
 	    untested "couldn't compile with cfi"
@@ -37,7 +37,7 @@ proc prepare_test {has_cfi} {
     } else {
 	set extension "no-cfi"
 	if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
-	     "${srcdir}/${subdir}/${objcallerfile}" \
+	     "${objcallerfile}" \
 	     object [list {additional_flags=-fomit-frame-pointer \
 		 -fno-unwind-tables \
 		 -fno-asynchronous-unwind-tables}]] != "" } {
@@ -45,8 +45,8 @@ proc prepare_test {has_cfi} {
 	    return false
       }
     }
-    if {[gdb_compile [list "${srcdir}/${subdir}/${objmainfile}" \
-	    "${srcdir}/${subdir}/${objcallerfile}"] \
+    if {[gdb_compile [list "${objmainfile}" \
+	    "${objcallerfile}"] \
 	    "${binfile}-${extension}" binfile {}] != ""} {
 	untested "couldn't link object files"
 	return false
@@ -66,7 +66,7 @@ proc prepare_test {has_cfi} {
 }
 
 if {[gdb_compile "${srcdir}/${subdir}/${srcfile2}" \
-	"${srcdir}/${subdir}/${objmainfile}" \
+	"${objmainfile}" \
 	object {debug}] != "" } {
     untested "couldn't compile main file"
     return
-- 
2.48.1


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

* Re: [PATCH] gdb/testsuite: Fix file location for gdb.base/backtrace-through-cu-nodebug
  2025-01-20 12:26       ` [PATCH] gdb/testsuite: Fix file location for gdb.base/backtrace-through-cu-nodebug Guinevere Larsen
@ 2025-01-20 12:46         ` Tom de Vries
  2025-01-20 12:48           ` Guinevere Larsen
  0 siblings, 1 reply; 24+ messages in thread
From: Tom de Vries @ 2025-01-20 12:46 UTC (permalink / raw)
  To: Guinevere Larsen, gdb-patches

On 1/20/25 13:26, Guinevere Larsen wrote:
> The newly added test gdb.base/backtrace-through-cu-nodebug.exp had a
> problem in the call to gdb_comiple, that caused the .o files to be

Hi Gwen,

gdb_comiple -> gdb_compile

> outputted in the GDB file tree. This commit fixes the issues in the calls.
> 
> Reported-By: Tom de Vries <tdevries@suse.de>

I've tested this, and checked that it fixes the problem.

LGTM.

Approved-By: Tom de Vries <tdevries@suse.de>

Thanks,
- Tom

> ---
> 
> Hi Tom,
> 
> Sorry about that, this patch should fix it. I'm not sure if this counts
> as obvious, so I haven't pushed it yet, do let me know if this is ok :)
> 
> ---
>   .../gdb.base/backtrace-through-cu-nodebug.exp      | 14 +++++++-------
>   1 file changed, 7 insertions(+), 7 deletions(-)
> 
> diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
> index d6e686539c9..53bf642a92c 100644
> --- a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
> +++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
> @@ -17,8 +17,8 @@
>   # trace goes through a function with no debug information.
>   
>   standard_testfile -caller.c -main.c
> -set objmainfile ${testfile}-main.o
> -set objcallerfile ${testfile}-caller.o
> +set objmainfile [standard_output_file ${testfile}-main.o]
> +set objcallerfile [standard_output_file ${testfile}-caller.o]
>   
>   # Recompile the inferior with or without CFI information, then run the
>   # inferior until the point where the important test starts.
> @@ -28,7 +28,7 @@ proc prepare_test {has_cfi} {
>       if {$has_cfi} {
>   	set extension "cfi"
>   	if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
> -	     "${srcdir}/${subdir}/${objcallerfile}" \
> +	     "${objcallerfile}" \
>   	     object [list {additional_flags=-fomit-frame-pointer \
>   		 -funwind-tables -fasynchronous-unwind-tables}]] != "" } {
>   	    untested "couldn't compile with cfi"
> @@ -37,7 +37,7 @@ proc prepare_test {has_cfi} {
>       } else {
>   	set extension "no-cfi"
>   	if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
> -	     "${srcdir}/${subdir}/${objcallerfile}" \
> +	     "${objcallerfile}" \
>   	     object [list {additional_flags=-fomit-frame-pointer \
>   		 -fno-unwind-tables \
>   		 -fno-asynchronous-unwind-tables}]] != "" } {
> @@ -45,8 +45,8 @@ proc prepare_test {has_cfi} {
>   	    return false
>         }
>       }
> -    if {[gdb_compile [list "${srcdir}/${subdir}/${objmainfile}" \
> -	    "${srcdir}/${subdir}/${objcallerfile}"] \
> +    if {[gdb_compile [list "${objmainfile}" \
> +	    "${objcallerfile}"] \
>   	    "${binfile}-${extension}" binfile {}] != ""} {
>   	untested "couldn't link object files"
>   	return false
> @@ -66,7 +66,7 @@ proc prepare_test {has_cfi} {
>   }
>   
>   if {[gdb_compile "${srcdir}/${subdir}/${srcfile2}" \
> -	"${srcdir}/${subdir}/${objmainfile}" \
> +	"${objmainfile}" \
>   	object {debug}] != "" } {
>       untested "couldn't compile main file"
>       return


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

* Re: [PATCH] gdb/testsuite: Fix file location for gdb.base/backtrace-through-cu-nodebug
  2025-01-20 12:46         ` Tom de Vries
@ 2025-01-20 12:48           ` Guinevere Larsen
  0 siblings, 0 replies; 24+ messages in thread
From: Guinevere Larsen @ 2025-01-20 12:48 UTC (permalink / raw)
  To: Tom de Vries, gdb-patches

On 1/20/25 9:46 AM, Tom de Vries wrote:
> On 1/20/25 13:26, Guinevere Larsen wrote:
>> The newly added test gdb.base/backtrace-through-cu-nodebug.exp had a
>> problem in the call to gdb_comiple, that caused the .o files to be
>
> Hi Gwen,
>
> gdb_comiple -> gdb_compile
>
>> outputted in the GDB file tree. This commit fixes the issues in the 
>> calls.
>>
>> Reported-By: Tom de Vries <tdevries@suse.de>
>
> I've tested this, and checked that it fixes the problem.
>
> LGTM.
>
> Approved-By: Tom de Vries <tdevries@suse.de>
Thanks for the quick review, I've pushed the patch!

-- 
Cheers,
Guinevere Larsen
She/Her/Hers

>
> Thanks,
> - Tom
>
>> ---
>>
>> Hi Tom,
>>
>> Sorry about that, this patch should fix it. I'm not sure if this counts
>> as obvious, so I haven't pushed it yet, do let me know if this is ok :)
>>
>> ---
>>   .../gdb.base/backtrace-through-cu-nodebug.exp      | 14 +++++++-------
>>   1 file changed, 7 insertions(+), 7 deletions(-)
>>
>> diff --git a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp 
>> b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
>> index d6e686539c9..53bf642a92c 100644
>> --- a/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
>> +++ b/gdb/testsuite/gdb.base/backtrace-through-cu-nodebug.exp
>> @@ -17,8 +17,8 @@
>>   # trace goes through a function with no debug information.
>>     standard_testfile -caller.c -main.c
>> -set objmainfile ${testfile}-main.o
>> -set objcallerfile ${testfile}-caller.o
>> +set objmainfile [standard_output_file ${testfile}-main.o]
>> +set objcallerfile [standard_output_file ${testfile}-caller.o]
>>     # Recompile the inferior with or without CFI information, then 
>> run the
>>   # inferior until the point where the important test starts.
>> @@ -28,7 +28,7 @@ proc prepare_test {has_cfi} {
>>       if {$has_cfi} {
>>       set extension "cfi"
>>       if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
>> -         "${srcdir}/${subdir}/${objcallerfile}" \
>> +         "${objcallerfile}" \
>>            object [list {additional_flags=-fomit-frame-pointer \
>>            -funwind-tables -fasynchronous-unwind-tables}]] != "" } {
>>           untested "couldn't compile with cfi"
>> @@ -37,7 +37,7 @@ proc prepare_test {has_cfi} {
>>       } else {
>>       set extension "no-cfi"
>>       if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" \
>> -         "${srcdir}/${subdir}/${objcallerfile}" \
>> +         "${objcallerfile}" \
>>            object [list {additional_flags=-fomit-frame-pointer \
>>            -fno-unwind-tables \
>>            -fno-asynchronous-unwind-tables}]] != "" } {
>> @@ -45,8 +45,8 @@ proc prepare_test {has_cfi} {
>>           return false
>>         }
>>       }
>> -    if {[gdb_compile [list "${srcdir}/${subdir}/${objmainfile}" \
>> -        "${srcdir}/${subdir}/${objcallerfile}"] \
>> +    if {[gdb_compile [list "${objmainfile}" \
>> +        "${objcallerfile}"] \
>>           "${binfile}-${extension}" binfile {}] != ""} {
>>       untested "couldn't link object files"
>>       return false
>> @@ -66,7 +66,7 @@ proc prepare_test {has_cfi} {
>>   }
>>     if {[gdb_compile "${srcdir}/${subdir}/${srcfile2}" \
>> -    "${srcdir}/${subdir}/${objmainfile}" \
>> +    "${objmainfile}" \
>>       object {debug}] != "" } {
>>       untested "couldn't compile main file"
>>       return
>


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

end of thread, other threads:[~2025-01-20 12:49 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-12-10 19:51 [PATCH v8 0/5] Modernize frame unwinders and add disable feature Guinevere Larsen
2024-12-10 19:51 ` [PATCH v8 1/5] gdb: make gdbarch store a vector of frame unwinders Guinevere Larsen
2025-01-14 14:28   ` Andrew Burgess
2025-01-14 20:34     ` Guinevere Larsen
2024-12-10 19:51 ` [PATCH v8 2/5] gdb: add "unwinder class" to " Guinevere Larsen
2025-01-14 15:28   ` Andrew Burgess
2024-12-10 19:51 ` [PATCH v8 3/5] gdb: Migrate frame unwinders to use C++ classes Guinevere Larsen
2025-01-14 17:13   ` Andrew Burgess
2024-12-10 19:51 ` [PATCH v8 4/5] gdb: introduce ability to disable frame unwinders Guinevere Larsen
2025-01-16 12:06   ` Andrew Burgess
2025-01-17 12:40     ` Guinevere Larsen
2025-01-17 13:55       ` Andrew Burgess
2025-01-17 14:47         ` Guinevere Larsen
2025-01-16 16:22   ` Andrew Burgess
2024-12-10 19:51 ` [PATCH v8 5/5] gdb/testsuite: Test for a backtrace through object without debuginfo Guinevere Larsen
2025-01-16 14:37   ` Andrew Burgess
2025-01-16 18:42     ` Guinevere Larsen
2025-01-17 13:58       ` Andrew Burgess
2025-01-18  8:07     ` Tom de Vries
2025-01-20 12:26       ` [PATCH] gdb/testsuite: Fix file location for gdb.base/backtrace-through-cu-nodebug Guinevere Larsen
2025-01-20 12:46         ` Tom de Vries
2025-01-20 12:48           ` Guinevere Larsen
2025-01-07 12:11 ` [PING][PATCH v8 0/5] Modernize frame unwinders and add disable feature Guinevere Larsen
2025-01-17 14:49 ` [PATCH " Guinevere Larsen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox