From: Guinevere Larsen Date: Fri, 19 Sep 2025 11:44:35 +0000 (-0300) Subject: gdb: add "essential" command class X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1518f2e087d31997711fc7f4472ab382c24fa903;p=thirdparty%2Fbinutils-gdb.git gdb: add "essential" command class 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 Approved-By: Tom Tromey --- diff --git a/gdb/NEWS b/gdb/NEWS index 097d8da350b..64e35f1a438 100644 --- 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 diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 382d4706839..d084ba80d24 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -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); diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index 6656f393580..300b77dbafe 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -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 \"\n\ +to get further explanations for any command , 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 with an empty prompt executes the last command again.\n\ +You can use 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\ diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c index 3531cf12d93..6a7d07ae29c 100644 --- a/gdb/cli/cli-decode.c +++ b/gdb/cli/cli-decode.c @@ -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) diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h index 9e006c1ae87..2aeed81ad58 100644 --- a/gdb/cli/cli-decode.h +++ b/gdb/cli/cli-decode.h @@ -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. */ diff --git a/gdb/command.h b/gdb/command.h index 385345a1b29..4b9114fb658 100644 --- a/gdb/command.h +++ b/gdb/command.h @@ -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); diff --git a/gdb/guile/scm-cmd.c b/gdb/guile/scm-cmd.c index ef4c9d6b011..d2172d98618 100644 --- a/gdb/guile/scm-cmd.c +++ b/gdb/guile/scm-cmd.c @@ -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 }; diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 08cd2514d3e..60f796909a3 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -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); diff --git a/gdb/printcmd.c b/gdb/printcmd.c index ba761069fbd..b9618f0a333 100644 --- a/gdb/printcmd.c +++ b/gdb/printcmd.c @@ -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); diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c index 54bac97146e..7445a9e165b 100644 --- a/gdb/python/py-cmd.c +++ b/gdb/python/py-cmd.c @@ -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; diff --git a/gdb/stack.c b/gdb/stack.c index 2c00eb2816b..c2c76be8163 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -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); diff --git a/gdb/testsuite/gdb.base/page.exp b/gdb/testsuite/gdb.base/page.exp index 026356ae680..c9c98848602 100644 --- a/gdb/testsuite/gdb.base/page.exp +++ b/gdb/testsuite/gdb.base/page.exp @@ -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" diff --git a/gdb/typeprint.c b/gdb/typeprint.c index 1609e53a10e..92dadd06c90 100644 --- a/gdb/typeprint.c +++ b/gdb/typeprint.c @@ -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\ diff --git a/gdb/unittests/command-def-selftests.c b/gdb/unittests/command-def-selftests.c index 095b57f6e95..56592a796e0 100644 --- a/gdb/unittests/command-def-selftests.c +++ b/gdb/unittests/command-def-selftests.c @@ -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); }