From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id jpjOBeCSd2kxRBYAWB0awg (envelope-from ) for ; Mon, 26 Jan 2026 11:14:24 -0500 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=JexhQSIt; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 06E701E08D; Mon, 26 Jan 2026 11:14:24 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 3D5BF1E08D for ; Mon, 26 Jan 2026 11:14:22 -0500 (EST) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id A8AF24BC7EDF for ; Mon, 26 Jan 2026 16:14:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A8AF24BC7EDF Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=JexhQSIt Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 0D6C34B920ED for ; Mon, 26 Jan 2026 16:13:12 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 0D6C34B920ED Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 0D6C34B920ED Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1769443992; cv=none; b=Nq1ylvrhTVe+2Xz7RpAs2FytP5fkODjQmuhcIydu4muPDvuBdcoVkquI1H1I5nL+Pzb6raz/MBYI1z62c4hObNhnLqAE/KpPShUhzsKfls1fvuopudo61j3R8dil4jls/nE+F2K84i8mosFLUVwZtuYIg6Yodf7JxGXqdMGo+bY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1769443992; c=relaxed/simple; bh=OXq5Et0RNcDpD0MYFYo+4AZyjqvXZmcAhLnQHI4Mb0Y=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=AsuyRqrHpuqNo7UHMnoJ5pBGK3NGyO6KGVctceNMsmsO9OxTwuEPgz8WJUMcKqKl1/hJROO5pexSlkrfj1yl0MsbkW1HwHZEtn+OkVKmLtKGYDUsnal7lncV0gsgMlD6MPCMxpriyfBqPkhh7H2rJVjKfmWt3tauB5WigvfQQls= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0D6C34B920ED DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1769443991; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=N00np6PhDwDuY9Xh7rddwKj3uj2dTUfAxoh9e0kHdD4=; b=JexhQSItLo17WJlTS85GXJR2SbLGu+lss+SYVJpMtCQ7MQJd4bg5P1f42SaWwRii6p14Qy Xh2XqdRj3Ys4W0jBC1L7eDSDYyJWYa0VQhH1xv5A1BLSXHx72fIY4zXs9UCm8bfXkLj8xD UGAEr7lhheBjiIp6Y76tSYrGNeictXA= Received: from mail-wr1-f70.google.com (mail-wr1-f70.google.com [209.85.221.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-357-40bXnfuyM9iynLvfEiRCpg-1; Mon, 26 Jan 2026 11:13:09 -0500 X-MC-Unique: 40bXnfuyM9iynLvfEiRCpg-1 X-Mimecast-MFC-AGG-ID: 40bXnfuyM9iynLvfEiRCpg_1769443989 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-432db1a9589so3604755f8f.0 for ; Mon, 26 Jan 2026 08:13:09 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769443988; x=1770048788; h=mime-version:message-id:date:references:in-reply-to:subject:cc:to :from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=N00np6PhDwDuY9Xh7rddwKj3uj2dTUfAxoh9e0kHdD4=; b=NAfxHQozpJwC9aMm3e25NRgt1RB3MhgBWwqbvRM7qHdaJCUUUIDaqYTiI0gLODOrYT QlETjPAZm2Tpz8/+9nwAekVnQwVsKSzeEZdUY7yFADVCFGKlaYmnnv2dKk/KP0Efm0Py 2AdiqMZMnhvBzERv/gV37s51estgwVChkhgexXSVsrV1SHWGnnmjp9KeJKh8euhbjTJn EgLlrY2paq9Jk5hUeVLL7vBxa1qy375rI8p49mKGIdz9nrK7cfoz2ohk3kxMhbhNrlg+ fdimjMjsqdwNNJGhjonp3trbvhepA/q5BjOEy609YI5wtcWoSh0+8GPdPx3PMb45FM60 7xLg== X-Forwarded-Encrypted: i=1; AJvYcCWwVKVoR6XTDaIuNcTQu9w5HwBmO78mz2vF98RUPjTMrjeY/jKv9QgszsKTXteJY2f1Sqt8UjZA9NsdcA==@sourceware.org X-Gm-Message-State: AOJu0YzZTpPwbmIv2Re/dcKoOGUJ46gRtxI1EGmtJ4gystNwk7aqVLHS 0QgxNtfmmRhlBcoyFriBt6iJFvDiPKxWV6sjS/I2KhKDKnl2iB39yYmmz2eNAKUl/2TWZeoiZRG nSUb2mBaptGuPq746LQdLnSQoJ/Ln8jwjKkM39eB5yORLboR8BY0HYZKEUHeNCVjD3WvZArM= X-Gm-Gg: AZuq6aJogN+k2MpZ1q09cvzozHxlKKljGGuLA2MUAiRMSj/OwMqzZ/YUx7+qQ5S9ogV ck+SZB+Y2n62IRuMT1wmrTH1rFSBj/qB+TL38FPPmFM7p0mYP76e4iDeDA7wad6t+DbuP5yKqX7 iWQeMXTyzvzRUvkzjTBFfs3YcbtY4TsBbnqEQFlfIidfHIuoVYyXZUl9uHJmtpWIAkNo5H1hVWW X5tMLFsOxlfKL+6MqJlFFAfJUPGT8K6a5/H0MtifO7FZ8N921pWlRy5kH9VKDlApc653129EuhB 6omJtglObrssiyfhFdeYZwigNPNnYbExwehZRQM1BY3uigUOSzkUyBttVtWE2RoDktdcQO5nvoT vYk4k X-Received: by 2002:a05:6000:4383:b0:435:8ec5:d27b with SMTP id ffacd0b85a97d-435ca1624bdmr9900378f8f.26.1769443988039; Mon, 26 Jan 2026 08:13:08 -0800 (PST) X-Received: by 2002:a05:6000:4383:b0:435:8ec5:d27b with SMTP id ffacd0b85a97d-435ca1624bdmr9900303f8f.26.1769443987167; Mon, 26 Jan 2026 08:13:07 -0800 (PST) Received: from localhost ([31.111.84.232]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-435b1f74942sm30025744f8f.36.2026.01.26.08.13.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Jan 2026 08:13:06 -0800 (PST) From: Andrew Burgess To: Guinevere Larsen , gdb-patches@sourceware.org Cc: Guinevere Larsen Subject: Re: [PATCH v2] gdb: add tutorial command In-Reply-To: <20260122202834.393095-1-guinevere@redhat.com> References: <20260122202834.393095-1-guinevere@redhat.com> Date: Mon, 26 Jan 2026 16:13:05 +0000 Message-ID: <87wm14fj3i.fsf@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: MllqD4ZeUaKTFBAPx9x-xeFQJ-CiFdkTy84qFTjPAIQ_1769443989 X-Mimecast-Originator: redhat.com Content-Type: text/plain X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org Guinevere Larsen 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 ' 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 . > + > +"""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 " > +to break right before executing line ''. 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 " to get more information on those Elsewhere in the text you use 'help ' 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 ' 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 > + > +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 styled_msg { > + std::array 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