diff --git a/gdb/NEWS b/gdb/NEWS
index feb3a37393a..da83db81112 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -54,6 +54,13 @@ disable missing-debug-handler LOCUS HANDLER
 maintenance info linux-lwps
   List all LWPs under control of the linux-nat target.
 
+maintenance frame-unwinder disable [-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.
+
 set remote thread-options-packet
 show remote thread-options-packet
   Set/show the use of the thread options packet.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 727f9275bfb..2921f2007ab 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -42022,6 +42022,30 @@ architecture, then enabling this flag does not cause them to be used.
 @item maint info frame-unwinders
 List the frame unwinders currently in effect, starting with the highest priority.
 
+@kindex maint frame-unwinder disable
+@kindex maint frame-unwinder enable
+@item maint frame-unwinder disable [@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/frame-unwind.c b/gdb/frame-unwind.c
index fd3d3b378ab..0806770f5a3 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -87,6 +87,23 @@ frame_unwinder_class_str (frame_unwind_class uclass)
   return location->second;
 }
 
+/* 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)
+{
+  const char *prefix = "FRAME_UNWIND_";
+  /* Skip the prefix if present.  */
+  if (strncasecmp (class_str, prefix, strlen(prefix)) == 0)
+    class_str += strlen(prefix);
+  for (const auto& it : unwind_class_conversion)
+    {
+      if (strcasecmp(it.second, class_str) == 0)
+	return it.first;
+    }
+  error (_("Unknown frame unwind class: %s"), class_str);
+}
+
 void
 frame_unwind_prepend_unwinder (struct gdbarch *gdbarch,
 				const struct frame_unwind *unwinder)
@@ -184,25 +201,47 @@ frame_unwind_find_by_frame (const frame_info_ptr &this_frame, void **this_cache)
 
   const struct frame_unwind *unwinder_from_target;
 
+  /* If we see a disabled unwinder, we assume some test is being run on
+     GDB, and we don't want to assert at the end of this function.  */
+  bool seen_disabled_unwinder = false;
+
   unwinder_from_target = target_get_unwinder ();
   if (unwinder_from_target != NULL
+      && unwinder_from_target->enabled ()
       && frame_unwind_try_unwinder (this_frame, this_cache,
 				   unwinder_from_target))
     return;
+  else if (unwinder_from_target != nullptr
+	   && !unwinder_from_target->enabled ())
+    seen_disabled_unwinder = true;
 
   unwinder_from_target = target_get_tailcall_unwinder ();
   if (unwinder_from_target != NULL
+      && unwinder_from_target->enabled ()
       && frame_unwind_try_unwinder (this_frame, this_cache,
 				   unwinder_from_target))
     return;
+  else if (unwinder_from_target != nullptr
+	   && !unwinder_from_target->enabled ())
+    seen_disabled_unwinder |= true;
 
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
   std::vector<const frame_unwind*> table = get_frame_unwind_table (gdbarch);
   for (auto unwinder: table)
-    if (frame_unwind_try_unwinder (this_frame, this_cache, unwinder))
-      return;
+    {
+      if (!unwinder->enabled ())
+	{
+	  seen_disabled_unwinder |= true;
+	  continue;
+	}
+      if (frame_unwind_try_unwinder (this_frame, this_cache, unwinder))
+	return;
+    }
 
-  internal_error (_("frame_unwind_find_by_frame failed"));
+  if (seen_disabled_unwinder)
+    error (_("frame_unwind_find_by_frame failed"));
+  else
+    internal_error (_("frame_unwind_find_by_frame failed"));
 }
 
 /* A default frame sniffer which always accepts the frame.  Used by
@@ -396,10 +435,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 (const struct frame_unwind* unwinder: table)
@@ -407,15 +447,88 @@ maintenance_info_frame_unwinders (const char *args, int from_tty)
       const char *name = unwinder->name ();
       const char *type = frame_type_str (unwinder->type ());
       const char *uclass = frame_unwinder_class_str (unwinder->unwinder_class ());
+      const char *enabled = (unwinder->enabled ()) ? "Y" : "N";
 
       ui_out_emit_list tuple_emitter (uiout, nullptr);
       uiout->field_string ("name", name);
       uiout->field_string ("type", type);
       uiout->field_string ("class", uclass);
+      uiout->field_string ("enabled", enabled);
       uiout->text ("\n");
     }
 }
 
+/* Helper function to both enable and disable frame unwinders.
+   if ENABLE is true, this call will be enabling unwinders,
+   otherwise the unwinders will be disabled.  */
+static void
+enable_disable_frame_unwinders (const char *args, int from_tty, bool enable)
+{
+  reinit_frame_cache ();
+  if (args == nullptr)
+    error (_("specify which frame unwinder(s) should be %s"),
+	   (enable)? "enabled" : "disabled");
+  struct gdbarch* gdbarch = current_inferior ()->arch ();
+  std::vector<const frame_unwind *> unwinder_list
+    = gdbarch_get_unwinder_list (gdbarch);
+
+  /* First see if the user wants to change all unwinders.  */
+  if (check_for_argument (&args, "-all"))
+    {
+      for (const frame_unwind *u : unwinder_list)
+	u->set_enabled (enable);
+      return;
+    }
+
+  /* If user entered a specific unwinder name, handle it here.  If the
+     unwinder is already at the expected state, error out.  */
+  if (check_for_argument (&args, "-name"))
+    {
+      bool did_something = false;
+      for (const frame_unwind *unwinder : unwinder_list)
+	{
+	  if (strcasecmp (unwinder->name (), args) == 0)
+	    {
+	      if (unwinder->enabled () == enable)
+		error (_("unwinder %s is already %s"),
+			 unwinder->name (),
+			 (unwinder->enabled ()) ? ("enabled") : ("disabled"));
+	      unwinder->set_enabled (enable);
+
+	      did_something = true;
+	      break;
+	    }
+	}
+      if (!did_something)
+	error (_("couldn't find unwinder named %s"),args);
+    }
+  else
+    {
+      /* Discard '-class' argument, if provided.  */
+      check_for_argument (&args, "-class");
+      enum frame_unwind_class dclass = str_to_frame_unwind_class (args);
+      for (auto unwinder: unwinder_list)
+	{
+	  if (unwinder->unwinder_class () == dclass)
+	    unwinder->set_enabled (enable);
+	}
+    }
+}
+
+/* Implement "maint frame-unwinders disable" command.  */
+static void
+maintenance_disable_frame_unwinders (const char *args, int from_tty)
+{
+  enable_disable_frame_unwinders (args, from_tty, false);
+}
+
+/* Implement "maint frame-unwinders disable" command.  */
+static void
+maintenance_enable_frame_unwinders (const char *args, int from_tty)
+{
+  enable_disable_frame_unwinders (args, from_tty, true);
+}
+
 void _initialize_frame_unwind ();
 void
 _initialize_frame_unwind ()
@@ -427,4 +540,37 @@ _initialize_frame_unwind ()
 	   _("List the frame unwinders currently in effect, "
 	     "starting with the highest priority."),
 	   &maintenanceinfolist);
+
+  /* Add "maint frame-unwinder disable/enable".  */
+  static struct cmd_list_element *maint_frame_unwinder;
+
+  add_basic_prefix_cmd ("frame-unwinder", class_maintenance,
+			_("Commands handling frame unwinders."),
+			&maint_frame_unwinder, 0, &maintenancelist);
+
+  add_cmd ("disable", class_maintenance, maintenance_disable_frame_unwinders,
+	   _("\
+Disable one or more frame unwinder(s).\n\
+Usage: maint frame-unwinder disable [-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 is to be disabled\n\
+\t-class  - CLASS is the class of unwinders to be disabled.\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 is to be enabled\n\
+\t-class  - CLASS is the class of unwinders to be enabled.\n\
+\n\
+UNWINDER and NAME are case insensitive."),
+	   & maint_frame_unwinder);
 }
diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h
index 62da34b6ee8..ffb131310dc 100644
--- a/gdb/frame-unwind.h
+++ b/gdb/frame-unwind.h
@@ -180,6 +180,9 @@ class frame_unwind
      unwinding.  */
   enum frame_unwind_class m_unwinder_class;
   const struct frame_data *m_unwind_data;
+
+  /* Whether this unwinder can be used when sniffing.  */
+  mutable bool m_enabled = true;
 public:
   frame_unwind (const char *name, frame_type type, frame_unwind_class uclass,
 		       const struct frame_data *data)
@@ -207,6 +210,16 @@ class frame_unwind
     return m_unwind_data;
   }
 
