gdb: use libbacktrace to create a better backtrace for fatal signals
authorAndrew Burgess <andrew.burgess@embecosm.com>
Wed, 11 Aug 2021 12:24:33 +0000 (13:24 +0100)
committerAndrew Burgess <andrew.burgess@embecosm.com>
Tue, 28 Sep 2021 11:21:22 +0000 (12:21 +0100)
GDB recently gained the ability to print a backtrace when a fatal
signal is encountered.  This backtrace is produced using the backtrace
and backtrace_symbols_fd API available in glibc.

However, in order for this API to actually map addresses to symbol
names it is required that the application (GDB) be compiled with
-rdynamic, which GDB is not by default.

As a result, the backtrace produced often looks like this:

  Fatal signal: Bus error
  ----- Backtrace -----
  ./gdb/gdb[0x80ec00]
  ./gdb/gdb[0x80ed56]
  /lib64/libc.so.6(+0x3c6b0)[0x7fc2ce1936b0]
  /lib64/libc.so.6(__poll+0x4f)[0x7fc2ce24da5f]
  ./gdb/gdb[0x15495ba]
  ./gdb/gdb[0x15489b8]
  ./gdb/gdb[0x9b794d]
  ./gdb/gdb[0x9b7a6d]
  ./gdb/gdb[0x9b943b]
  ./gdb/gdb[0x9b94a1]
  ./gdb/gdb[0x4175dd]
  /lib64/libc.so.6(__libc_start_main+0xf3)[0x7fc2ce17e1a3]
  ./gdb/gdb[0x4174de]
  ---------------------

This is OK if you have access to the exact same build of GDB, you can
manually map the addresses back to symbols, however, it is next to
useless if all you have is a backtrace copied into a bug report.

GCC uses libbacktrace for printing a backtrace when it encounters an
error.  In recent commits I added this library into the binutils-gdb
repository, and in this commit I allow this library to be used by
GDB.  Now (when GDB is compiled with debug information) the backtrace
looks like this:

  ----- Backtrace -----
  0x80ee08 gdb_internal_backtrace
   ../../src/gdb/event-top.c:989
  0x80ef0b handle_fatal_signal
   ../../src/gdb/event-top.c:1036
  0x7f24539dd6af ???
  0x7f2453a97a5f ???
  0x154976f gdb_wait_for_event
   ../../src/gdbsupport/event-loop.cc:613
  0x1548b6d _Z16gdb_do_one_eventv
   ../../src/gdbsupport/event-loop.cc:237
  0x9b7b02 start_event_loop
   ../../src/gdb/main.c:421
  0x9b7c22 captured_command_loop
   ../../src/gdb/main.c:481
  0x9b95f0 captured_main
   ../../src/gdb/main.c:1353
  0x9b9656 _Z8gdb_mainP18captured_main_args
   ../../src/gdb/main.c:1368
  0x4175ec main
   ../../src/gdb/gdb.c:32
  ---------------------

Which seems much more useful.

Use of libbacktrace is optional.  If GDB is configured with
--disable-libbacktrace then the libbacktrace directory will not be
built, and GDB will not try to use this library.  In this case GDB
would try to use the old backtrace and backtrace_symbols_fd API.

All of the functions related to writing the backtrace of GDB itself
have been moved into the new files gdb/by-utils.{c,h}.

gdb/Makefile.in
gdb/bt-utils.c [new file with mode: 0644]
gdb/bt-utils.h [new file with mode: 0644]
gdb/config.in
gdb/configure
gdb/configure.ac
gdb/event-top.c

index 320d3326a8170ea09c94ba51f4b842c4726f360c..5a3bb9522792ef44cbb1042d9b9483d3cf503faf 100644 (file)
@@ -248,6 +248,10 @@ GDBFLAGS =
 GNULIB_PARENT_DIR = ..
 include $(GNULIB_PARENT_DIR)/gnulib/Makefile.gnulib.inc
 
