* [PATCH v2] gdb: add tutorial command
@ 2026-01-22 20:28 Guinevere Larsen
2026-01-23 7:11 ` Eli Zaretskii
` (2 more replies)
0 siblings, 3 replies; 11+ messages in thread
From: Guinevere Larsen @ 2026-01-22 20:28 UTC (permalink / raw)
To: gdb-patches; +Cc: Guinevere Larsen
Before this commit, there is little way for a new user to learn how to
use GDB on their own. The documentation contains an example session,
but that isn't contained in GDB itself, and the "help" and "apropos"
commands exist, but they aren't the best to really teach what GDB can
do, only to describe commands on their own.
This commit changes this by introducing a command called "tutorial",
which takes a page out of common design from the last few decades and
provides a self-contained tutorial for users, walking them through a
simple bug in C code, and explaining several commands in context.
The tutorial is mostly implemented (ab)using the before_prompt hook to
print the messages, so that users can have completion, history and so
on, and it is implemented in python to make maintaining it in the future
as simple as possible.
---
gdb/NEWS | 4 +
gdb/data-directory/Makefile.in | 1 +
gdb/doc/gdb.texinfo | 34 +++
gdb/python/lib/gdb/command/tutorial.py | 404 +++++++++++++++++++++++++
gdb/top.c | 5 +-
5 files changed, 447 insertions(+), 1 deletion(-)
create mode 100644 gdb/python/lib/gdb/command/tutorial.py
diff --git a/gdb/NEWS b/gdb/NEWS
index 74fc353d7e9..5c8c06ca301 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -80,6 +80,10 @@ show progress-bars enabled
content, to be disabled (the set command), or to see if
progress-bars are currently enabled or not (the show command).
+tutorial
+ Guided example session with a generated C file, to teach an entirely
+ new user how to use GDB for basic debugging.
+
* Changed commands
maintenance info program-spaces
diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
index 7257da9cd11..572b7e25e48 100644
--- a/gdb/data-directory/Makefile.in
+++ b/gdb/data-directory/Makefile.in
@@ -93,6 +93,7 @@ PYTHON_FILE_LIST = \
gdb/command/missing_files.py \
gdb/command/pretty_printers.py \
gdb/command/prompt.py \
+ gdb/command/tutorial.py \
gdb/command/type_printers.py \
gdb/command/unwinders.py \
gdb/command/xmethods.py \
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 7059f73935c..99359f27822 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -2577,6 +2577,40 @@ system Readline library, or @samp{(internal)} indicating @value{GDBN}
is using a statically linked in version of the Readline library.
@end table
+Finally, if you want to learn how to use GDB for simple debug session,
+you can use the command @code{tutorial}.
+
+@table @code
+@kindex tutorial
+@item tutorial
+This command starts an interactive tutorial, teaching you basic commands
+and how you can chain them together for a successful debugging session.
+Warning: If you have an ongoing debug session, this command will kill that
+session and you'll have to start from scratch.
+
+@smallexample
+(@value{GDBP}) tutorial
+Welcome to GDB! This quick tutorial should be enough to get
+you acquainted with essential commands for basic debugging.
+At any point, you can feel free to use 'help <command>' to
+learn more about the commands that are being suggested, or
+any other commands that you may have heard of. You may also
+type 'quit' at any point to quit this tutorial, and use it
+again to quit GDB entirely.
+
+First, GDB will generate a C file with example code for the
+debug session. The code will contain a slightly flawed
+implementation of a bubble sort, which is used to sort an
+array of size 6. After the function is called, the program
+will print either "sorted" or "not sorted" depending on
+the result. Finally, the program will return 0 if the array
+is *not* sorted, and 1 if it is sorted.
+
+Enter the desired name for the file. This is important to
+avoid a collision that could overwrite your local data.
+@end smallexample
+@end table
+
@node Running
@chapter Running Programs Under @value{GDBN}
diff --git a/gdb/python/lib/gdb/command/tutorial.py b/gdb/python/lib/gdb/command/tutorial.py
new file mode 100644
index 00000000000..7e0bd0331f0
--- /dev/null
+++ b/gdb/python/lib/gdb/command/tutorial.py
@@ -0,0 +1,404 @@
+# GDB 'tutorial' command.
+# Copyright (C) 2025-2026 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/>.
+
+"""Implementation of the GDB 'tutorial' command using the GDB Python API."""
+
+import os
+
+import gdb
+
+tutorial_state = 0
+display_counter = 0
+
+generated_code = None
+generated_binary = None
+
+state_string = [
+ """
+To start properly debugging, first the file, which GDB calls the
+"inferior", needs to be loaded. That is how this tutorial
+will refer to it from now on.
+Use the following command to load the inferior:""",
+ """
+Next, you should verify that there really is a bug in the
+example, by running the entire program. One way to do that
+is by using the command "start", which runs the inferior
+until the first line of the main function, and then use the
+command "continue", which runs the inferior until it is
+forced to stop, such as by a breakpoint, signal or finishing.
+""",
+ """
+Notice the "not sorted" message, that is output of the inferior
+showing the bug.
+
+You've verified that the bug is there, now you should check
+if the bug is happening in the sort function or in the
+verification function. Stop after the sorting function, to
+be able to confirm if the array is sorted or not.
+You can do so by using "list main" to find the line number
+after the call to 'sort', then using the command "break <num>"
+to break right before executing line '<num>'. Then, execute
+the inferior again.
+If the call to the function 'sort' was not visible, following
+invocations of "list" will print following lines of the source
+code.
+""",
+ """
+You can check the value of the variable with the command
+"print vec". That command will evaluate any arbitrary
+expression valid for the current language, with all side effects
+that the expression would have.
+This means you can use it to prototype a solution without
+needing to recompile the inferior. Try it by setting 'vec'
+to a sorted array, like by using:
+ print vec = {0, 1, 2, 3, 4, 5}
+and see how the program would end, with the "continue"
+command again.
+""",
+ """
+Now that you're sure that 'sorted' is working correctly, you
+should get to the start of the 'sort' function. You can set a
+breakpoint using the function name, or you can repeatedly use
+the command "step" after "start", to move one line at a time,
+and entering any function calls that happen.
+""",
+ """
+This is the end of the guided section, from now on, try and
+debug the inferior on your own, you should have all the needed
+tools.
+
+Some extra commands that may be helpful are:
+ "next" - like step, but skips functions
+ "display" - like print, but shows every time the inferior stops
+ "watch" - like breakpoint, but when a variable changes
+Feel free to use "help <cmd>" to get more information on those
+commands.
+
+Additionally, if you'd like to see the entire array like it
+was printed in the 'main' function, use the following syntax:
+ print (*vec)@6
+
+Once you understand the bug, or would like to quit the tutorial,
+run the inferior until it exits and GDB will clean up after itself.
+""",
+]
+
+tutorial_reminders = [
+ "Please load the file with the previous command",
+ "You should run the inferior to make sure the bug exists",
+ "You need to stop in main, before the 'sorted' function is run",
+ "Use the print command to fix vec and see if 'sorted' works",
+ "Stop inside the 'sort' function",
+ "When done, run the inferior till the end to exit the tutorial",
+]
+
+
+# Print a message then get an input from the user
+# The input must be one of: y, Y, n, N
+# Return True if the user accepted (y or Y) False otherwise.
+def get_yn_opt(message):
+ message += " [y/n]: "
+ valid = "yYnN"
+ opt = input(message)
+ while opt not in valid:
+ input("Please enter y or n: ")
+ return opt in "yY"
+
+
+def inferior_in_location(first_line, last_line=None):
+ # This is in a try block because if the inferior isn't running,
+ # a "No stack" exception is thrown.
+ try:
+ loc = gdb.execute("frame", to_string=True)
+ loc = int(loc.split("\n")[1].split()[0])
+ if loc > first_line:
+ if last_line is None or loc <= last_line:
+ return True
+ except gdb.error:
+ # Exception is fine. This just means the inferior isn't
+ # running, so we can return false.
+ pass
+ return False
+
+
+# Test if a binary is loaded with the expected name.
+def inferior_is_loaded():
+ loaded = gdb.current_progspace().filename
+ if loaded is not None:
+ return loaded.endswith(generated_binary)
+ return False
+
+
+# Increment state and reset counter since a tutorial message
+# was last displayed.
+def increment_state():
+ global tutorial_state, display_counter
+ tutorial_state += 1
+ display_counter = 0
+
+
+# Function that holds all the logic to whether a step of the tutorial
+# has been completed.
+def should_advance_state():
+ # Tutorial has just started, immediately increment to print the
+ # first message.
+ if tutorial_state == -1:
+ return True
+ elif tutorial_state == 0:
+ return inferior_is_loaded()
+ # The only way to increment past states 1 and 3 is with the exit hook.
+ elif tutorial_state == 1 or tutorial_state == 3:
+ return False
+ elif tutorial_state == 2:
+ return inferior_in_location(25)
+ elif tutorial_state == 4:
+ return inferior_in_location(13, 20)
+ else:
+ return False
+
+
+def cleanup():
+ print(
+ """
+Thank you for taking this tutorial. We hope it has been
+helpful for you. If you found any bugs or would like to
+provide feedback, feel free to send do so through IRC, in
+the #gdb room of libera.chat, or send an email to
+gdb@sourceware.org.
+To recap, these were the commands explained in the tutorial
+ * shell
+ * file
+ * start
+ * continue
+ * list
+ * break
+ * print
+ * step
+ * next
+ * display
+ * watch
+ * quit
+ * help
+ """
+ )
+ gdb.events.before_prompt.disconnect(tutorial_hook)
+ gdb.events.exited.disconnect(tutorial_exit_hook)
+ # Clean up example code.
+ try:
+ f = open(generated_code)
+ f.close()
+ os.remove(generated_code)
+ except FileNotFoundError:
+ # File doesn't exist, nothing to do.
+ pass
+ try:
+ f = open(generated_binary)
+ f.close()
+ os.remove(generated_binary)
+ except FileNotFoundError:
+ # File doesn't exist, nothing to do.
+ pass
+
+
+# Hook for the "inferior exit" event. This is used to progress
+# a few states of the tutorial, and to finish it when the user is done.
+def tutorial_exit_hook(event):
+ if tutorial_state == 1:
+ increment_state()
+ elif tutorial_state == 3:
+ if event.exit_code == 1:
+ increment_state()
+ else:
+ print("The solution didn't work. Try again!")
+ elif tutorial_state == (len(state_string) - 1):
+ print(
+ "The tutorial is complete. Exiting tutorial and cleaning up the artifacts"
+ )
+ cleanup()
+ else:
+ opt = get_yn_opt(
+ "The tutorial program exited unexpectedly, "
+ + "would you like to quit the tutorial?"
+ )
+ if opt:
+ cleanup()
+
+
+# Main way that the tutorial functionality is implemented.
+# This is done by abusing before_prompt hooks so that we use GDB's
+# readline implementation, history, completion and so on.
+def tutorial_hook():
+ global display_counter
+
+ if should_advance_state():
+ increment_state()
+ elif display_counter == 10:
+ print("Reminder:", tutorial_reminders[tutorial_state])
+ display_counter = 1
+
+ if tutorial_state >= len(state_string):
+ cleanup()
+ gdb.events.before_prompt.disconnect(tutorial_hook)
+ elif display_counter == 0:
+ print(state_string[tutorial_state])
+ if tutorial_state == 0:
+ print(" file ", generated_binary)
+
+ display_counter += 1
+
+
+# Main implementation of the tutorial command.
+class Tutorial(gdb.Command):
+ """Tutorial on the usage of the core commands of GDB.
+
+ usage: tutorial
+
+ This tutorial does not aim to be comprehensive. On the contrary,
+ it aims to be a quick way for you to start using GDB and learn
+ how to search for more commands that you may find useful.
+ """
+
+ def __init__(self):
+ super(Tutorial, self).__init__(
+ name="tutorial", command_class=gdb.COMMAND_ESSENTIAL, prefix=False
+ )
+
+ def invoke(self, arg_str, from_tty):
+ global tutorial_state, generated_code
+ print(
+ """
+Welcome to GDB! This quick tutorial should be enough to get
+you acquainted with essential commands for basic debugging.
+At any point, you can feel free to use 'help <command>' to
+learn more about the commands that are being suggested, or
+any other commands that you may have heard of. You may also
+type 'quit' at any point to quit this tutorial, and use it
+again to quit GDB entirely.
+
+First, GDB will generate a C file with example code for the
+debug session. The code will contain a slightly flawed
+implementation of a bubble sort, which is used to sort an
+array of size 6. After the function is called, the program
+will print either "sorted" or "not sorted" depending on
+the result. Finally, the program will return 0 if the array
+is *not* sorted, and 1 if it is sorted.
+
+Enter the desired name for the file. This is important to
+avoid a collision that could overwrite your local data.
+"""
+ )
+ generated_code = input("Leave empty for default, example_code.c: ")
+
+ if generated_code == "":
+ generated_code = "example_code.c"
+
+ self._generateFile()
+ tutorial_state = -1
+ gdb.events.before_prompt.connect(tutorial_hook)
+ gdb.events.exited.connect(tutorial_exit_hook)
+ return
+
+ # Keeping for historical context
+ try:
+ self.teachMainCommands()
+ except Exception:
+ print("leaving tutorial")
+
+ def _generateFile(self):
+ global generated_binary
+ code = """
+#include <stdio.h>
+
+int sorted(int *vec, int size);
+
+void swap(int *a, int *b)
+{
+ int tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+int sort(int *vec, int size)
+{
+ for (int i = 0; i < size; i++)
+ for (int j = i; j < size - 1; j++)
+ if (vec[j] >= vec[j+1])
+ swap (&vec[j], &vec[j+1]);
+}
+
+int main ()
+{
+ int vec[6] = {5, 4, 3, 2, 1, 0};
+
+ sort(vec, 6);
+ return !sorted(vec, 6);
+}
+
+int sorted(int *vec, int size)
+{
+ int ret = 0;
+ for (int i = 0; i < size - 1; i++)
+ {
+ if (vec[i] > vec[i+1])
+ {
+ printf("not ");
+ ret = 1;
+ break;
+ }
+ }
+ printf("sorted\\n");
+ return ret;
+}"""
+ try:
+ with open(generated_code, "w") as f:
+ f.write(code)
+ print("Example code was created successfully")
+ except Exception:
+ print("Unable to automate creation of example code.")
+ print("Please paste the following in a file named")
+ print(f'"{generated_code}"')
+ print(code)
+
+ print(
+ """
+Please enter the desired binary name for the example program.
+"""
+ )
+ generated_binary = input("Leave empty for the default name, a.out: ")
+ if generated_binary == "":
+ generated_binary = "a.out"
+
+ print(
+ """
+Finally, please enter the compilation line for your compiler of
+choice. If you don't know how to compile via command line, you
+can try using:"""
+ )
+ print(f" gcc {generated_code} -g -o {generated_binary}\n")
+ inp = input("Enter the compilation command: ")
+ if inp == "":
+ inp = "gcc " + generated_code + " -g -o " + generated_binary
+ gdb.execute(f"shell {inp}")
+
+ if gdb.convenience_variable("_shell_exitcode") != 0:
+ print("Compilation failed. This might have been because of")
+ print("a typo in your input, or the compiler not being in the")
+ print("path. Please ensure that the command line is correct:")
+ print(inp)
+ raise Exception("compilation fail")
+
+
+Tutorial()
diff --git a/gdb/top.c b/gdb/top.c
index f1d0baeb3f4..d811d718b65 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -1451,7 +1451,8 @@ print_gdb_hints (struct ui_file *stream)
gdb_assert (width > 0);
std::string docs_url = "http://www.gnu.org/software/gdb/documentation/";
- std::array<string_file, 4> styled_msg {
+ std::array<string_file, 5> styled_msg {
+ string_file (true),
string_file (true),
string_file (true),
string_file (true),
@@ -1466,6 +1467,8 @@ print_gdb_hints (struct ui_file *stream)
gdb_printf (&styled_msg[3],
_("Type \"%ps\" to search for commands related to \"word\"."),
styled_string (command_style.style (), "apropos word"));
+ gdb_printf (&styled_msg[4], _("To learn how to use GDB, type \"%ps\"."),
+ styled_string (command_style.style (), "tutorial"));
/* If there isn't enough space to display the longest URL in a boxed
style, then don't use the box, the terminal will break the output
base-commit: 483c5cc7764832b2944f99e5548103a7c1f89ea7
--
2.52.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] gdb: add tutorial command
2026-01-22 20:28 [PATCH v2] gdb: add tutorial command Guinevere Larsen
@ 2026-01-23 7:11 ` Eli Zaretskii
2026-01-23 14:14 ` Guinevere Larsen
2026-01-24 11:41 ` Hannes Domani
2026-01-26 16:13 ` Andrew Burgess
2 siblings, 1 reply; 11+ messages in thread
From: Eli Zaretskii @ 2026-01-23 7:11 UTC (permalink / raw)
To: Guinevere Larsen; +Cc: gdb-patches
> From: Guinevere Larsen <guinevere@redhat.com>
> Cc: Guinevere Larsen <guinevere@redhat.com>
> Date: Thu, 22 Jan 2026 17:28:34 -0300
>
> gdb/NEWS | 4 +
> gdb/data-directory/Makefile.in | 1 +
> gdb/doc/gdb.texinfo | 34 +++
> gdb/python/lib/gdb/command/tutorial.py | 404 +++++++++++++++++++++++++
> gdb/top.c | 5 +-
> 5 files changed, 447 insertions(+), 1 deletion(-)
> create mode 100644 gdb/python/lib/gdb/command/tutorial.py
Thanks.
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -80,6 +80,10 @@ show progress-bars enabled
> content, to be disabled (the set command), or to see if
> progress-bars are currently enabled or not (the show command).
>
> +tutorial
> + Guided example session with a generated C file, to teach an entirely
> + new user how to use GDB for basic debugging.
Perhaps "Start a guided example session..." would be more in-line with
the style of describing commands?
Also, I think we should document that this command is only available
if GDB was built with Python, both in NEWS and in the manual.
> +@item tutorial
> +This command starts an interactive tutorial, teaching you basic commands
> +and how you can chain them together for a successful debugging session.
> +Warning: If you have an ongoing debug session, this command will kill that
I would suggest to give "Warning" the @emph or @strong markup.
> +@smallexample
> +(@value{GDBP}) tutorial
This example takes quite a few lines, but we don't want it to be
broken between pages in arbitrary places, I think. So we should use
@group..@end group either around the entire text, or at least around
each paragraph of it.
> +Welcome to GDB! This quick tutorial should be enough to get
^^
There should be two spaces there.
> +This means you can use it to prototype a solution without
> +needing to recompile the inferior. Try it by setting 'vec'
> +to a sorted array, like by using:
> + print vec = {0, 1, 2, 3, 4, 5}
> +and see how the program would end, with the "continue"
> +command again.
Isn't it strange to teach newbies that to assign a value to a
variable, one must use the 'print' command? Why not "set variable"?
> + print("The solution didn't work. Try again!")
^^
> + elif tutorial_state == (len(state_string) - 1):
> + print(
> + "The tutorial is complete. Exiting tutorial and cleaning up the artifacts"
^^
Two spaces between sentences again.
> + This tutorial does not aim to be comprehensive. On the contrary,
^^
And here.
> + def invoke(self, arg_str, from_tty):
> + global tutorial_state, generated_code
> + print(
> + """
> +Welcome to GDB! This quick tutorial should be enough to get
^^
Likewise.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] gdb: add tutorial command
2026-01-23 7:11 ` Eli Zaretskii
@ 2026-01-23 14:14 ` Guinevere Larsen
2026-01-23 14:23 ` Arsen Arsenović
2026-01-23 14:28 ` Eli Zaretskii
0 siblings, 2 replies; 11+ messages in thread
From: Guinevere Larsen @ 2026-01-23 14:14 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 1039 bytes --]
Thanks for the review, I have made all the other changes you suggested!
On 1/23/26 4:11 AM, Eli Zaretskii wrote:
>> +This means you can use it to prototype a solution without
>> +needing to recompile the inferior. Try it by setting 'vec'
>> +to a sorted array, like by using:
>> + print vec = {0, 1, 2, 3, 4, 5}
>> +and see how the program would end, with the "continue"
>> +command again.
> Isn't it strange to teach newbies that to assign a value to a
> variable, one must use the 'print' command? Why not "set variable"?
It is a bit strange, I suppose, but "set" (like "call") are not things I
use personally, when print can do the same and is one fewer command to
remember
And this doubles as a warning that if the user calls a function that has
side-effects, those will also happen, and they aren't necessarily always
desired by the user...
I can change it if you feel strongly that we should use "set" instead,
but I find the side-effect explanation to be worth the unintuitiveness
--
Cheers,
Guinevere Larsen
It/she
[-- Attachment #2: Type: text/html, Size: 1689 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] gdb: add tutorial command
2026-01-23 14:14 ` Guinevere Larsen
@ 2026-01-23 14:23 ` Arsen Arsenović
2026-01-23 14:28 ` Eli Zaretskii
1 sibling, 0 replies; 11+ messages in thread
From: Arsen Arsenović @ 2026-01-23 14:23 UTC (permalink / raw)
To: Guinevere Larsen; +Cc: Eli Zaretskii, gdb-patches
[-- Attachment #1: Type: text/plain, Size: 1568 bytes --]
Guinevere Larsen <guinevere@redhat.com> writes:
> Thanks for the review, I have made all the other changes you suggested!
>
> On 1/23/26 4:11 AM, Eli Zaretskii wrote:
>>> +This means you can use it to prototype a solution without
>>> +needing to recompile the inferior. Try it by setting 'vec'
>>> +to a sorted array, like by using:
>>> + print vec = {0, 1, 2, 3, 4, 5}
>>> +and see how the program would end, with the "continue"
>>> +command again.
>> Isn't it strange to teach newbies that to assign a value to a
>> variable, one must use the 'print' command? Why not "set variable"?
>
> It is a bit strange, I suppose, but "set" (like "call") are not things I use
> personally, when print can do the same and is one fewer command to remember
>
> And this doubles as a warning that if the user calls a function that has
> side-effects, those will also happen, and they aren't necessarily always
> desired by the user...
>
> I can change it if you feel strongly that we should use "set" instead, but I
> find the side-effect explanation to be worth the unintuitiveness
FWIW, I use both set and call to avoid printing the result values as
appropriate (less screen real-estate, and fewer $n variables are my
reasons). I think the text is fine as-is, but would personally mention
it.
I do think mentioning that 'print' computes an arbitrary expression
including side effects is quite important, and if I was to mention call
and set, I'd define them in terms of print for that reason (as the
manual does).
--
Arsen Arsenović
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 418 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] gdb: add tutorial command
2026-01-23 14:14 ` Guinevere Larsen
2026-01-23 14:23 ` Arsen Arsenović
@ 2026-01-23 14:28 ` Eli Zaretskii
2026-01-26 14:54 ` Andrew Burgess
1 sibling, 1 reply; 11+ messages in thread
From: Eli Zaretskii @ 2026-01-23 14:28 UTC (permalink / raw)
To: Guinevere Larsen; +Cc: gdb-patches
> Date: Fri, 23 Jan 2026 11:14:20 -0300
> Cc: gdb-patches@sourceware.org
> From: Guinevere Larsen <guinevere@redhat.com>
>
> > Isn't it strange to teach newbies that to assign a value to a
> > variable, one must use the 'print' command? Why not "set variable"?
>
> It is a bit strange, I suppose, but "set" (like "call") are not things I use personally, when print can do the same
> and is one fewer command to remember
>
> And this doubles as a warning that if the user calls a function that has side-effects, those will also happen,
> and they aren't necessarily always desired by the user...
>
> I can change it if you feel strongly that we should use "set" instead, but I find the side-effect explanation to
> be worth the unintuitiveness
I think the tutorial should teach the commands intended for their
purposes. Some commands have side effects, but that's not newbie
stuff, IMO.
What do others think about this?
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] gdb: add tutorial command
2026-01-22 20:28 [PATCH v2] gdb: add tutorial command Guinevere Larsen
2026-01-23 7:11 ` Eli Zaretskii
@ 2026-01-24 11:41 ` Hannes Domani
2026-01-26 18:08 ` Guinevere Larsen
2026-01-26 16:13 ` Andrew Burgess
2 siblings, 1 reply; 11+ messages in thread
From: Hannes Domani @ 2026-01-24 11:41 UTC (permalink / raw)
To: gdb-patches, Guinevere Larsen
Am Donnerstag, 22. Januar 2026 um 21:30:04 MEZ hat Guinevere Larsen <guinevere@redhat.com> Folgendes geschrieben:
> Before this commit, there is little way for a new user to learn how to
> use GDB on their own. The documentation contains an example session,
> but that isn't contained in GDB itself, and the "help" and "apropos"
> commands exist, but they aren't the best to really teach what GDB can
> do, only to describe commands on their own.
>
> This commit changes this by introducing a command called "tutorial",
> which takes a page out of common design from the last few decades and
> provides a self-contained tutorial for users, walking them through a
> simple bug in C code, and explaining several commands in context.
>
> The tutorial is mostly implemented (ab)using the before_prompt hook to
> print the messages, so that users can have completion, history and so
> on, and it is implemented in python to make maintaining it in the future
> as simple as possible.
> ---
> +def cleanup():
> + print(
> + """
> +Thank you for taking this tutorial. We hope it has been
> +helpful for you. If you found any bugs or would like to
> +provide feedback, feel free to send do so through IRC, in
> +the #gdb room of libera.chat, or send an email to
> +gdb@sourceware.org.
> +To recap, these were the commands explained in the tutorial
> + * shell
The shell command was not explained.
> + * file
> + * start
> + * continue
> + * list
> + * break
> + * print
> + * step
> + * next
> + * display
> + * watch
> + * quit
> + * help
> + """
> + )
> + gdb.events.before_prompt.disconnect(tutorial_hook)
> + gdb.events.exited.disconnect(tutorial_exit_hook)
> + # Clean up example code.
At the end of the tutorial I get this warning:
warning: BFD: reopening /home/src/a.out: No such file or directory
I've silenced it by adding this line here:
gdb.execute("file")
But then I get these warnings instead:
Error in re-setting breakpoint 2: No source file named example_code.c.
Error in re-setting breakpoint 3: No symbol table is loaded. Use the "file" command.
So I've also added this line before the other:
gdb.execute("delete breakpoints")
Then all warnings were gone.
> + try:
> + f = open(generated_code)
> + f.close()
> + os.remove(generated_code)
> + except FileNotFoundError:
> + # File doesn't exist, nothing to do.
> + pass
> + try:
> + f = open(generated_binary)
> + f.close()
> + os.remove(generated_binary)
> + except FileNotFoundError:
> + # File doesn't exist, nothing to do.
> + pass
> +
Hannes
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] gdb: add tutorial command
2026-01-23 14:28 ` Eli Zaretskii
@ 2026-01-26 14:54 ` Andrew Burgess
2026-01-26 18:50 ` Guinevere Larsen
0 siblings, 1 reply; 11+ messages in thread
From: Andrew Burgess @ 2026-01-26 14:54 UTC (permalink / raw)
To: Eli Zaretskii, Guinevere Larsen; +Cc: gdb-patches
Eli Zaretskii <eliz@gnu.org> writes:
>> Date: Fri, 23 Jan 2026 11:14:20 -0300
>> Cc: gdb-patches@sourceware.org
>> From: Guinevere Larsen <guinevere@redhat.com>
>>
>> > Isn't it strange to teach newbies that to assign a value to a
>> > variable, one must use the 'print' command? Why not "set variable"?
>>
>> It is a bit strange, I suppose, but "set" (like "call") are not things I use personally, when print can do the same
>> and is one fewer command to remember
>>
>> And this doubles as a warning that if the user calls a function that has side-effects, those will also happen,
>> and they aren't necessarily always desired by the user...
>>
>> I can change it if you feel strongly that we should use "set" instead, but I find the side-effect explanation to
>> be worth the unintuitiveness
>
> I think the tutorial should teach the commands intended for their
> purposes. Some commands have side effects, but that's not newbie
> stuff, IMO.
>
> What do others think about this?
I agree Eli. I have always find it strange that some folk use print to
set variable. At least Guinevere did offer some explanation, which is
nice to know, but I do think that for beginner, using the intended
command would be a good idea.
That doesn't mean there's not room for some kind of:
Advanced hint: Some users use 'print' instead of 'set' for
setting inferior variables, this works because ... etc.
within the tutorial text.
Thanks,
Andrew
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] gdb: add tutorial command
2026-01-22 20:28 [PATCH v2] gdb: add tutorial command Guinevere Larsen
2026-01-23 7:11 ` Eli Zaretskii
2026-01-24 11:41 ` Hannes Domani
@ 2026-01-26 16:13 ` Andrew Burgess
2026-01-27 19:39 ` Guinevere Larsen
2 siblings, 1 reply; 11+ messages in thread
From: Andrew Burgess @ 2026-01-26 16:13 UTC (permalink / raw)
To: Guinevere Larsen, gdb-patches; +Cc: Guinevere Larsen
Guinevere Larsen <guinevere@redhat.com> writes:
> Before this commit, there is little way for a new user to learn how to
> use GDB on their own. The documentation contains an example session,
> but that isn't contained in GDB itself, and the "help" and "apropos"
> commands exist, but they aren't the best to really teach what GDB can
> do, only to describe commands on their own.
>
> This commit changes this by introducing a command called "tutorial",
> which takes a page out of common design from the last few decades and
> provides a self-contained tutorial for users, walking them through a
> simple bug in C code, and explaining several commands in context.
>
> The tutorial is mostly implemented (ab)using the before_prompt hook to
> print the messages, so that users can have completion, history and so
> on, and it is implemented in python to make maintaining it in the future
> as simple as possible.
> ---
> gdb/NEWS | 4 +
> gdb/data-directory/Makefile.in | 1 +
> gdb/doc/gdb.texinfo | 34 +++
> gdb/python/lib/gdb/command/tutorial.py | 404 +++++++++++++++++++++++++
> gdb/top.c | 5 +-
> 5 files changed, 447 insertions(+), 1 deletion(-)
> create mode 100644 gdb/python/lib/gdb/command/tutorial.py
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 74fc353d7e9..5c8c06ca301 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -80,6 +80,10 @@ show progress-bars enabled
> content, to be disabled (the set command), or to see if
> progress-bars are currently enabled or not (the show command).
>
> +tutorial
> + Guided example session with a generated C file, to teach an entirely
> + new user how to use GDB for basic debugging.
> +
> * Changed commands
>
> maintenance info program-spaces
> diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
> index 7257da9cd11..572b7e25e48 100644
> --- a/gdb/data-directory/Makefile.in
> +++ b/gdb/data-directory/Makefile.in
> @@ -93,6 +93,7 @@ PYTHON_FILE_LIST = \
> gdb/command/missing_files.py \
> gdb/command/pretty_printers.py \
> gdb/command/prompt.py \
> + gdb/command/tutorial.py \
> gdb/command/type_printers.py \
> gdb/command/unwinders.py \
> gdb/command/xmethods.py \
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 7059f73935c..99359f27822 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -2577,6 +2577,40 @@ system Readline library, or @samp{(internal)} indicating @value{GDBN}
> is using a statically linked in version of the Readline library.
> @end table
>
> +Finally, if you want to learn how to use GDB for simple debug session,
> +you can use the command @code{tutorial}.
> +
> +@table @code
> +@kindex tutorial
> +@item tutorial
> +This command starts an interactive tutorial, teaching you basic commands
> +and how you can chain them together for a successful debugging session.
> +Warning: If you have an ongoing debug session, this command will kill that
> +session and you'll have to start from scratch.
> +
> +@smallexample
> +(@value{GDBP}) tutorial
> +Welcome to GDB! This quick tutorial should be enough to get
> +you acquainted with essential commands for basic debugging.
> +At any point, you can feel free to use 'help <command>' to
> +learn more about the commands that are being suggested, or
> +any other commands that you may have heard of. You may also
> +type 'quit' at any point to quit this tutorial, and use it
> +again to quit GDB entirely.
> +
> +First, GDB will generate a C file with example code for the
> +debug session. The code will contain a slightly flawed
> +implementation of a bubble sort, which is used to sort an
> +array of size 6. After the function is called, the program
> +will print either "sorted" or "not sorted" depending on
> +the result. Finally, the program will return 0 if the array
> +is *not* sorted, and 1 if it is sorted.
> +
> +Enter the desired name for the file. This is important to
> +avoid a collision that could overwrite your local data.
> +@end smallexample
> +@end table
> +
> @node Running
> @chapter Running Programs Under @value{GDBN}
>
> diff --git a/gdb/python/lib/gdb/command/tutorial.py b/gdb/python/lib/gdb/command/tutorial.py
> new file mode 100644
> index 00000000000..7e0bd0331f0
> --- /dev/null
> +++ b/gdb/python/lib/gdb/command/tutorial.py
> @@ -0,0 +1,404 @@
> +# GDB 'tutorial' command.
> +# Copyright (C) 2025-2026 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/>.
> +
> +"""Implementation of the GDB 'tutorial' command using the GDB Python API."""
> +
> +import os
> +
> +import gdb
> +
> +tutorial_state = 0
> +display_counter = 0
> +
> +generated_code = None
> +generated_binary = None
> +
> +state_string = [
> + """
> +To start properly debugging, first the file, which GDB calls the
> +"inferior", needs to be loaded. That is how this tutorial
> +will refer to it from now on.
I'm not sure I'd agree with this. The inferior is the whole thing being
debugged, the executable, shared libraries, process memory. The
executable, which the 'file' command loads, is just one part of an
inferior.
I get we don't want to bike shed each and every word of the tutorial,
and this stuff can be polished later, so I guess, feel free to take my
nit-picking as much as you'd like.
> +Use the following command to load the inferior:""",
> + """
> +Next, you should verify that there really is a bug in the
> +example, by running the entire program. One way to do that
> +is by using the command "start", which runs the inferior
There is a reasonably new gdb.Style API, which would allow you to:
command_style = gdb.Style('command')
print(command_style.apply("start"))
this will print 'start' using the 'command' style.
Here you could do something like:
...is by using the command '""" + command_style.apply('start') + """',
which runs the inferior...
And you'll now have the command name styled.
Of course, there's probably better ways to do this. I think I'd be
tempted to support some kind of basic markup scheme and then filter the
strings before printing.
Also, in the original, "Welcome to GDB! This quick tutorial..." text you
wrap commands in single quotes, but here you switch to double quotes. I
would be nice to make this consistent. I changed it to single quotes in
my example above, but only because I didn't know if 4 double quotes in a
row would do the right thing.
> +until the first line of the main function, and then use the
> +command "continue", which runs the inferior until it is
> +forced to stop, such as by a breakpoint, signal or finishing.
> +""",
In stage 0 the tutorial text ends with a very definitive instruction for
what to do next:
Use the following command to load the inferior:
file a.out
In comparison the text here is more suggestive:
... One way to do that is by using the command "start", ...
While it's not rocket science, I do think I prefer the approach from
stage 0, even if it means duplicating some text. I think each stage
should end with a definitive list of what to do next:
Use the following two commands to run the inferior to completion:
start
continue
> + """
> +Notice the "not sorted" message, that is output of the inferior
> +showing the bug.
> +
> +You've verified that the bug is there, now you should check
> +if the bug is happening in the sort function or in the
> +verification function. Stop after the sorting function, to
> +be able to confirm if the array is sorted or not.
> +You can do so by using "list main" to find the line number
> +after the call to 'sort', then using the command "break <num>"
> +to break right before executing line '<num>'. Then, execute
> +the inferior again.
> +If the call to the function 'sort' was not visible, following
> +invocations of "list" will print following lines of the source
> +code.
> +""",
> + """
> +You can check the value of the variable with the command
> +"print vec". That command will evaluate any arbitrary
> +expression valid for the current language, with all side effects
> +that the expression would have.
> +This means you can use it to prototype a solution without
> +needing to recompile the inferior. Try it by setting 'vec'
> +to a sorted array, like by using:
> + print vec = {0, 1, 2, 3, 4, 5}
> +and see how the program would end, with the "continue"
> +command again.
> +""",
> + """
> +Now that you're sure that 'sorted' is working correctly, you
> +should get to the start of the 'sort' function. You can set a
> +breakpoint using the function name, or you can repeatedly use
> +the command "step" after "start", to move one line at a time,
> +and entering any function calls that happen.
> +""",
> + """
> +This is the end of the guided section, from now on, try and
> +debug the inferior on your own, you should have all the needed
> +tools.
> +
> +Some extra commands that may be helpful are:
> + "next" - like step, but skips functions
> + "display" - like print, but shows every time the inferior stops
> + "watch" - like breakpoint, but when a variable changes
> +Feel free to use "help <cmd>" to get more information on those
Elsewhere in the text you use 'help <command>' which I think is better.
But either way you should probably pick one.
> +commands.
> +
> +Additionally, if you'd like to see the entire array like it
> +was printed in the 'main' function, use the following syntax:
> + print (*vec)@6
> +
> +Once you understand the bug, or would like to quit the tutorial,
> +run the inferior until it exits and GDB will clean up after itself.
> +""",
> +]
> +
> +tutorial_reminders = [
> + "Please load the file with the previous command",
> + "You should run the inferior to make sure the bug exists",
> + "You need to stop in main, before the 'sorted' function is run",
> + "Use the print command to fix vec and see if 'sorted' works",
> + "Stop inside the 'sort' function",
> + "When done, run the inferior till the end to exit the tutorial",
> +]
> +
> +
> +# Print a message then get an input from the user
> +# The input must be one of: y, Y, n, N
> +# Return True if the user accepted (y or Y) False otherwise.
> +def get_yn_opt(message):
> + message += " [y/n]: "
> + valid = "yYnN"
> + opt = input(message)
> + while opt not in valid:
> + input("Please enter y or n: ")
> + return opt in "yY"
> +
> +
> +def inferior_in_location(first_line, last_line=None):
> + # This is in a try block because if the inferior isn't running,
> + # a "No stack" exception is thrown.
> + try:
> + loc = gdb.execute("frame", to_string=True)
> + loc = int(loc.split("\n")[1].split()[0])
> + if loc > first_line:
> + if last_line is None or loc <= last_line:
> + return True
> + except gdb.error:
> + # Exception is fine. This just means the inferior isn't
> + # running, so we can return false.
> + pass
> + return False
> +
> +
> +# Test if a binary is loaded with the expected name.
> +def inferior_is_loaded():
> + loaded = gdb.current_progspace().filename
> + if loaded is not None:
> + return loaded.endswith(generated_binary)
> + return False
> +
> +
> +# Increment state and reset counter since a tutorial message
> +# was last displayed.
> +def increment_state():
> + global tutorial_state, display_counter
> + tutorial_state += 1
> + display_counter = 0
> +
> +
> +# Function that holds all the logic to whether a step of the tutorial
> +# has been completed.
> +def should_advance_state():
> + # Tutorial has just started, immediately increment to print the
> + # first message.
> + if tutorial_state == -1:
> + return True
> + elif tutorial_state == 0:
> + return inferior_is_loaded()
> + # The only way to increment past states 1 and 3 is with the exit hook.
> + elif tutorial_state == 1 or tutorial_state == 3:
> + return False
> + elif tutorial_state == 2:
> + return inferior_in_location(25)
> + elif tutorial_state == 4:
> + return inferior_in_location(13, 20)
> + else:
> + return False
> +
> +
> +def cleanup():
> + print(
> + """
> +Thank you for taking this tutorial. We hope it has been
> +helpful for you. If you found any bugs or would like to
> +provide feedback, feel free to send do so through IRC, in
> +the #gdb room of libera.chat, or send an email to
> +gdb@sourceware.org.
> +To recap, these were the commands explained in the tutorial
> + * shell
> + * file
> + * start
> + * continue
> + * list
> + * break
> + * print
> + * step
> + * next
> + * display
> + * watch
> + * quit
> + * help
> + """
> + )
> + gdb.events.before_prompt.disconnect(tutorial_hook)
> + gdb.events.exited.disconnect(tutorial_exit_hook)
> + # Clean up example code.
> + try:
> + f = open(generated_code)
> + f.close()
> + os.remove(generated_code)
> + except FileNotFoundError:
> + # File doesn't exist, nothing to do.
> + pass
> + try:
> + f = open(generated_binary)
> + f.close()
> + os.remove(generated_binary)
> + except FileNotFoundError:
> + # File doesn't exist, nothing to do.
> + pass
> +
> +
> +# Hook for the "inferior exit" event. This is used to progress
> +# a few states of the tutorial, and to finish it when the user is done.
> +def tutorial_exit_hook(event):
> + if tutorial_state == 1:
> + increment_state()
> + elif tutorial_state == 3:
> + if event.exit_code == 1:
> + increment_state()
> + else:
> + print("The solution didn't work. Try again!")
> + elif tutorial_state == (len(state_string) - 1):
> + print(
> + "The tutorial is complete. Exiting tutorial and cleaning up the artifacts"
> + )
> + cleanup()
> + else:
> + opt = get_yn_opt(
> + "The tutorial program exited unexpectedly, "
> + + "would you like to quit the tutorial?"
> + )
> + if opt:
> + cleanup()
> +
> +
> +# Main way that the tutorial functionality is implemented.
> +# This is done by abusing before_prompt hooks so that we use GDB's
> +# readline implementation, history, completion and so on.
> +def tutorial_hook():
> + global display_counter
> +
> + if should_advance_state():
> + increment_state()
> + elif display_counter == 10:
> + print("Reminder:", tutorial_reminders[tutorial_state])
> + display_counter = 1
> +
> + if tutorial_state >= len(state_string):
> + cleanup()
> + gdb.events.before_prompt.disconnect(tutorial_hook)
> + elif display_counter == 0:
> + print(state_string[tutorial_state])
> + if tutorial_state == 0:
> + print(" file ", generated_binary)
> +
> + display_counter += 1
> +
> +
> +# Main implementation of the tutorial command.
> +class Tutorial(gdb.Command):
> + """Tutorial on the usage of the core commands of GDB.
> +
> + usage: tutorial
> +
> + This tutorial does not aim to be comprehensive. On the contrary,
> + it aims to be a quick way for you to start using GDB and learn
> + how to search for more commands that you may find useful.
> + """
> +
> + def __init__(self):
> + super(Tutorial, self).__init__(
> + name="tutorial", command_class=gdb.COMMAND_ESSENTIAL, prefix=False
> + )
> +
> + def invoke(self, arg_str, from_tty):
> + global tutorial_state, generated_code
> + print(
> + """
> +Welcome to GDB! This quick tutorial should be enough to get
> +you acquainted with essential commands for basic debugging.
> +At any point, you can feel free to use 'help <command>' to
> +learn more about the commands that are being suggested, or
> +any other commands that you may have heard of. You may also
> +type 'quit' at any point to quit this tutorial, and use it
> +again to quit GDB entirely.
> +
> +First, GDB will generate a C file with example code for the
> +debug session. The code will contain a slightly flawed
> +implementation of a bubble sort, which is used to sort an
> +array of size 6. After the function is called, the program
> +will print either "sorted" or "not sorted" depending on
> +the result. Finally, the program will return 0 if the array
> +is *not* sorted, and 1 if it is sorted.
> +
> +Enter the desired name for the file. This is important to
> +avoid a collision that could overwrite your local data.
> +"""
> + )
> + generated_code = input("Leave empty for default, example_code.c: ")
I tried running the tutorial in a 25 line terminal, and not long after
this input prompt you'll get a pagination request. The problem is that
GDB's pager doesn't understand that we've blocked at an input prompt,
giving users a chance to read what's on the screen.
I wonder if we need a new API, like: 'gdb.reset_pager()' which calls
'reinitialize_more_filter ()' (from utils.c). You'd then call this
immediately after each call to input() as you know at that point the
user has had a chance to read any previous text.
Or maybe we should provide a gdb.input() function which is like input,
but includes the clear for us?
Or maybe this doesn't need fixing at all for this commit, but is just
something to think about for the future.
> +
> + if generated_code == "":
> + generated_code = "example_code.c"
> +
> + self._generateFile()
> + tutorial_state = -1
> + gdb.events.before_prompt.connect(tutorial_hook)
> + gdb.events.exited.connect(tutorial_exit_hook)
> + return
> +
> + # Keeping for historical context
Missing period at the end.
> + try:
> + self.teachMainCommands()
> + except Exception:
> + print("leaving tutorial")
> +
> + def _generateFile(self):
> + global generated_binary
> + code = """
> +#include <stdio.h>
> +
> +int sorted(int *vec, int size);
> +
> +void swap(int *a, int *b)
> +{
> + int tmp = *a;
> + *a = *b;
> + *b = tmp;
> +}
> +
> +int sort(int *vec, int size)
> +{
> + for (int i = 0; i < size; i++)
> + for (int j = i; j < size - 1; j++)
> + if (vec[j] >= vec[j+1])
> + swap (&vec[j], &vec[j+1]);
> +}
> +
> +int main ()
> +{
> + int vec[6] = {5, 4, 3, 2, 1, 0};
> +
> + sort(vec, 6);
> + return !sorted(vec, 6);
> +}
> +
> +int sorted(int *vec, int size)
> +{
> + int ret = 0;
> + for (int i = 0; i < size - 1; i++)
> + {
> + if (vec[i] > vec[i+1])
> + {
> + printf("not ");
> + ret = 1;
> + break;
> + }
> + }
> + printf("sorted\\n");
> + return ret;
Shouldn't this code also adopt GNU coding standard?
> +}"""
> + try:
> + with open(generated_code, "w") as f:
> + f.write(code)
> + print("Example code was created successfully")
> + except Exception:
> + print("Unable to automate creation of example code.")
> + print("Please paste the following in a file named")
> + print(f'"{generated_code}"')
> + print(code)
> +
> + print(
> + """
> +Please enter the desired binary name for the example program.
> +"""
> + )
> + generated_binary = input("Leave empty for the default name, a.out: ")
> + if generated_binary == "":
> + generated_binary = "a.out"
> +
> + print(
> + """
> +Finally, please enter the compilation line for your compiler of
> +choice. If you don't know how to compile via command line, you
> +can try using:"""
> + )
> + print(f" gcc {generated_code} -g -o {generated_binary}\n")
> + inp = input("Enter the compilation command: ")
> + if inp == "":
> + inp = "gcc " + generated_code + " -g -o " + generated_binary
> + gdb.execute(f"shell {inp}")
> +
> + if gdb.convenience_variable("_shell_exitcode") != 0:
> + print("Compilation failed. This might have been because of")
> + print("a typo in your input, or the compiler not being in the")
> + print("path. Please ensure that the command line is correct:")
> + print(inp)
> + raise Exception("compilation fail")
> +
> +
> +Tutorial()
> diff --git a/gdb/top.c b/gdb/top.c
> index f1d0baeb3f4..d811d718b65 100644
> --- a/gdb/top.c
> +++ b/gdb/top.c
> @@ -1451,7 +1451,8 @@ print_gdb_hints (struct ui_file *stream)
> gdb_assert (width > 0);
>
> std::string docs_url = "http://www.gnu.org/software/gdb/documentation/";
> - std::array<string_file, 4> styled_msg {
> + std::array<string_file, 5> styled_msg {
> + string_file (true),
> string_file (true),
> string_file (true),
> string_file (true),
> @@ -1466,6 +1467,8 @@ print_gdb_hints (struct ui_file *stream)
> gdb_printf (&styled_msg[3],
> _("Type \"%ps\" to search for commands related to \"word\"."),
> styled_string (command_style.style (), "apropos word"));
> + gdb_printf (&styled_msg[4], _("To learn how to use GDB, type \"%ps\"."),
> + styled_string (command_style.style (), "tutorial"));
I think there's two possibilities here.
This additional line of text could be made conditional on HAVE_PYTHON,
so we only advertise the command when it's available.
Or, you can provide a default C++ implementation that prints a message
about the tutorial command requiring Python support. A Python command
will override a builtin C++ command, so if Python support is available,
that's what the user will see.
Thanks,
Andrew
>
> /* If there isn't enough space to display the longest URL in a boxed
> style, then don't use the box, the terminal will break the output
>
> base-commit: 483c5cc7764832b2944f99e5548103a7c1f89ea7
> --
> 2.52.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] gdb: add tutorial command
2026-01-24 11:41 ` Hannes Domani
@ 2026-01-26 18:08 ` Guinevere Larsen
0 siblings, 0 replies; 11+ messages in thread
From: Guinevere Larsen @ 2026-01-26 18:08 UTC (permalink / raw)
To: Hannes Domani, gdb-patches
On 1/24/26 8:41 AM, Hannes Domani wrote:
> Am Donnerstag, 22. Januar 2026 um 21:30:04 MEZ hat Guinevere Larsen <guinevere@redhat.com> Folgendes geschrieben:
>
>> Before this commit, there is little way for a new user to learn how to
>> use GDB on their own. The documentation contains an example session,
>> but that isn't contained in GDB itself, and the "help" and "apropos"
>> commands exist, but they aren't the best to really teach what GDB can
>> do, only to describe commands on their own.
>>
>> This commit changes this by introducing a command called "tutorial",
>> which takes a page out of common design from the last few decades and
>> provides a self-contained tutorial for users, walking them through a
>> simple bug in C code, and explaining several commands in context.
>>
>> The tutorial is mostly implemented (ab)using the before_prompt hook to
>> print the messages, so that users can have completion, history and so
>> on, and it is implemented in python to make maintaining it in the future
>> as simple as possible.
>> ---
>> +def cleanup():
>> + print(
>> + """
>> +Thank you for taking this tutorial. We hope it has been
>> +helpful for you. If you found any bugs or would like to
>> +provide feedback, feel free to send do so through IRC, in
>> +the #gdb room of libera.chat, or send an email to
>> +gdb@sourceware.org.
>> +To recap, these were the commands explained in the tutorial
>> + * shell
> The shell command was not explained.
Good point. It is also pretty advanced, I guess I can just remove from
here.
>
>
>> + * file
>> + * start
>> + * continue
>> + * list
>> + * break
>> + * print
>> + * step
>> + * next
>> + * display
>> + * watch
>> + * quit
I suppose I didn't explain quit anymore? I'll double check and possibly
remove this as well.
>> + * help
>> + """
>> + )
>> + gdb.events.before_prompt.disconnect(tutorial_hook)
>> + gdb.events.exited.disconnect(tutorial_exit_hook)
>> + # Clean up example code.
> At the end of the tutorial I get this warning:
> warning: BFD: reopening /home/src/a.out: No such file or directory
>
> I've silenced it by adding this line here:
> gdb.execute("file")
>
> But then I get these warnings instead:
> Error in re-setting breakpoint 2: No source file named example_code.c.
> Error in re-setting breakpoint 3: No symbol table is loaded. Use the "file" command.
>
> So I've also added this line before the other:
> gdb.execute("delete breakpoints")
>
> Then all warnings were gone.
These warnings happen when the target is mourning the inferior (ie,
making an internal cleanup of stuff). I don't understand all the cleanup
that GDB has to do, so I don't know if deleting all breakpoints is
enough or if we're still leaving cruft behind
In a 1:1 meeting with Andrew, he suggested instead that cleanup could be
called in the next "before_prompt" hook instead, so we're guaranteed to
have finished the internal cleanup but we do it before the user can
actually do anything.
--
Cheers,
Guinevere Larsen
It/she
>
>
>> + try:
>> + f = open(generated_code)
>> + f.close()
>> + os.remove(generated_code)
>> + except FileNotFoundError:
>> + # File doesn't exist, nothing to do.
>> + pass
>> + try:
>> + f = open(generated_binary)
>> + f.close()
>> + os.remove(generated_binary)
>> + except FileNotFoundError:
>> + # File doesn't exist, nothing to do.
>> + pass
>> +
>
> Hannes
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] gdb: add tutorial command
2026-01-26 14:54 ` Andrew Burgess
@ 2026-01-26 18:50 ` Guinevere Larsen
0 siblings, 0 replies; 11+ messages in thread
From: Guinevere Larsen @ 2026-01-26 18:50 UTC (permalink / raw)
To: Andrew Burgess, Eli Zaretskii; +Cc: gdb-patches
On 1/26/26 11:54 AM, Andrew Burgess wrote:
> Eli Zaretskii <eliz@gnu.org> writes:
>
>>> Date: Fri, 23 Jan 2026 11:14:20 -0300
>>> Cc: gdb-patches@sourceware.org
>>> From: Guinevere Larsen <guinevere@redhat.com>
>>>
>>>> Isn't it strange to teach newbies that to assign a value to a
>>>> variable, one must use the 'print' command? Why not "set variable"?
>>> It is a bit strange, I suppose, but "set" (like "call") are not things I use personally, when print can do the same
>>> and is one fewer command to remember
>>>
>>> And this doubles as a warning that if the user calls a function that has side-effects, those will also happen,
>>> and they aren't necessarily always desired by the user...
>>>
>>> I can change it if you feel strongly that we should use "set" instead, but I find the side-effect explanation to
>>> be worth the unintuitiveness
>> I think the tutorial should teach the commands intended for their
>> purposes. Some commands have side effects, but that's not newbie
>> stuff, IMO.
>>
>> What do others think about this?
> I agree Eli. I have always find it strange that some folk use print to
> set variable. At least Guinevere did offer some explanation, which is
> nice to know, but I do think that for beginner, using the intended
> command would be a good idea.
>
> That doesn't mean there's not room for some kind of:
>
> Advanced hint: Some users use 'print' instead of 'set' for
> setting inferior variables, this works because ... etc.
>
> within the tutorial text.
>
> Thanks,
> Andrew
>
Seeing as everyone seems to be in agreement with using set, I'll update
the text when I send a v3.
--
Cheers,
Guinevere Larsen
It/she
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] gdb: add tutorial command
2026-01-26 16:13 ` Andrew Burgess
@ 2026-01-27 19:39 ` Guinevere Larsen
0 siblings, 0 replies; 11+ messages in thread
From: Guinevere Larsen @ 2026-01-27 19:39 UTC (permalink / raw)
To: Andrew Burgess, gdb-patches
On 1/26/26 1:13 PM, Andrew Burgess wrote:
> Guinevere Larsen <guinevere@redhat.com> writes:
>
>> Before this commit, there is little way for a new user to learn how to
>> use GDB on their own. The documentation contains an example session,
>> but that isn't contained in GDB itself, and the "help" and "apropos"
>> commands exist, but they aren't the best to really teach what GDB can
>> do, only to describe commands on their own.
>>
>> This commit changes this by introducing a command called "tutorial",
>> which takes a page out of common design from the last few decades and
>> provides a self-contained tutorial for users, walking them through a
>> simple bug in C code, and explaining several commands in context.
>>
>> The tutorial is mostly implemented (ab)using the before_prompt hook to
>> print the messages, so that users can have completion, history and so
>> on, and it is implemented in python to make maintaining it in the future
>> as simple as possible.
>> ---
>> gdb/NEWS | 4 +
>> gdb/data-directory/Makefile.in | 1 +
>> gdb/doc/gdb.texinfo | 34 +++
>> gdb/python/lib/gdb/command/tutorial.py | 404 +++++++++++++++++++++++++
>> gdb/top.c | 5 +-
>> 5 files changed, 447 insertions(+), 1 deletion(-)
>> create mode 100644 gdb/python/lib/gdb/command/tutorial.py
>>
>> diff --git a/gdb/NEWS b/gdb/NEWS
>> index 74fc353d7e9..5c8c06ca301 100644
>> --- a/gdb/NEWS
>> +++ b/gdb/NEWS
>> @@ -80,6 +80,10 @@ show progress-bars enabled
>> content, to be disabled (the set command), or to see if
>> progress-bars are currently enabled or not (the show command).
>>
>> +tutorial
>> + Guided example session with a generated C file, to teach an entirely
>> + new user how to use GDB for basic debugging.
>> +
>> * Changed commands
>>
>> maintenance info program-spaces
>> diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
>> index 7257da9cd11..572b7e25e48 100644
>> --- a/gdb/data-directory/Makefile.in
>> +++ b/gdb/data-directory/Makefile.in
>> @@ -93,6 +93,7 @@ PYTHON_FILE_LIST = \
>> gdb/command/missing_files.py \
>> gdb/command/pretty_printers.py \
>> gdb/command/prompt.py \
>> + gdb/command/tutorial.py \
>> gdb/command/type_printers.py \
>> gdb/command/unwinders.py \
>> gdb/command/xmethods.py \
>> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
>> index 7059f73935c..99359f27822 100644
>> --- a/gdb/doc/gdb.texinfo
>> +++ b/gdb/doc/gdb.texinfo
>> @@ -2577,6 +2577,40 @@ system Readline library, or @samp{(internal)} indicating @value{GDBN}
>> is using a statically linked in version of the Readline library.
>> @end table
>>
>> +Finally, if you want to learn how to use GDB for simple debug session,
>> +you can use the command @code{tutorial}.
>> +
>> +@table @code
>> +@kindex tutorial
>> +@item tutorial
>> +This command starts an interactive tutorial, teaching you basic commands
>> +and how you can chain them together for a successful debugging session.
>> +Warning: If you have an ongoing debug session, this command will kill that
>> +session and you'll have to start from scratch.
>> +
>> +@smallexample
>> +(@value{GDBP}) tutorial
>> +Welcome to GDB! This quick tutorial should be enough to get
>> +you acquainted with essential commands for basic debugging.
>> +At any point, you can feel free to use 'help <command>' to
>> +learn more about the commands that are being suggested, or
>> +any other commands that you may have heard of. You may also
>> +type 'quit' at any point to quit this tutorial, and use it
>> +again to quit GDB entirely.
>> +
>> +First, GDB will generate a C file with example code for the
>> +debug session. The code will contain a slightly flawed
>> +implementation of a bubble sort, which is used to sort an
>> +array of size 6. After the function is called, the program
>> +will print either "sorted" or "not sorted" depending on
>> +the result. Finally, the program will return 0 if the array
>> +is *not* sorted, and 1 if it is sorted.
>> +
>> +Enter the desired name for the file. This is important to
>> +avoid a collision that could overwrite your local data.
>> +@end smallexample
>> +@end table
>> +
>> @node Running
>> @chapter Running Programs Under @value{GDBN}
>>
>> diff --git a/gdb/python/lib/gdb/command/tutorial.py b/gdb/python/lib/gdb/command/tutorial.py
>> new file mode 100644
>> index 00000000000..7e0bd0331f0
>> --- /dev/null
>> +++ b/gdb/python/lib/gdb/command/tutorial.py
>> @@ -0,0 +1,404 @@
>> +# GDB 'tutorial' command.
>> +# Copyright (C) 2025-2026 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/>.
>> +
>> +"""Implementation of the GDB 'tutorial' command using the GDB Python API."""
>> +
>> +import os
>> +
>> +import gdb
>> +
>> +tutorial_state = 0
>> +display_counter = 0
>> +
>> +generated_code = None
>> +generated_binary = None
>> +
>> +state_string = [
>> + """
>> +To start properly debugging, first the file, which GDB calls the
>> +"inferior", needs to be loaded. That is how this tutorial
>> +will refer to it from now on.
> I'm not sure I'd agree with this. The inferior is the whole thing being
> debugged, the executable, shared libraries, process memory. The
> executable, which the 'file' command loads, is just one part of an
> inferior.
>
> I get we don't want to bike shed each and every word of the tutorial,
> and this stuff can be polished later, so I guess, feel free to take my
> nit-picking as much as you'd like.
I think this is valuable feedback. I probably didn't really know the
difference between them.
I'll try to figure out some better wording for v3.
>
>> +Use the following command to load the inferior:""",
>> + """
>> +Next, you should verify that there really is a bug in the
>> +example, by running the entire program. One way to do that
>> +is by using the command "start", which runs the inferior
> There is a reasonably new gdb.Style API, which would allow you to:
>
> command_style = gdb.Style('command')
> print(command_style.apply("start"))
>
> this will print 'start' using the 'command' style.
>
> Here you could do something like:
>
> ...is by using the command '""" + command_style.apply('start') + """',
> which runs the inferior...
>
> And you'll now have the command name styled.
>
> Of course, there's probably better ways to do this. I think I'd be
> tempted to support some kind of basic markup scheme and then filter the
> strings before printing.
Good idea, I'll try to figure something like that out. Shouldn't be too
hard to do.
> Also, in the original, "Welcome to GDB! This quick tutorial..." text you
> wrap commands in single quotes, but here you switch to double quotes. I
> would be nice to make this consistent. I changed it to single quotes in
> my example above, but only because I didn't know if 4 double quotes in a
> row would do the right thing.
Yeah, I'll standardize. I have this bad habit of doing things and
forgetting what I chose the day before and just going ahead anyway.
>
>> +until the first line of the main function, and then use the
>> +command "continue", which runs the inferior until it is
>> +forced to stop, such as by a breakpoint, signal or finishing.
>> +""",
> In stage 0 the tutorial text ends with a very definitive instruction for
> what to do next:
>
> Use the following command to load the inferior:
> file a.out
>
> In comparison the text here is more suggestive:
>
> ... One way to do that is by using the command "start", ...
>
> While it's not rocket science, I do think I prefer the approach from
> stage 0, even if it means duplicating some text. I think each stage
> should end with a definitive list of what to do next:
>
> Use the following two commands to run the inferior to completion:
> start
> continue
I disagree. The reason is that information is much more likely to stick
in someone's brain if they had to work to get the answer. It's why a
class giving exposition on solving problems will be forgotten much
faster than practical exercises with proper guidance. This is something
I commonly hear in video game design critique, but holds true in most
software design I think... Think about how many "website tours" you've
taken that were immediately forgotten, but once you had to figure out
how to do it without the tour you remember.
The reason why the first step isn't vague is because it is just useful
for the tutorial, as I expect most users to be familiar with making a
`gdb <bin>` call, so they don't need to remember that step for the long
term. The rest of the tutorial wants to teach the user to reason with
the commands explained, so that they can solve new problems, rather than
teaching them the "correct" way to do something (especially since many
of these have no correct way to be done).
>
>> + """
>> +Notice the "not sorted" message, that is output of the inferior
>> +showing the bug.
>> +
>> +You've verified that the bug is there, now you should check
>> +if the bug is happening in the sort function or in the
>> +verification function. Stop after the sorting function, to
>> +be able to confirm if the array is sorted or not.
>> +You can do so by using "list main" to find the line number
>> +after the call to 'sort', then using the command "break <num>"
>> +to break right before executing line '<num>'. Then, execute
>> +the inferior again.
>> +If the call to the function 'sort' was not visible, following
>> +invocations of "list" will print following lines of the source
>> +code.
>> +""",
>> + """
>> +You can check the value of the variable with the command
>> +"print vec". That command will evaluate any arbitrary
>> +expression valid for the current language, with all side effects
>> +that the expression would have.
>> +This means you can use it to prototype a solution without
>> +needing to recompile the inferior. Try it by setting 'vec'
>> +to a sorted array, like by using:
>> + print vec = {0, 1, 2, 3, 4, 5}
>> +and see how the program would end, with the "continue"
>> +command again.
>> +""",
>> + """
>> +Now that you're sure that 'sorted' is working correctly, you
>> +should get to the start of the 'sort' function. You can set a
>> +breakpoint using the function name, or you can repeatedly use
>> +the command "step" after "start", to move one line at a time,
>> +and entering any function calls that happen.
>> +""",
>> + """
>> +This is the end of the guided section, from now on, try and
>> +debug the inferior on your own, you should have all the needed
>> +tools.
>> +
>> +Some extra commands that may be helpful are:
>> + "next" - like step, but skips functions
>> + "display" - like print, but shows every time the inferior stops
>> + "watch" - like breakpoint, but when a variable changes
>> +Feel free to use "help <cmd>" to get more information on those
> Elsewhere in the text you use 'help <command>' which I think is better.
> But either way you should probably pick one.
Agreed. Will update to be more verbose
>
>> +commands.
>> +
>> +Additionally, if you'd like to see the entire array like it
>> +was printed in the 'main' function, use the following syntax:
>> + print (*vec)@6
>> +
>> +Once you understand the bug, or would like to quit the tutorial,
>> +run the inferior until it exits and GDB will clean up after itself.
>> +""",
>> +]
>> +
>> +tutorial_reminders = [
>> + "Please load the file with the previous command",
>> + "You should run the inferior to make sure the bug exists",
>> + "You need to stop in main, before the 'sorted' function is run",
>> + "Use the print command to fix vec and see if 'sorted' works",
>> + "Stop inside the 'sort' function",
>> + "When done, run the inferior till the end to exit the tutorial",
>> +]
>> +
>> +
>> +# Print a message then get an input from the user
>> +# The input must be one of: y, Y, n, N
>> +# Return True if the user accepted (y or Y) False otherwise.
>> +def get_yn_opt(message):
>> + message += " [y/n]: "
>> + valid = "yYnN"
>> + opt = input(message)
>> + while opt not in valid:
>> + input("Please enter y or n: ")
>> + return opt in "yY"
>> +
>> +
>> +def inferior_in_location(first_line, last_line=None):
>> + # This is in a try block because if the inferior isn't running,
>> + # a "No stack" exception is thrown.
>> + try:
>> + loc = gdb.execute("frame", to_string=True)
>> + loc = int(loc.split("\n")[1].split()[0])
>> + if loc > first_line:
>> + if last_line is None or loc <= last_line:
>> + return True
>> + except gdb.error:
>> + # Exception is fine. This just means the inferior isn't
>> + # running, so we can return false.
>> + pass
>> + return False
>> +
>> +
>> +# Test if a binary is loaded with the expected name.
>> +def inferior_is_loaded():
>> + loaded = gdb.current_progspace().filename
>> + if loaded is not None:
>> + return loaded.endswith(generated_binary)
>> + return False
>> +
>> +
>> +# Increment state and reset counter since a tutorial message
>> +# was last displayed.
>> +def increment_state():
>> + global tutorial_state, display_counter
>> + tutorial_state += 1
>> + display_counter = 0
>> +
>> +
>> +# Function that holds all the logic to whether a step of the tutorial
>> +# has been completed.
>> +def should_advance_state():
>> + # Tutorial has just started, immediately increment to print the
>> + # first message.
>> + if tutorial_state == -1:
>> + return True
>> + elif tutorial_state == 0:
>> + return inferior_is_loaded()
>> + # The only way to increment past states 1 and 3 is with the exit hook.
>> + elif tutorial_state == 1 or tutorial_state == 3:
>> + return False
>> + elif tutorial_state == 2:
>> + return inferior_in_location(25)
>> + elif tutorial_state == 4:
>> + return inferior_in_location(13, 20)
>> + else:
>> + return False
>> +
>> +
>> +def cleanup():
>> + print(
>> + """
>> +Thank you for taking this tutorial. We hope it has been
>> +helpful for you. If you found any bugs or would like to
>> +provide feedback, feel free to send do so through IRC, in
>> +the #gdb room of libera.chat, or send an email to
>> +gdb@sourceware.org.
>> +To recap, these were the commands explained in the tutorial
>> + * shell
>> + * file
>> + * start
>> + * continue
>> + * list
>> + * break
>> + * print
>> + * step
>> + * next
>> + * display
>> + * watch
>> + * quit
>> + * help
>> + """
>> + )
>> + gdb.events.before_prompt.disconnect(tutorial_hook)
>> + gdb.events.exited.disconnect(tutorial_exit_hook)
>> + # Clean up example code.
>> + try:
>> + f = open(generated_code)
>> + f.close()
>> + os.remove(generated_code)
>> + except FileNotFoundError:
>> + # File doesn't exist, nothing to do.
>> + pass
>> + try:
>> + f = open(generated_binary)
>> + f.close()
>> + os.remove(generated_binary)
>> + except FileNotFoundError:
>> + # File doesn't exist, nothing to do.
>> + pass
>> +
>> +
>> +# Hook for the "inferior exit" event. This is used to progress
>> +# a few states of the tutorial, and to finish it when the user is done.
>> +def tutorial_exit_hook(event):
>> + if tutorial_state == 1:
>> + increment_state()
>> + elif tutorial_state == 3:
>> + if event.exit_code == 1:
>> + increment_state()
>> + else:
>> + print("The solution didn't work. Try again!")
>> + elif tutorial_state == (len(state_string) - 1):
>> + print(
>> + "The tutorial is complete. Exiting tutorial and cleaning up the artifacts"
>> + )
>> + cleanup()
>> + else:
>> + opt = get_yn_opt(
>> + "The tutorial program exited unexpectedly, "
>> + + "would you like to quit the tutorial?"
>> + )
>> + if opt:
>> + cleanup()
>> +
>> +
>> +# Main way that the tutorial functionality is implemented.
>> +# This is done by abusing before_prompt hooks so that we use GDB's
>> +# readline implementation, history, completion and so on.
>> +def tutorial_hook():
>> + global display_counter
>> +
>> + if should_advance_state():
>> + increment_state()
>> + elif display_counter == 10:
>> + print("Reminder:", tutorial_reminders[tutorial_state])
>> + display_counter = 1
>> +
>> + if tutorial_state >= len(state_string):
>> + cleanup()
>> + gdb.events.before_prompt.disconnect(tutorial_hook)
>> + elif display_counter == 0:
>> + print(state_string[tutorial_state])
>> + if tutorial_state == 0:
>> + print(" file ", generated_binary)
>> +
>> + display_counter += 1
>> +
>> +
>> +# Main implementation of the tutorial command.
>> +class Tutorial(gdb.Command):
>> + """Tutorial on the usage of the core commands of GDB.
>> +
>> + usage: tutorial
>> +
>> + This tutorial does not aim to be comprehensive. On the contrary,
>> + it aims to be a quick way for you to start using GDB and learn
>> + how to search for more commands that you may find useful.
>> + """
>> +
>> + def __init__(self):
>> + super(Tutorial, self).__init__(
>> + name="tutorial", command_class=gdb.COMMAND_ESSENTIAL, prefix=False
>> + )
>> +
>> + def invoke(self, arg_str, from_tty):
>> + global tutorial_state, generated_code
>> + print(
>> + """
>> +Welcome to GDB! This quick tutorial should be enough to get
>> +you acquainted with essential commands for basic debugging.
>> +At any point, you can feel free to use 'help <command>' to
>> +learn more about the commands that are being suggested, or
>> +any other commands that you may have heard of. You may also
>> +type 'quit' at any point to quit this tutorial, and use it
>> +again to quit GDB entirely.
>> +
>> +First, GDB will generate a C file with example code for the
>> +debug session. The code will contain a slightly flawed
>> +implementation of a bubble sort, which is used to sort an
>> +array of size 6. After the function is called, the program
>> +will print either "sorted" or "not sorted" depending on
>> +the result. Finally, the program will return 0 if the array
>> +is *not* sorted, and 1 if it is sorted.
>> +
>> +Enter the desired name for the file. This is important to
>> +avoid a collision that could overwrite your local data.
>> +"""
>> + )
>> + generated_code = input("Leave empty for default, example_code.c: ")
> I tried running the tutorial in a 25 line terminal, and not long after
> this input prompt you'll get a pagination request. The problem is that
> GDB's pager doesn't understand that we've blocked at an input prompt,
> giving users a chance to read what's on the screen.
>
> I wonder if we need a new API, like: 'gdb.reset_pager()' which calls
> 'reinitialize_more_filter ()' (from utils.c). You'd then call this
> immediately after each call to input() as you know at that point the
> user has had a chance to read any previous text.
>
> Or maybe we should provide a gdb.input() function which is like input,
> but includes the clear for us?
>
> Or maybe this doesn't need fixing at all for this commit, but is just
> something to think about for the future.
I think providing gdb.input is the best option, but if you wouldn't
mind, I'd leave this as a future improvement.
>
>> +
>> + if generated_code == "":
>> + generated_code = "example_code.c"
>> +
>> + self._generateFile()
>> + tutorial_state = -1
>> + gdb.events.before_prompt.connect(tutorial_hook)
>> + gdb.events.exited.connect(tutorial_exit_hook)
>> + return
>> +
>> + # Keeping for historical context
> Missing period at the end.
oops, this was a note for myself, deleted everything past the return
>
>> + try:
>> + self.teachMainCommands()
>> + except Exception:
>> + print("leaving tutorial")
>> +
>> + def _generateFile(self):
>> + global generated_binary
>> + code = """
>> +#include <stdio.h>
>> +
>> +int sorted(int *vec, int size);
>> +
>> +void swap(int *a, int *b)
>> +{
>> + int tmp = *a;
>> + *a = *b;
>> + *b = tmp;
>> +}
>> +
>> +int sort(int *vec, int size)
>> +{
>> + for (int i = 0; i < size; i++)
>> + for (int j = i; j < size - 1; j++)
>> + if (vec[j] >= vec[j+1])
>> + swap (&vec[j], &vec[j+1]);
>> +}
>> +
>> +int main ()
>> +{
>> + int vec[6] = {5, 4, 3, 2, 1, 0};
>> +
>> + sort(vec, 6);
>> + return !sorted(vec, 6);
>> +}
>> +
>> +int sorted(int *vec, int size)
>> +{
>> + int ret = 0;
>> + for (int i = 0; i < size - 1; i++)
>> + {
>> + if (vec[i] > vec[i+1])
>> + {
>> + printf("not ");
>> + ret = 1;
>> + break;
>> + }
>> + }
>> + printf("sorted\\n");
>> + return ret;
> Shouldn't this code also adopt GNU coding standard?
yeah fair point. Updated
>
>> +}"""
>> + try:
>> + with open(generated_code, "w") as f:
>> + f.write(code)
>> + print("Example code was created successfully")
>> + except Exception:
>> + print("Unable to automate creation of example code.")
>> + print("Please paste the following in a file named")
>> + print(f'"{generated_code}"')
>> + print(code)
>> +
>> + print(
>> + """
>> +Please enter the desired binary name for the example program.
>> +"""
>> + )
>> + generated_binary = input("Leave empty for the default name, a.out: ")
>> + if generated_binary == "":
>> + generated_binary = "a.out"
>> +
>> + print(
>> + """
>> +Finally, please enter the compilation line for your compiler of
>> +choice. If you don't know how to compile via command line, you
>> +can try using:"""
>> + )
>> + print(f" gcc {generated_code} -g -o {generated_binary}\n")
>> + inp = input("Enter the compilation command: ")
>> + if inp == "":
>> + inp = "gcc " + generated_code + " -g -o " + generated_binary
>> + gdb.execute(f"shell {inp}")
>> +
>> + if gdb.convenience_variable("_shell_exitcode") != 0:
>> + print("Compilation failed. This might have been because of")
>> + print("a typo in your input, or the compiler not being in the")
>> + print("path. Please ensure that the command line is correct:")
>> + print(inp)
>> + raise Exception("compilation fail")
>> +
>> +
>> +Tutorial()
>> diff --git a/gdb/top.c b/gdb/top.c
>> index f1d0baeb3f4..d811d718b65 100644
>> --- a/gdb/top.c
>> +++ b/gdb/top.c
>> @@ -1451,7 +1451,8 @@ print_gdb_hints (struct ui_file *stream)
>> gdb_assert (width > 0);
>>
>> std::string docs_url = "http://www.gnu.org/software/gdb/documentation/";
>> - std::array<string_file, 4> styled_msg {
>> + std::array<string_file, 5> styled_msg {
>> + string_file (true),
>> string_file (true),
>> string_file (true),
>> string_file (true),
>> @@ -1466,6 +1467,8 @@ print_gdb_hints (struct ui_file *stream)
>> gdb_printf (&styled_msg[3],
>> _("Type \"%ps\" to search for commands related to \"word\"."),
>> styled_string (command_style.style (), "apropos word"));
>> + gdb_printf (&styled_msg[4], _("To learn how to use GDB, type \"%ps\"."),
>> + styled_string (command_style.style (), "tutorial"));
> I think there's two possibilities here.
>
> This additional line of text could be made conditional on HAVE_PYTHON,
> so we only advertise the command when it's available.
>
> Or, you can provide a default C++ implementation that prints a message
> about the tutorial command requiring Python support. A Python command
> will override a builtin C++ command, so if Python support is available,
> that's what the user will see.
Oh, I didn't know that this is how it worked. That makes it a lot easier
to make the fallback command! I think I'll go that route
>
> Thanks,
> Andrew
>
>>
>> /* If there isn't enough space to display the longest URL in a boxed
>> style, then don't use the box, the terminal will break the output
>>
>> base-commit: 483c5cc7764832b2944f99e5548103a7c1f89ea7
>> --
>> 2.52.0
--
Cheers,
Guinevere Larsen
It/she
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-01-27 19:40 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-01-22 20:28 [PATCH v2] gdb: add tutorial command Guinevere Larsen
2026-01-23 7:11 ` Eli Zaretskii
2026-01-23 14:14 ` Guinevere Larsen
2026-01-23 14:23 ` Arsen Arsenović
2026-01-23 14:28 ` Eli Zaretskii
2026-01-26 14:54 ` Andrew Burgess
2026-01-26 18:50 ` Guinevere Larsen
2026-01-24 11:41 ` Hannes Domani
2026-01-26 18:08 ` Guinevere Larsen
2026-01-26 16:13 ` Andrew Burgess
2026-01-27 19:39 ` Guinevere Larsen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox