]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb: add "essential" command class
authorGuinevere Larsen <guinevere@redhat.com>
Fri, 19 Sep 2025 11:44:35 +0000 (08:44 -0300)
committerGuinevere Larsen <guinevere@redhat.com>
Wed, 5 Nov 2025 21:17:23 +0000 (18:17 -0300)
Currently, there is no way for a new user to have an idea of common
useful commands  and behaviors from the GDB interface itself, without
checking the example session in the documentation.  This command class
aims to close that gap by providing a set of quickstart commands that
allows for any simple debug session to happen without anything too
egregious missing.

The set of commands was chosen somewhat arbitrarily, based on what I
used or missed the most.  The one overarching important thing, however,
is that the list is kept short, so as to not overwhelm new users.  This
is confirmed by the newly introduced selftest, essential_command_count,
which ensures there are 20 or fewer essential commands.

Here's the reasoning for some of the choices:
* The command "start" was picked over "run" because combining it with
"continue" achieves the same effect, and I prefer it over needing to set
a breakpoint on main to stop at the start of the inferior.
* The command "ptype" is chosen because I believe it is important to
provide a way for the user to check a variable's type from inside GDB,
and ptype is a more complete command than the alternative, "whatis".

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Approved-By: Tom Tromey <tom@tromey.com>
14 files changed:
gdb/NEWS
gdb/breakpoint.c
gdb/cli/cli-cmds.c
gdb/cli/cli-decode.c
gdb/cli/cli-decode.h
gdb/command.h
gdb/guile/scm-cmd.c
gdb/infcmd.c
gdb/printcmd.c
gdb/python/py-cmd.c
gdb/stack.c
gdb/testsuite/gdb.base/page.exp
gdb/typeprint.c
gdb/unittests/command-def-selftests.c

index 097d8da350bb106972a49d6e93d9d08f6c188b4d..64e35f1a438f763e460ae3876cca5675540d6304 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -55,6 +55,11 @@ info inferiors
   as an additional line under the inferior's table entry in the
   output.
 
+New command class for help
+  The new command class "essential" has been added, which is a set of
+  commands that we, as developers, believe would be close to a minimal
+  set of commands for a new user of GDB.
+
 * Changed remote packets
 
 single-inf-arg in qSupported
index 382d4706839da9b0dd2b671c1ba35129f39c4b2e..d084ba80d246c64422b929a66096bb2be6d0255e 100644 (file)
@@ -14916,7 +14916,8 @@ This command may be abbreviated \"disable\"."),
           &disablelist);
 
   cmd_list_element *delete_cmd
