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 <eliz@gnu.org>
Approved-By: Tom Tromey <tom@tromey.com>
This commit is contained in:
Guinevere Larsen
2025-09-19 08:44:35 -03:00
parent 7028626eff
commit 1518f2e087
14 changed files with 78 additions and 19 deletions

View File

@@ -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

View 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);

View 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\

View 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)

View 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. */

View 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);

View 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
};

View 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);

View 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);

View 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;

View 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);

View 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"

View 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\

View 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);
}