+  bool enabled () const
+  {
+    return m_enabled;
+  }
+
+  void set_enabled (bool state) const
+  {
+    m_enabled = state;
+  }
+
   /* Default stop_reason function.  It reports NO_REASON, unless the
      frame is the outermost.  */
   virtual enum unwind_stop_reason stop_reason (const frame_info_ptr &this_frame,
diff --git a/gdb/testsuite/gdb.base/frame-unwind-disable.c b/gdb/testsuite/gdb.base/frame-unwind-disable.c
new file mode 100644
index 00000000000..078517c011e
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-unwind-disable.c
@@ -0,0 +1,21 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2024 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int main ()
+{
+    return 0;
+}
diff --git a/gdb/testsuite/gdb.base/frame-unwind-disable.exp b/gdb/testsuite/gdb.base/frame-unwind-disable.exp
new file mode 100644
index 00000000000..4efc87ccfcf
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-unwind-disable.exp
@@ -0,0 +1,114 @@
+# Copyright 2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test multiple situations in which we may use the maintenance command to
+# disable and enable frame unwinders, and check that they really are
+# disabled when they say the are.
+
+standard_testfile .c
+
+# Proc to check if the unwinder of the given name is in the desired state.
+# STATE can be either Y or N.
+proc check_unwinder_state { unwinder_name state {testname ""} } {
+    if {${testname} == ""} {
+	set testname "checking state ${state} for ${unwinder_name}"
+    }
+    gdb_test "maint info frame-unwinders" \
+	".*${unwinder_name}\\s+\\w+\\s+\\w+.+${state}.*" \
+	"${testname}"
+}
+
+proc check_unwinder_class { unwinder_class state {testname ""} } {
+    set should_pass true
+    if {$testname == ""} {
+	set testname "checking if $unwinder_class state is $state"
+    }
+    gdb_test_multiple "maint info frame-unwinders" "collect for $testname" -lbl {
+	-re "^\\w+\\s+\\w+\\s+${unwinder_class}\\s+\(.\)\\s+" {
+	    set cur_state $expec_out(1, string)
+	    if {$cur_state != $state} {
+		set should_pass false
+	    }
+	    exp_continue
+	}
+	-re "${::gdb_prompt} $" {
+	    pass $gdb_test_name
+	}
+    }
+
+    gdb_assert {$should_pass == true} "$testname"
+}
+
+if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile} ]} {
+    return -1
+}
+
+if {![runto_main]} {
+    untested "couldn't run to main"
+    return
+}
+
+# Test disabling all unwinders
+check_unwinder_class "FRAME_UNWIND_\\w+" "Y" \
+    "Checking all unwinders before any changes"
+gdb_test_no_output "maint frame-unwinder disable -all"
+check_unwinder_class "FRAME_UNWIND_\\w+" "N" \
+    "Checking all unwinders were properly disabled"
+
+# Test if GDB can still make a backtrace once all unwinders are disabled.
+# It should be impossible.
+gdb_test "backtrace" ".*frame_unwind_find_by_frame failed.*" \
+    "confirm that no suitable unwinders are found"
+
+# Reenable all unwinders
+gdb_test_no_output "maint frame-unwinder enable -all"
+check_unwinder_class "FRAME_UNWIND_\\w+" "Y" \
+    "Checking all unwinders after re-enabling them all"
+
+# Check that we are able to get backtraces once again.
+gdb_test "backtrace" ".0\\s+main .. at.*" \
+    "confirm we now get usable backtraces"
+
+# Check if we can disable an unwinder based on the name.
+check_unwinder_state "dummy" "Y"
+gdb_test_no_output "maint frame-unwinder disable -name dummy"
+check_unwinder_state "dummy" "N"
+# And verify what happens if you try to disable it again.
+gdb_test "maint frame-unwinder disable -name dummy" \
+    "unwinder dummy is already disabled" \
+    "disable already disabled unwinder"
+check_unwinder_state "dummy" "N" "check if dummy continues disabled"
+
+foreach class {GDB ARCH DEBUGINFO EXTENSION} {
+    # Disable all unwinders of type CLASS, and check that the command worked.
+    gdb_test_no_output "maint frame-unwinder disable ${class}"
+    check_unwinder_class "FRAME_UNWINDER_${class}" "N"
+}
+
+# Now check if we are able to enable a single unwinder, and what happens if we
+# enable it twice.
+gdb_test_no_output "maint frame-unwinder enable -name dummy"
+check_unwinder_state "dummy" "Y" "successfully enabled dummy unwinder"
+gdb_test "maint frame-unwinder enable -name dummy" \
+    "unwinder dummy is already enabled" \
+    "enable already enabled unwinder"
+check_unwinder_state "dummy" "Y" "check if dummy continues enabled"
+
+foreach class {GDB ARCH DEBUGINFO EXTENSION} {
+    # Enable all unwinders of type CLASS, and check that the command worked.
+    # using "-class" option to ensure it works.  Should make no difference.
+    gdb_test_no_output "maint frame-unwinder enable -class ${class}"
+    check_unwinder_class "FRAME_UNWINDER_${class}" "Y"
+}