+# For libbacktrace.
+LIBBACKTRACE_INC=@LIBBACKTRACE_INC@
+LIBBACKTRACE_LIB=@LIBBACKTRACE_LIB@
+
 SUPPORT = ../gdbsupport
 LIBSUPPORT = $(SUPPORT)/libgdbsupport.a
 INCSUPPORT = -I$(srcdir)/.. -I..
@@ -614,9 +618,9 @@ INTERNAL_CFLAGS_BASE = \
        $(GLOBAL_CFLAGS) $(PROFILE_CFLAGS) \
        $(GDB_CFLAGS) $(OPCODES_CFLAGS) $(READLINE_CFLAGS) $(ZLIBINC) \
        $(BFD_CFLAGS) $(INCLUDE_CFLAGS) $(LIBDECNUMBER_CFLAGS) \
-       $(INTL_CFLAGS) $(INCGNU) $(INCSUPPORT) $(ENABLE_CFLAGS) \
-       $(INTERNAL_CPPFLAGS) $(SRCHIGH_CFLAGS) $(TOP_CFLAGS) $(PTHREAD_CFLAGS) \
-       $(DEBUGINFOD_CFLAGS)
+       $(INTL_CFLAGS) $(INCGNU) $(INCSUPPORT) $(LIBBACKTRACE_INC) \
+       $(ENABLE_CFLAGS) $(INTERNAL_CPPFLAGS) $(SRCHIGH_CFLAGS) \
+       $(TOP_CFLAGS) $(PTHREAD_CFLAGS) $(DEBUGINFOD_CFLAGS)
 INTERNAL_WARN_CFLAGS = $(INTERNAL_CFLAGS_BASE) $(GDB_WARN_CFLAGS)
 INTERNAL_CFLAGS = $(INTERNAL_WARN_CFLAGS) $(GDB_WERROR_CFLAGS)
 
@@ -637,12 +641,12 @@ INTERNAL_LDFLAGS = \
 # LIBIBERTY appears twice on purpose.
 CLIBS = $(SIM) $(READLINE) $(OPCODES) $(LIBCTF) $(BFD) $(ZLIB) \
         $(LIBSUPPORT) $(INTL) $(LIBIBERTY) $(LIBDECNUMBER) \
-       $(XM_CLIBS) $(GDBTKLIBS) \
+       $(XM_CLIBS) $(GDBTKLIBS)  $(LIBBACKTRACE_LIB) \
        @LIBS@ @GUILE_LIBS@ @PYTHON_LIBS@ \
        $(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) $(LIBIPT) \
        $(WIN32LIBS) $(LIBGNU) $(LIBGNU_EXTRA_LIBS) $(LIBICONV) \
        $(LIBMPFR) $(LIBGMP) $(SRCHIGH_LIBS) $(LIBXXHASH) $(PTHREAD_LIBS) \
-       $(DEBUGINFOD_LIBS)
+       $(DEBUGINFOD_LIBS) $(LIBBABELTRACE_LIB)
 CDEPS = $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) $(CTF_DEPS) \
        $(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) $(LIBGNU) \
        $(LIBSUPPORT)
@@ -995,6 +999,7 @@ COMMON_SFILES = \
        break-catch-syscall.c \
        break-catch-throw.c \
        breakpoint.c \
+       bt-utils.c \
        btrace.c \
        build-id.c \
        buildsym-legacy.c \
@@ -1256,6 +1261,7 @@ HFILES_NO_SRCDIR = \
        breakpoint.h \
        bsd-kvm.h \
        bsd-uthread.h \
+       bt-utils.h \
        build-id.h \
        buildsym-legacy.h \
        buildsym.h \