-    = add_prefix_cmd ("delete", class_breakpoint, delete_command, _("\
+    = add_prefix_cmd ("delete", class_breakpoint | class_essential,
+                     delete_command, _("\
 Delete all or some breakpoints.\n\
 Usage: delete [BREAKPOINTNUM]...\n\
 Arguments are breakpoint numbers with spaces in between.\n\
@@ -14949,7 +14950,7 @@ See also the \"delete\" command which clears breakpoints by number."));
   add_com_alias ("cl", clear_cmd, class_breakpoint, 1);
 
   cmd_list_element *break_cmd
-    = add_com ("break", class_breakpoint, break_command, _("\
+    = add_com ("break", class_breakpoint | class_essential, break_command, _("\
 Set breakpoint at specified location.\n"
 BREAK_ARGS_HELP ("break")));
   set_cmd_completer (break_cmd, location_completer);
@@ -15020,7 +15021,7 @@ Options:\n\
 \n\
 A watchpoint stops execution of your program whenever the value of\n\
 an expression changes."), opts);
-  c = add_com ("watch", class_breakpoint, watch_command,
+  c = add_com ("watch", class_breakpoint | class_essential, watch_command,
               watch_help.c_str ());
   set_cmd_completer_handle_brkchars (c, watch_command_completer);
 
index 6656f393580646226d9b5aa3f1ff4ed8f97b7f52..300b77dbafe8ed8be19505e866034ad70f3f6368 100644 (file)
@@ -2628,6 +2628,20 @@ INIT_GDB_FILE (cli_cmds)
   /* Define the classes of commands.
      They will appear in the help list in alphabetical order.  */
 
+  add_cmd ("essential", class_essential, _("\
+GDB essential commands.\n\
+Welcome to GDB!  This help text aims to provide a quickstart explanation\n\
+that will allow you to start using GDB.  Feel free to use \"help <cmd>\"\n\
+to get further explanations for any command <cmd>, and check the online\n\
+documentation for in-depth explanations.\n\
+Here are some common GDB behaviors that you can expect, which are\n\
+not tied to any specific command but rather GDB functionality itself:\n\
+\n\
+EXPR is any arbitrary expression valid for the current programming language.\n\
+Pressing <return> with an empty prompt executes the last command again.\n\
+You can use <tab> to complete commands and symbols.  Pressing it twice lists\n\
+all possible completions if more than one is available."),
+          &cmdlist);
   add_cmd ("internals", class_maintenance, _("\
 Maintenance commands.\n\
 Some gdb commands are provided just for use by gdb maintainers.\n\
@@ -2880,7 +2894,7 @@ and send its output to SHELL_COMMAND."));
   add_com_alias ("|", pipe_cmd, class_support, 0);
 
   cmd_list_element *list_cmd
-    = add_com ("list", class_files, list_command, _("\
+    = add_com ("list", class_files | class_essential, list_command, _("\
 List specified function or line.\n\
 With no argument, lists ten more lines after or around previous listing.\n\
 \"list +\" lists the ten lines following a previous ten-line listing.\n\
@@ -2941,7 +2955,7 @@ Show definitions of non-python/scheme user defined commands.\n\
 Argument is the name of the user defined command.\n\
 With no argument, show definitions of all user defined commands."), &showlist);
   set_cmd_completer (c, show_user_completer);
-  add_com ("apropos", class_support, apropos_command, _("\
+  add_com ("apropos", class_support | class_essential, apropos_command, _("\
 Search for commands matching a REGEXP.\n\
 Usage: apropos [-v] REGEXP\n\
 Flag -v indicates to produce a verbose output, showing full documentation\n\
index 3531cf12d934531f01fcc8a2a6ee5d9c4fe72fde..6a7d07ae29cba1334da66f60c0a5cd90856a7985 100644 (file)
@@ -1954,7 +1954,10 @@ help_list (struct cmd_list_element *list, const char *cmdtype,
                styled_string (command_style.style (), cmdtype),
                prefix);
 
-  bool recurse = (theclass != all_commands) && (theclass != all_classes);
+  /* Don't recurse if theclass is beginner, since the quickstart
+     help is meant to be direct and not include prefix commands.  */
+  bool recurse = (theclass != all_commands) && (theclass != all_classes)
+                && (theclass != class_essential);
   help_cmd_list (list, theclass, recurse, stream);
 
   if (theclass == all_classes)
index 9e006c1ae87fd3ab3fc48de34728e37a6c66ba83..2aeed81ad58728a2a16aa207daf4244252669d2b 100644 (file)
@@ -102,6 +102,9 @@ struct cmd_list_element
   bool is_prefix () const
   { return this->subcommands != nullptr; }
 
+  bool is_essential () const
+  { return (this->theclass & class_essential) != 0; }
+
   /* Return true if this command is a "command class help" command.  For
      instance, a "stack" dummy command is registered so that one can do
      "help stack" and show help for all commands of the "stack" class.  */
index 385345a1b295b4bf36e7c5dcb4d46468de324698..4b9114fb658f26e264ec5684b6379226886db903 100644 (file)
@@ -64,9 +64,10 @@ enum command_class
   class_maintenance = 1 << 12, /* internals */
   class_tui = 1 << 13,         /* text-user-interface */
   class_user = 1 << 14,        /* user-defined */
+  class_essential = 1 << 15,    /* essential */
 
   /* Used for "show" commands that have no corresponding "set" command.  */
-  no_set_class = 1 << 15
+  no_set_class = 1 << 16
 };
 DEF_ENUM_FLAGS_TYPE (enum command_class, command_classes);
 
index ef4c9d6b011e70788f828d5097b56055a284c553..d2172d98618789897b4693e162f4b7785a6fd296 100644 (file)
@@ -564,6 +564,7 @@ static const scheme_integer_constant command_classes[] =
   { "COMMAND_OBSCURE", class_obscure },
   { "COMMAND_MAINTENANCE", class_maintenance },
   { "COMMAND_USER", class_user },
+  { "COMMAND_ESSENTIAL", class_essential },
 
   END_INTEGER_CONSTANTS
 };
index 08cd2514d3ece5e93d2e5a8ab837a866b02cb302..60f796909a3cb6e9905399f6b0ab294db87a8d69 100644 (file)
@@ -3248,7 +3248,7 @@ Upon return, the value returned is printed and put in the value history."));
   add_com_alias ("fin", finish_cmd, class_run, 1);
 
   cmd_list_element *next_cmd
-    = add_com ("next", class_run, next_command, _("\
+    = add_com ("next", class_run | class_essential, next_command, _("\
 Step program, proceeding through subroutine calls.\n\
 Usage: next [N]\n\
 Unlike \"step\", if the current source line calls a subroutine,\n\
@@ -3257,7 +3257,7 @@ the call, in effect treating it as a single source line."));
   add_com_alias ("n", next_cmd, class_run, 1);
 
   cmd_list_element *step_cmd
-    = add_com ("step", class_run, step_command, _("\
+    = add_com ("step", class_run | class_essential, step_command, _("\
 Step program until it reaches a different source line.\n\
 Usage: step [N]\n\
 Argument N means step N times (or till program stops for another \
@@ -3291,7 +3291,7 @@ for an address to start at."));
   add_com_alias ("j", jump_cmd, class_run, 1);
 
   cmd_list_element *continue_cmd
-    = add_com ("continue", class_run, continue_command, _("\
+    = add_com ("continue", class_run | class_essential, continue_command, _("\
 Continue program being debugged, after signal or breakpoint.\n\
 Usage: continue [N]\n\
 If proceeding from breakpoint, a number N may be used as an argument,\n\
@@ -3312,7 +3312,7 @@ RUN_ARGS_HELP));
   set_cmd_completer (run_cmd, deprecated_filename_completer);
   add_com_alias ("r", run_cmd, class_run, 1);
 
-  c = add_com ("start", class_run, start_command, _("\
+  c = add_com ("start", class_run | class_essential, start_command, _("\
 Start the debugged program stopping at the beginning of the main procedure.\n"
 RUN_ARGS_HELP));
   set_cmd_completer (c, deprecated_filename_completer);
index ba761069fbdea0f6a34e683dd670b23ed28136f2..b9618f0a333450515a02b48d136c022716bf342a 100644 (file)
@@ -3231,7 +3231,7 @@ No argument means cancel all automatic-display expressions.\n\
 Do \"info display\" to see current list of code numbers."),
           &cmdlist);
 
-  c = add_com ("display", class_vars, display_command, _("\
+  c = add_com ("display", class_vars | class_essential, display_command, _("\
 Print value of expression EXP each time the program stops.\n\
 Usage: display[/FMT] EXP\n\
 /FMT may be used before EXP as in the \"print\" command.\n\
@@ -3345,7 +3345,8 @@ but no count or size letter (see \"x\" command)."),
                                              print_opts);
 
   cmd_list_element *print_cmd
-    = add_com ("print", class_vars, print_command, print_help.c_str ());
+    = add_com ("print", class_vars | class_essential, print_command,
+              print_help.c_str ());
   set_cmd_completer_handle_brkchars (print_cmd, print_command_completer);
   add_com_alias ("p", print_cmd, class_vars, 1);
   add_com_alias ("inspect", print_cmd, class_vars, 1);
index 54bac97146ecdfc302a5fdb7ed8b54874671aa48..7445a9e165bb8fd336d6b649dc327eead2a8dc05 100644 (file)
@@ -460,7 +460,7 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
       && cmdtype != class_info && cmdtype != class_breakpoint
       && cmdtype != class_trace && cmdtype != class_obscure
       && cmdtype != class_maintenance && cmdtype != class_user
-      && cmdtype != class_tui)
+      && cmdtype != class_tui && cmdtype != class_essential)
     {
       PyErr_Format (PyExc_RuntimeError, _("Invalid command class argument."));
       return -1;
@@ -621,6 +621,8 @@ gdbpy_initialize_commands ()
       || PyModule_AddIntConstant (gdb_module, "COMMAND_MAINTENANCE",
                                  class_maintenance) < 0
       || PyModule_AddIntConstant (gdb_module, "COMMAND_USER", class_user) < 0
+      || PyModule_AddIntConstant (gdb_module, "COMMAND_ESSENTIAL",
+                                 class_essential) < 0
       || PyModule_AddIntConstant (gdb_module, "COMMAND_TUI", class_tui) < 0)
     return -1;
 
index 2c00eb2816bc566121d775e7fadc049833b5e3d6..c2c76be8163c6693a0454bc63a121249c3416ae3 100644 (file)
@@ -3270,7 +3270,7 @@ Control remains in the debugger, but when you continue\n\
 execution will resume in the frame above the one now selected.\n\
 If an argument is given, it is an expression for the value to return."));
 
-  add_com ("up", class_stack, up_command, _("\
+  add_com ("up", class_stack | class_essential, up_command, _("\
 Select and print stack frame that called this one.\n\
 An argument says how many frames up to go."));
   add_com ("up-silently", class_support, up_silently_command, _("\
@@ -3278,7 +3278,7 @@ Same as the `up' command, but does not print anything.\n\
 This is useful in command scripts."));
 
   cmd_list_element *down_cmd
-    = add_com ("down", class_stack, down_command, _("\
+    = add_com ("down", class_stack | class_essential, down_command, _("\
 Select and print stack frame called by this one.\n\
 An argument says how many frames down to go."));
   add_com_alias ("do", down_cmd, class_stack, 1);
@@ -3449,7 +3449,7 @@ With a negative COUNT, print outermost -COUNT frames."),
                               backtrace_opts);
 
   cmd_list_element *backtrace_cmd
-    = add_com ("backtrace", class_stack, backtrace_command,
+    = add_com ("backtrace", class_stack | class_essential, backtrace_command,
               backtrace_help.c_str ());
   set_cmd_completer_handle_brkchars (backtrace_cmd, backtrace_command_completer);
 
index 026356ae680f7d6c6f25e83670b4b243b9afe359..c9c9884860266945cc18553bd27236e4ad2fbb72 100644 (file)
@@ -26,6 +26,7 @@ gdb_test_sequence "help" "unpaged help" {
     "aliases -- User-defined aliases of other commands"
     "breakpoints -- Making program stop at certain points"
     "data -- Examining data"
+    "essential -- GDB essential commands"
     "files -- Specifying and examining files"
     "internals -- Maintenance commands"
     "obscure -- Obscure features"
@@ -53,10 +54,10 @@ gdb_expect_list "paged help" \
     "aliases -- User-defined aliases of other commands"
     "breakpoints -- Making program stop at certain points"
     "data -- Examining data"
+    "essential -- GDB essential commands"
     "files -- Specifying and examining files"
     "internals -- Maintenance commands"
     "obscure -- Obscure features"
-    "running -- Running the program"
 }
 gdb_test "q"
 
index 1609e53a10e0837b108cdff8dc4c16a19081ccad..92dadd06c90cdcbe603ee6cde076e4b02aa15d0a 100644 (file)
@@ -738,7 +738,7 @@ INIT_GDB_FILE (typeprint)
 {
   struct cmd_list_element *c;
 
-  c = add_com ("ptype", class_vars, ptype_command, _("\
+  c = add_com ("ptype", class_vars | class_essential, ptype_command, _("\
 Print definition of type TYPE.\n\
 Usage: ptype[/FLAGS] TYPE | EXPRESSION\n\
 Argument may be any type (for example a type name defined by typedef,\n\
index 095b57f6e95cd900c522b2b8010e27c01c83e2e0..56592a796e0fa235056577ed912fe1a03ecc423d 100644 (file)
@@ -217,6 +217,29 @@ command_structure_invariants_tests ()
 
 }
 
+namespace essential_command_tests {
+
+/* The maximum number of commands that can be considered
+   essential by GDB.  This value was chosen arbitrarily,
+   but it must be kept low, so as to not overwhelm new
+   users.  */
+static constexpr int max_essential_cmds = 20;
+
+static void
+essential_command_count_tests ()
+{
+  int nr_essential_cmds = 0;
+
+  for (struct cmd_list_element *c = cmdlist; c != nullptr; c = c->next)
+    {
+      if (c->is_essential ())
+       nr_essential_cmds ++;
+    }
+
+  SELF_CHECK (nr_essential_cmds <= max_essential_cmds);
+}
+}
+
 } /* namespace selftests */
 
 INIT_GDB_FILE (command_def_selftests)
@@ -228,4 +251,8 @@ INIT_GDB_FILE (command_def_selftests)
   selftests::register_test
     ("command_structure_invariants",
      selftests::command_structure_tests::command_structure_invariants_tests);
+
+  selftests::register_test
+    ("essential_command_count",
+     selftests::essential_command_tests::essential_command_count_tests);
 }