From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id cdCtIG5ovWlJijEAWB0awg (envelope-from ) for ; Fri, 20 Mar 2026 11:31:58 -0400 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=Zm3EFXEi; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 812BC1E08C; Fri, 20 Mar 2026 11:31:58 -0400 (EDT) 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 09EE61E08C for ; Fri, 20 Mar 2026 11:31:57 -0400 (EDT) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 0C1344C318BB for ; Fri, 20 Mar 2026 15:31:56 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0C1344C318BB 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=Zm3EFXEi 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 25B334BB5906 for ; Fri, 20 Mar 2026 15:31:18 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 25B334BB5906 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 25B334BB5906 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=1774020678; cv=none; b=hqoy1MZwUSaRam1RnXcQR9PcZ3oGYB5NQl6y3zNOTgHKEwu6iBjIxZsGPiXxH1QB6X4u07y46nYxF1XnVaRjIIA74vgqEIOcxxYyUgfaRga8nain9jLabBQrMLIjsXhplzvDj/GONeqcKYbjwz+kH/kcL/axg1P/RtdzLhyj2VY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774020678; c=relaxed/simple; bh=xn0xhmYnAPPgZ0BSqij5WnctUX7xpkrIGekS82NPaLI=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=hFvv0/G/iRbzJQc4ApxHliSS+WY+Z8LSMzExI2nsSHseKFnOPYMyk0td8UPGGLtCuJLCxGPNhm7oGV7qZsAvGak9Fjy72ioyr2sCksAmMW0hzDPJBRQCSdX/5rkz1aMTJ9WvWWVmriQdGBgzdBlQPhIQqTRwf21k4VkXPQDJQAs= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 25B334BB5906 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774020677; 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: content-transfer-encoding:content-transfer-encoding; bh=1cFrLr6/+GhE3AKt/M5xYl22lWEALhnvuIR3zR5qC70=; b=Zm3EFXEiWu9nu65mOpmHH7jX/ccR6k5ASXM9IqJOG4zuzKlFSkXWfRuL6+aDCwvrT/TjD8 JZ6VUM1UtIADjJ24GNw5/rMgz+33rgKA50DVHrDc/VEXe7Je6ukWinMhEK9rRh8nsmAbYx nzzyuXLxsjQLjc1BMWfGqFtP7GJEPS4= Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-353-it1-MvKbMgytgPfOL_DhSA-1; Fri, 20 Mar 2026 11:31:16 -0400 X-MC-Unique: it1-MvKbMgytgPfOL_DhSA-1 X-Mimecast-MFC-AGG-ID: it1-MvKbMgytgPfOL_DhSA_1774020675 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-4852cf0318dso20063415e9.3 for ; Fri, 20 Mar 2026 08:31:16 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774020675; x=1774625475; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=1cFrLr6/+GhE3AKt/M5xYl22lWEALhnvuIR3zR5qC70=; b=maYLfRo1dzooB0slGmhunjEFy4pEai5IkCCcFFbmkwdJ1Kyyc4wVQ4IentSe9E0PQ1 BCBEvMjM8nkvJlXfSoWjNeCDoP5QJFbLUvrDVlAwXGh43+kOCGwTb0woYvD9x3RIldu8 IhMOkG9xigfbB/Hozi/N5kkXEZeAwKxdYeMWLZHHyhShr65m02CU+zQeyABUsjtq+Hu+ BF35s2yX4o1xoLft5g7s/eEmWRnfwPBR7PxX0b1oVLIZPk6zkxJQCRnLYQVxm2jK2fJx FcRu3nQ7cDHpxBFwAW1WhWTb4u9pGuUSYwd9+8E9pPMRm6V2gFnxhlsHd9lxFHB9zYK7 WhUg== X-Gm-Message-State: AOJu0YzZ/M0r84EtFhSz+QqmOaEsaSN21zFML6eMPjBPz/jbgALgk4q1 rWKlv6Y+MHthu/xlYQg5qpKc6eYAlmhKBSzZvkYTcbTHen8f6lgGPSnVmIFJJd816AyW4H0belN 5RNsRmt/1b7cCm9K9vvoavt3gVKk9/TkrZ53btCx90uucBwQOD5vUBwsbcgJuq40feXRhrqGm9M 35n6wg8l/7C1+lehdZu5W5ncMmrQcLe1CT8SidafijmaqGH2A= X-Gm-Gg: ATEYQzyBgtdjbPtH18a9VuMjHBfmzidwsSxnnvpwmVNL7ElFdG/xzcwMFtaufsrWNKD OH9EK6QSvn5lg4Vl1E0BD8IpBQG0dbzNnPl94LQQuQ4xFcmnz0CJemmfKFTKI8GB9iewe5cbB3R kRfEtClMGZ6lxeGgtZLZUtDArQCMQZ47/YnzZ0OZqqnoTH9CcTvCHZgtgeIXviCYxdKe4UcFwpk GDmcXagKtb/YN3ffoUEn6jUwUsuQhLfPlvNnyWRUYIqgZ4n/XzPmFVUAgFHULqlLYzQraBe2BPH 34Hif9pZQaxfOoYFEZIbbm+NJ9v1FcgjqU0CaiusOuI1tS7KGsufUHIT7jaR9eIYVsrx+b6gl30 PTRyRbl8VL2H57YbV X-Received: by 2002:a05:600c:c167:b0:483:9139:4c1d with SMTP id 5b1f17b1804b1-486fedd4143mr54500775e9.14.1774020674931; Fri, 20 Mar 2026 08:31:14 -0700 (PDT) X-Received: by 2002:a05:600c:c167:b0:483:9139:4c1d with SMTP id 5b1f17b1804b1-486fedd4143mr54500035e9.14.1774020674171; Fri, 20 Mar 2026 08:31:14 -0700 (PDT) Received: from localhost ([31.111.84.232]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-486f8aacc73sm128993565e9.0.2026.03.20.08.31.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Mar 2026 08:31:13 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH] gdb/python: allow Architecture.disassemble to give styled output Date: Fri, 20 Mar 2026 15:31:11 +0000 Message-Id: <2412723aff2f39ed778743b957d2ab8da51335e1.1774020654.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: sVLrV0-ONgtgJp98xixl0rhkmOI3sKnv6TUpD6ns7Us_1774020675 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true 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 Extend the Architecture.disassemble API to allow the user to request styled disassembler output via a new styling argument. A user can now write: insn = arch.disassemble(address, styling = True) The instruction strings returned within INSN will contain ANSI escape sequences so long as 'set style enabled on' is in effect. This means that the user's personal settings (disabling styling) will override a GDB extension that requests styled disassembler output. I think this makes sense. The default for the styling argument is False, this maintains the current unstyled output as default. --- gdb/NEWS | 5 + gdb/doc/python.texi | 7 +- gdb/python/py-arch.c | 11 +- .../gdb.python/py-arch-disasm-style.c | 52 ++++++++ .../gdb.python/py-arch-disasm-style.exp | 119 ++++++++++++++++++ 5 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 gdb/testsuite/gdb.python/py-arch-disasm-style.c create mode 100644 gdb/testsuite/gdb.python/py-arch-disasm-style.exp diff --git a/gdb/NEWS b/gdb/NEWS index e46a5108272..da74c248c54 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -229,6 +229,11 @@ qExecAndArgs the appropriate user setting is enabled, and GDB knows how to style this source file. + ** The Architecture.disassemble method accepts a new 'styling' + argument, which defaults to False. When set to True the 'asm' + strings in the disassembler output can contain ANSI escape + sequences to indicate styling. + * Guile API ** Procedures 'memory-port-read-buffer-size', diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 2df3b7c0423..e9ca0d67792 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -7632,7 +7632,7 @@ Architectures In Python Return the name (string value) of the architecture. @end defun -@defun Architecture.disassemble (start_pc @r{[}, end_pc @r{[}, count@r{]]}) +@defun Architecture.disassemble (start_pc @r{[}, end_pc @r{[}, count@r{]]} @w{@r{[}, styling = @code{False}@r{]}}) Return a list of disassembled instructions starting from the memory address @var{start_pc}. The optional arguments @var{end_pc} and @var{count} determine the number of instructions in the returned list. @@ -7661,6 +7661,11 @@ Architectures In Python language flavor used is the same as that specified by the current CLI variable @code{disassembly-flavor}. @xref{Machine Code}. +When the optional argument @var{styling} is @code{True} the @var{asm} +string can contain ANSI terminal escape sequences if styling is +enabled (@pxref{Output Styling}). When @var{styling} is @code{False}, +which is the default, then no styling will be applied to @var{asm}. + @item length The value corresponding to this key is the length (integer value) of the instruction in bytes. diff --git a/gdb/python/py-arch.c b/gdb/python/py-arch.c index f40d7da1763..4031f925b04 100644 --- a/gdb/python/py-arch.c +++ b/gdb/python/py-arch.c @@ -124,18 +124,21 @@ archpy_name (PyObject *self, PyObject *args) static PyObject * archpy_disassemble (PyObject *self, PyObject *args, PyObject *kw) { - static const char *keywords[] = { "start_pc", "end_pc", "count", NULL }; + static const char *keywords[] = { + "start_pc", "end_pc", "count", "styling", nullptr + }; CORE_ADDR start = 0, end = 0; CORE_ADDR pc; long count = 0, i; PyObject *start_obj = nullptr, *end_obj = nullptr, *count_obj = nullptr; struct gdbarch *gdbarch = NULL; + int styling_p = 0; ARCHPY_REQUIRE_VALID (self, gdbarch); - if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "O|OO", + if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "O|OOp", keywords, &start_obj, &end_obj, - &count_obj)) + &count_obj, &styling_p)) return NULL; if (get_addr_from_python (start_obj, &start) < 0) @@ -190,7 +193,7 @@ archpy_disassemble (PyObject *self, PyObject *args, PyObject *kw) if (PyList_Append (result_list.get (), insn_dict.get ())) return NULL; /* PyList_Append Sets the exception. */ - string_file stb; + string_file stb (styling_p); try { diff --git a/gdb/testsuite/gdb.python/py-arch-disasm-style.c b/gdb/testsuite/gdb.python/py-arch-disasm-style.c new file mode 100644 index 00000000000..ec2ae965552 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-arch-disasm-style.c @@ -0,0 +1,52 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 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 . */ + +volatile int global_var = 0; + +/* This only exists so we can call it from main. */ + +void +worker_func (void) +{ + global_var = global_var + 2; + global_var = global_var + 2; + global_var = global_var + 2; + global_var = global_var + 2; +} + +/* It's all nonsense in this function. We just want 'main' to be more than + 10 instructions long. */ + +int +main (void) +{ + int i; + + global_var = global_var + 2; + + for (int i = 0; i < 10; ++i) + global_var = global_var + i; + + worker_func (); + + global_var = global_var + 2; + global_var = global_var + 2; + global_var = global_var + 2; + global_var = global_var + 2; + + return global_var; +} diff --git a/gdb/testsuite/gdb.python/py-arch-disasm-style.exp b/gdb/testsuite/gdb.python/py-arch-disasm-style.exp new file mode 100644 index 00000000000..2c38a62b80e --- /dev/null +++ b/gdb/testsuite/gdb.python/py-arch-disasm-style.exp @@ -0,0 +1,119 @@ +# Copyright 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 . + +# Test styling for the Architecture.disassemble method. + +load_lib gdb-python.exp +require allow_python_tests + +standard_testfile + +if { [build_executable "failed to build" ${testfile} ${srcfile}] } { + return +} + +with_ansi_styling_terminal { + clean_restart $testfile +} + +if {![runto_main]} { + return +} + +# A new command 'py-disasm' which takes an address and disassembles 10 +# instructions starting from that address. The disassembly will +# include styling. +gdb_test_multiline "python disasm command" \ + "python" "" \ + "class py_disasm_cmd(gdb.Command):" "" \ + " def __init__(self):" "" \ + " super().__init__(\"py-disasm\", gdb.COMMAND_OBSCURE)" "" \ + " def invoke(self, args, from_tty):" "" \ + " argv = gdb.string_to_argv(args)" "" \ + " inf = gdb.selected_inferior ()" "" \ + " arch = inf.architecture ()" "" \ + " addr = gdb.parse_and_eval(argv\[0\])" "" \ + " insn = arch.disassemble(addr.address, count = 10, styling = True)" "" \ + " for i in insn:" "" \ + " formatted_addr = gdb.format_address(i\['addr'\])" "" \ + " print(\"0x%x: \t %s\" % (i\['addr'\], i\['asm'\]))" "" \ + "py_disasm_cmd()" "" \ + "end" "" + +# Run CMD which will disassemble 10 instructions. Capture those +# instructions into a list where each list item is itself a list of +# the form [ADDRESS, STRING], where STRING is the disassembled +# instruction. +# +# Returns the list of disassembled instructions, which should be 10 +# elements long if this worked. +# +# This proc emits a pass/fail on whether 10 instructions were captured +# using TESTNAME. +proc disassemble_and_gather_insn { cmd testname } { + set insn {} + gdb_test_multiple $cmd $testname { + -re "^[string_to_regexp $cmd]\r\n" { + exp_continue + } + + -re "^\[^:\r\n]*($::hex)\[^:\r\n\]*:\\s+(\[^\r\n\]+)\r\n" { + set addr $expect_out(1,string) + set asm $expect_out(2,string) + lappend insn [list $addr $asm] + exp_continue + } + + -re "^$::gdb_prompt $" { + gdb_assert {[llength $insn] == 10} $gdb_test_name + } + } + + return $insn +} + +# Capture the instructions, with styling, using GDB's CLI +# disassembler. +set expected_insn [disassemble_and_gather_insn "x/10i *main" \ + "disassemble some instructions"] + +# Now disassemble using our Python disassemble command. Capture these +# instructions. +set py_insn [disassemble_and_gather_insn "py-disasm *main" \ + "disassemble instructions via python"] + +# Check we got the same disassembled output, including styling, in +# both cases. +gdb_assert { $expected_insn eq $py_insn } \ + "instructions, with styling, are the same" + +# Disable styling. +gdb_test_no_output "set style enabled off" + +# Run the Python disassembled again. Now that styling is disabled +# there should be no escape sequences in the disassembler output. +set py_insn [disassemble_and_gather_insn "py-disasm *main" \ + "disassemble instructions via python, no styling"] + +# Check for escape sequences. There should be none. +set found_escape false +foreach insn $py_insn { + set asm [lindex $insn 1] + if {[string first "\033" $asm] != -1} { + set found_escape true + } +} +gdb_assert { !$found_escape } \ + "no escape sequences in unstyled disassembler output" base-commit: e5425f2687d66034a8d3fe94264cf99b42c1cb1a -- 2.25.4