diff --git a/gdb/bt-utils.c b/gdb/bt-utils.c
new file mode 100644 (file)
index 0000000..b5e0a0e
--- /dev/null
@@ -0,0 +1,170 @@
+/* Copyright (C) 2021 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include "defs.h"
+#include "bt-utils.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "top.h"
+#include "cli/cli-decode.h"
+
+/* See bt-utils.h.  */
+
+void
+gdb_internal_backtrace_set_cmd (const char *args, int from_tty,
+                               cmd_list_element *c)
+{
+  gdb_assert (c->type == set_cmd);
+  gdb_assert (c->var_type == var_boolean);
+  gdb_assert (c->var != nullptr);
+
+#ifndef GDB_PRINT_INTERNAL_BACKTRACE
+  bool *var_ptr = (bool *) c->var;
+
+  if (*var_ptr)
+    {
+      *var_ptr = false;
+      error (_("support for this feature is not compiled into GDB"));
+    }
+#endif
+}
+
+#ifdef GDB_PRINT_INTERNAL_BACKTRACE_USING_LIBBACKTRACE
+
+/* Callback used by libbacktrace if it encounters an error.  */
+
+static void
+libbacktrace_error (void *data, const char *errmsg, int errnum)
+{
+  /* A negative errnum indicates no debug info was available, just
+     skip printing a backtrace in this case.  */
+  if (errnum < 0)
+    return;
+
+  const auto sig_write = [] (const char *msg) -> void
+  {
+    gdb_stderr->write_async_safe (msg, strlen (msg));
+  };
+
+  sig_write ("error creating backtrace: ");
+  sig_write (errmsg);
+  if (errnum > 0)
+    {
+      char buf[20];
+      snprintf (buf, sizeof (buf), ": %d", errnum);
+      buf[sizeof (buf) - 1] = '\0';
+
+      sig_write (buf);
+    }
+  sig_write ("\n");
+}
+
+/* Callback used by libbacktrace to print a single stack frame.  */
+
+static int
+libbacktrace_print (void *data, uintptr_t pc, const char *filename,
+                   int lineno, const char *function)
+{
+  const auto sig_write = [] (const char *msg) -> void
+  {
+    gdb_stderr->write_async_safe (msg, strlen (msg));
+  };
+
+  /* Buffer to print addresses and line numbers into.  An 8-byte address
+     with '0x' prefix and a null terminator requires 20 characters.  This
+     also feels like it should be enough to represent line numbers in most
+     files.  We are also careful to ensure we don't overflow this buffer.  */
+  char buf[20];
+
+  snprintf (buf, sizeof (buf), "0x%lx ", pc);
+  buf[sizeof (buf) - 1] = '\0';
+  sig_write (buf);
+  sig_write (function == nullptr ? "???" : function);
+  if (filename != nullptr)
+    {
+      sig_write ("\n\t");
+      sig_write (filename);
+      sig_write (":");
+      snprintf (buf, sizeof (buf), "%d", lineno);
+      buf[sizeof (buf) - 1] = '\0';
+      sig_write (buf);
+    }
+  sig_write ("\n");
+
+  return function != nullptr && strcmp (function, "main") == 0;
+}
+
+/* Write a backtrace to GDB's stderr in an async safe manor.  This is a
+   backtrace of GDB, not any running inferior, and is to be used when GDB
+   crashes or hits some other error condition.  */
+
+static void
+gdb_internal_backtrace_1 ()
+{
+  static struct backtrace_state *state = nullptr;
+
+  if (state == nullptr)
+    state = backtrace_create_state (nullptr, 0, libbacktrace_error, nullptr);
+
+  backtrace_full (state, 0, libbacktrace_print, libbacktrace_error, nullptr);
+}
+
+#elif defined GDB_PRINT_INTERNAL_BACKTRACE_USING_EXECINFO
+
+/* See the comment on previous version of this function.  */
+
+static void
+gdb_internal_backtrace_1 ()
+{
+  const auto sig_write = [] (const char *msg) -> void
+  {
+    gdb_stderr->write_async_safe (msg, strlen (msg));
+  };
+
+  /* Allow up to 25 frames of backtrace.  */
+  void *buffer[25];
+  int frames = backtrace (buffer, ARRAY_SIZE (buffer));
+
+  backtrace_symbols_fd (buffer, frames, gdb_stderr->fd ());
+  if (frames == ARRAY_SIZE (buffer))
+    sig_write (_("Backtrace might be incomplete.\n"));
+}
+
+#endif
+
+/* See bt-utils.h.  */
+
+void
+gdb_internal_backtrace ()
+{
+  if (current_ui == nullptr)
+    return;
+
+  const auto sig_write = [] (const char *msg) -> void
+  {
+    gdb_stderr->write_async_safe (msg, strlen (msg));
+  };
+
+  sig_write (_("----- Backtrace -----\n"));
+
+  if (gdb_stderr->fd () > -1)
+    gdb_internal_backtrace_1 ();
+  else
+    sig_write (_("Backtrace unavailable\n"));
+
+  sig_write ("---------------------\n");
+}
diff --git a/gdb/bt-utils.h b/gdb/bt-utils.h
new file mode 100644 (file)
index 0000000..433aa23
--- /dev/null
@@ -0,0 +1,69 @@
+/* Copyright (C) 2021 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+/* Support for printing a backtrace when GDB hits an error.  This is not
+   for printing backtraces of the inferior, but backtraces of GDB itself.  */
+
+#ifdef HAVE_LIBBACKTRACE
+# include "backtrace.h"
+# include "backtrace-supported.h"
+# if BACKTRACE_SUPPORTED && (! BACKTRACE_USES_MALLOC)
+#  define GDB_PRINT_INTERNAL_BACKTRACE
+#  define GDB_PRINT_INTERNAL_BACKTRACE_USING_LIBBACKTRACE
+# endif
+#endif
+
+#if defined HAVE_EXECINFO_H                    \
+  && defined HAVE_EXECINFO_BACKTRACE           \
+  && !defined PRINT_BACKTRACE_ON_FATAL_SIGNAL
+# include <execinfo.h>
+# define GDB_PRINT_INTERNAL_BACKTRACE
+# define GDB_PRINT_INTERNAL_BACKTRACE_USING_EXECINFO
+#endif
+
+/* Define GDB_PRINT_INTERNAL_BACKTRACE_INIT_ON.  This is a boolean value
+   that can be used as an initial value for a set/show user setting, where
+   the setting controls printing a GDB internal backtrace.
+
+   If backtrace printing is supported then this will have the value true,
+   but if backtrace printing is not supported then this has the value
+   false.  */
+#ifdef GDB_PRINT_INTERNAL_BACKTRACE
+# define GDB_PRINT_INTERNAL_BACKTRACE_INIT_ON true
+#else
+# define GDB_PRINT_INTERNAL_BACKTRACE_INIT_ON false
+#endif
+
+/* Print a backtrace of the current GDB process to the current
+   gdb_stderr.  The output is done in a signal async manor, so it is safe
+   to call from signal handlers.  */
+
+extern void gdb_internal_backtrace ();
+
+/* A generic function that can be used as the set function for any set
+   command that enables printing of an internal backtrace.  Command C must
+   be a boolean set command.
+
+   If GDB doesn't support printing a backtrace, and the user has tried to
+   set the variable associated with command C to true, then the associated
+   variable will be set back to false, and an error thrown.
+
+   If GDB does support printing a backtrace then this function does
+   nothing.  */
+
+extern void gdb_internal_backtrace_set_cmd (const char *args, int from_tty,
+                                           cmd_list_element *c);
index af3680c6d9524f99c52e48f1b4c30c57856efe04..c61f7a9135267da5f1356219d1621a38133bf764 100644 (file)
 /* Define if you have the babeltrace library. */
 #undef HAVE_LIBBABELTRACE
 
+/* Define if libbacktrace is being used. */
+#undef HAVE_LIBBACKTRACE
+
 /* Define to 1 if debuginfod is enabled. */
 #undef HAVE_LIBDEBUGINFOD
 
index f0b1af4a6ea5e303525db8dcea98c45a4ef5b28d..7c8335f2576e5ca3a126dab42b0df03d5b1817bf 100755 (executable)
@@ -636,6 +636,8 @@ LIBCTF
 LTLIBBABELTRACE
 LIBBABELTRACE
 HAVE_LIBBABELTRACE
+LIBBACKTRACE_LIB
+LIBBACKTRACE_INC
 HAVE_NATIVE_GCORE_HOST
 NAT_GENERATED_FILES
 XM_CLIBS
@@ -925,6 +927,7 @@ with_tcl
 with_tk
 with_x
 enable_sim
+enable_libbacktrace
 with_babeltrace
 with_libbabeltrace_prefix
 with_libbabeltrace_type
@@ -1601,6 +1604,8 @@ Optional Features:
                           gcc is used
   --enable-ubsan          enable undefined behavior sanitizer (auto/yes/no)
   --enable-sim            link gdb with simulator
+  --enable-libbacktrace   use libbacktrace to write a backtrace after a fatal
+                          signal.
   --enable-libctf         Handle .ctf type-info sections [default=yes]
   --enable-unit-tests     Enable the inclusion of unit tests when compiling
                           GDB
@@ -18659,6 +18664,33 @@ _ACEOF
 
 fi
 
+# Setup possible use of libbacktrace.
+# Check whether --enable-libbacktrace was given.
+if test "${enable_libbacktrace+set}" = set; then :
+  enableval=$enable_libbacktrace; case "${enableval}" in
+  yes)  enable_libbacktrace=yes ;;
+  no)   enable_libbacktrace=no  ;;
+  *)    as_fn_error $? "bad value ${enableval} for --enable-libbacktrace option" "$LINENO" 5 ;;
+esac
+else
+  enable_libbacktrace=yes
+fi
+
+
+if test "${enable_libbacktrace}" == "yes"; then
+  LIBBACKTRACE_INC="-I$srcdir/../libbacktrace/ -I../libbacktrace/"
+  LIBBACKTRACE_LIB=../libbacktrace/.libs/libbacktrace.a
+
+$as_echo "#define HAVE_LIBBACKTRACE 1" >>confdefs.h
+
+else
+  LIBBACKTRACE_INC=
+  LIBBACKTRACE_LIB=
+fi
+
+
+
+
 # Check for babeltrace and babeltrace-ctf
 
 # Check whether --with-babeltrace was given.
index 93f11316a142476dbf62f2ee7999f3eb742f07d7..0d91be59cd6159fba8c1e961bac231c48055c583 100644 (file)
@@ -2131,6 +2131,29 @@ if test x"${gdb_osabi}" != x ; then
                       [Define to the default OS ABI for this configuration.])
 fi
 
+# Setup possible use of libbacktrace.
+AC_ARG_ENABLE([libbacktrace],
+[AS_HELP_STRING([--enable-libbacktrace],
+                [use libbacktrace to write a backtrace after a fatal signal.])],
+[case "${enableval}" in
+  yes)  enable_libbacktrace=yes ;;
+  no)   enable_libbacktrace=no  ;;
+  *)    AC_MSG_ERROR(bad value ${enableval} for --enable-libbacktrace option) ;;
+esac],
+enable_libbacktrace=yes)
+
+if test "${enable_libbacktrace}" == "yes"; then
+  LIBBACKTRACE_INC="-I$srcdir/../libbacktrace/ -I../libbacktrace/"
+  LIBBACKTRACE_LIB=../libbacktrace/.libs/libbacktrace.a
+  AC_DEFINE(HAVE_LIBBACKTRACE, 1, [Define if libbacktrace is being used.])
+else
+  LIBBACKTRACE_INC=
+  LIBBACKTRACE_LIB=
+fi
+
+AC_SUBST(LIBBACKTRACE_INC)
+AC_SUBST(LIBBACKTRACE_LIB)
+
 # Check for babeltrace and babeltrace-ctf
 AC_ARG_WITH(babeltrace,
   AS_HELP_STRING([--with-babeltrace], [include babeltrace support (auto/yes/no)]),
index 9233a3650ac401e2b34637a556f96e8429f8c8ea..64c624ce4d746f3e60086e581ae72f7e79a66e6a 100644 (file)
 #include "gdbsupport/gdb_select.h"
 #include "gdbsupport/gdb-sigmask.h"
 #include "async-event.h"
+#include "bt-utils.h"
 
 /* readline include files.  */
 #include "readline/readline.h"
 #include "readline/history.h"
 
-#ifdef HAVE_EXECINFO_H
-# include <execinfo.h>
-#endif /* HAVE_EXECINFO_H */
-
 /* readline defines this.  */
 #undef savestring
 
@@ -102,12 +99,7 @@ int call_stdin_event_handler_again_p;
 
 /* When true GDB will produce a minimal backtrace when a fatal signal is
    reached (within GDB code).  */
-static bool bt_on_fatal_signal
-#ifdef HAVE_EXECINFO_BACKTRACE
-  = true;
-#else
-  = false;
-#endif /* HAVE_EXECINFO_BACKTRACE */
+static bool bt_on_fatal_signal = GDB_PRINT_INTERNAL_BACKTRACE_INIT_ON;
 
 /* Implement 'maintenance show backtrace-on-fatal-signal'.  */
 
@@ -118,20 +110,6 @@ show_bt_on_fatal_signal (struct ui_file *file, int from_tty,
   fprintf_filtered (file, _("Backtrace on a fatal signal is %s.\n"), value);
 }
 
-/* Implement 'maintenance set backtrace-on-fatal-signal'.  */
-
-static void
-set_bt_on_fatal_signal (const char *args, int from_tty, cmd_list_element *c)
-{
-#ifndef HAVE_EXECINFO_BACKTRACE
-  if (bt_on_fatal_signal)
-    {
-      bt_on_fatal_signal = false;
-      error (_("support for this feature is not compiled into GDB"));
-    }
-#endif
-}
-
 /* Signal handling variables.  */
 /* Each of these is a pointer to a function that the event loop will
    invoke if the corresponding signal has received.  The real signal
@@ -904,7 +882,7 @@ unblock_signal (int sig)
 static void ATTRIBUTE_NORETURN
 handle_fatal_signal (int sig)
 {
-#ifdef HAVE_EXECINFO_BACKTRACE
+#ifdef GDB_PRINT_INTERNAL_BACKTRACE
   const auto sig_write = [] (const char *msg) -> void
   {
     gdb_stderr->write_async_safe (msg, strlen (msg));
@@ -917,19 +895,8 @@ handle_fatal_signal (int sig)
       sig_write (strsignal (sig));
       sig_write ("\n");
 
-      /* Allow up to 25 frames of backtrace.  */
-      void *buffer[25];
-      int frames = backtrace (buffer, ARRAY_SIZE (buffer));
-      sig_write (_("----- Backtrace -----\n"));
-      if (gdb_stderr->fd () > -1)
-       {
-         backtrace_symbols_fd (buffer, frames, gdb_stderr->fd ());
-         if (frames == ARRAY_SIZE (buffer))
-           sig_write (_("Backtrace might be incomplete.\n"));
-       }
-      else
-       sig_write (_("Backtrace unavailable\n"));
-      sig_write ("---------------------\n");
+      gdb_internal_backtrace ();
+
       sig_write (_("A fatal error internal to GDB has been detected, "
                   "further\ndebugging is not possible.  GDB will now "
                   "terminate.\n\n"));
@@ -944,7 +911,7 @@ handle_fatal_signal (int sig)
 
       gdb_stderr->flush ();
     }
-#endif /* HAVE_EXECINF_BACKTRACE */
+#endif
 
   /* If possible arrange for SIG to have its default behaviour (which
      should be to terminate the current process), unblock SIG, and reraise
@@ -1449,7 +1416,7 @@ Use \"on\" to enable, \"off\" to disable.\n\
 If enabled, GDB will produce a minimal backtrace if it encounters a fatal\n\
 signal from within GDB itself.  This is a mechanism to help diagnose\n\
 crashes within GDB, not a mechanism for debugging inferiors."),
-                          set_bt_on_fatal_signal,
+                          gdb_internal_backtrace_set_cmd,
                           show_bt_on_fatal_signal,
                           &maintenance_set_cmdlist,
                           &maintenance_show_cmdlist);