From: Tom de Vries <tdevries@suse.de>
To: gdb-patches@sourceware.org
Subject: Re: [PATCH] [gdb/tui] Improve asm window
Date: Mon, 4 Aug 2025 15:20:10 +0200 [thread overview]
Message-ID: <31f5329b-e7eb-414f-84a0-a8b12a81ef2f@suse.de> (raw)
In-Reply-To: <53f6f2e9-d15e-4302-a1c8-cefaa2ae3790@suse.de>
On 8/3/25 19:21, Tom de Vries wrote:
> On 8/3/25 17:00, Tom de Vries wrote:
>> The TUI asm window is (ignoring borders) currently structured as
>> follows, from
>> left to right:
>> - exec info string (3 chars)
>> - numeric address (hex with 0x prefix)
>> - space
>> - symbolic address of the form <name> or <name+n> (with n decimal)
>> - spaces left-aligning the following item to a multiple of tui tab-width,
>> relative to the start of the numeric address
>> - disassembled instruction
>>
>> For example, the main function in a hello world exec looks like this:
>> ...
>> +---------------------------------------------------------------+
>> | 0x4005d7 <main> push %rbp |
>> | 0x4005d8 <main+1> mov %rsp,%rbp |
>> | 0x4005db <main+4> mov $0x4005fc,%edi |
>> | 0x4005e0 <main+9> call 0x4004d0 <puts@plt> |
>> | 0x4005e5 <main+14> mov $0x0,%eax |
>> | 0x4005ea <main+19> pop %rbp |
>> | 0x4005eb <main+20> ret |
>> | 0x4005ec <_fini> sub $0x8,%rsp |
>> | 0x4005f0 <_fini+4> add $0x8,%rsp |
>> +---------------------------------------------------------------+
>> ...
>>
>> A known problem with this approach is that the symbolic address can be
>> very
>> long, for instance when dealing with C++ symbols, with the result that
>> the
>> disassembled instruction is not visible unless the user scrolls to it.
>>
>> And after stepping the cursor position may be reset, again requiring
>> scrolling.
>>
>> I've tried to address this in the following way.
>>
>> I've divided the window into 4 parts:
>> - exec info string
>> - numeric address
>> - symbolic address
>> - disassembled instruction
>>
>> First, we decide which parts will be shown, depending on the available
>> width.
>>
>> In the minimal case, we have:
>> - exec info string
>> - disassembled instruction
>>
>> With a bit more space available, we have:
>> - exec info string
>> - numeric address
>> - disassembled instruction
>>
>> And with yet a bit more space available, we have the current layout
>> with all 4
>> parts.
>>
>> [ Note that this is similar to how the status line is handled. ]
>>
>> In the latter case, after reserving space for the exec info string and
>> the
>> numeric address, the remaining space is divided between the symbolic
>> address
>> and the disassembled instruction.
>>
>> If the available space for the symbolic address is insufficient, the
>> symbolic
>> address is abbreviated.
>>
>> If the available space for the disassembled instruction is
>> insufficient, it's
>> truncated at the end, as is the case currently.
>>
>> Without this patch, with the example from PR tui/25347, we have:
>> ...
>> +---------------------------------------------------------------+
>> | 0x401697 <_Z3fooN5boost5units8quantityINS0_4unitINS0_4listIN|
>> | 0x401698 <_Z3fooN5boost5units8quantityINS0_4unitINS0_4listIN|
>> | 0x40169b <_Z3fooN5boost5units8quantityINS0_4unitINS0_4listIN|
>> | 0x40169f <_Z3fooN5boost5units8quantityINS0_4unitINS0_4listIN|
>> |B+>0x4016a3 <_Z3fooN5boost5units8quantityINS0_4unitINS0_4listIN|
>> | 0x4016a7 <_Z3fooN5boost5units8quantityINS0_4unitINS0_4listIN|
>> | 0x4016ab <_Z3fooN5boost5units8quantityINS0_4unitINS0_4listIN|
>> | 0x4016af <_Z3fooN5boost5units8quantityINS0_4unitINS0_4listIN|
>> | 0x4016b2 <_Z3fooN5boost5units8quantityINS0_4unitINS0_4listIN|
>> +---------------------------------------------------------------+
>> ...
>> and with this patch we have instead:
>> ...
>> +---------------------------------------------------------------+
>> | 0x401697 push %rbp |
>> | 0x401698 mov %rsp,%rbp |
>> | 0x40169b sub $0x20,%rsp |
>> | 0x40169f mov %rdi,-0x18(%rbp) |
>> |B+>0x4016a3 lea -0x8(%rbp),%rax |
>> | 0x4016a7 mov -0x18(%rbp),%rdx |
>> | 0x4016ab mov -0x18(%rbp),%rcx |
>> | 0x4016af mov %rcx,%rsi |
>> | 0x4016b2 mov %rax,%rdi |
>> +---------------------------------------------------------------+
>> ...
>> and after resizing that to a bit more lines and columns:
>> ...
>> +-----------------------------------------------------------------------------+
>> | 0x401697 <_Z3fooN5b...EEEvEEdEE> push
>> %rbp |
>> | 0x401698 <_Z3fooN5b...EEEvEEdEE+1> mov %rsp,
>> %rbp |
>> | 0x40169b <_Z3fooN5b...EEEvEEdEE+4> sub $0x20,
>> %rsp |
>> | 0x40169f <_Z3fooN5b...EEEvEEdEE+8> mov
>> %rdi,-0x18(%rbp) |
>> |B+>0x4016a3 <_Z3fooN5b...EEEvEEdEE+12> lea -0x8(%rbp),
>> %rax |
>> | 0x4016a7 <_Z3fooN5b...EEEvEEdEE+16> mov -0x18(%rbp),
>> %rdx |
>> | 0x4016ab <_Z3fooN5b...EEEvEEdEE+20> mov -0x18(%rbp),
>> %rcx |
>> | 0x4016af <_Z3fooN5b...EEEvEEdEE+24> mov %rcx,
>> %rsi |
>> | 0x4016b2 <_Z3fooN5b...EEEvEEdEE+27> mov %rax,
>> %rdi |
>> | 0x4016b5 <_Z3fooN5b...EEEvEEdEE+30> call 0x403bd7
>> <_ZN5boost5unit|
>> | 0x4016ba <_Z3fooN5b...EEEvEEdEE+35> lea -0x8(%rbp),
>> %rax |
>> | 0x4016be <_Z3fooN5b...EEEvEEdEE+39> mov %rax,
>> %rsi |
>> +-----------------------------------------------------------------------------+
>> ...
>>
>> The patch uses 2 hardcoded constants:
>> - const size_t min_sym_addr_size = 30
>> - const size_t min_insn_size = 30
>> to choose which parts will be shown.
>>
>> The patch also uses the hardcoded 50/50 ratio to divide space between the
>> symbolic address and the disassembled instruction parts, in case
>> they're both
>> used.
>>
>
> Hmm, I did:
> ...
> $ echo $COLUMNS
> 94
> $ echo $LINES
> 26
> $ gdb -q build/gdb/gdb -ex "b captured_main_1" -ex "layout asm" -ex run
> ...
> and got:
> ...
> │ 0xb75847 <captured_main_1(captured_main_args*)+9> sub $0x218,%rsp
> │
> │ 0xb7584e <captured_mai..._main_args*)+16> mov
> %rdi,-0x228(%rbp) │
> ...
> so clearly there's some bug in the patch. Both lines are in the same
> function, so either both should abbreviate, or both should not.
>
I've fixed this in a v2 (
https://sourceware.org/pipermail/gdb-patches/2025-August/219701.html ).
Thanks,
- Tom
>> Tested on x86_64-linux.
>>
>> There was one update required in the TUI testsuite due to this failure:
>> ...
>> FAIL: gdb.tui/new-layout.exp: \
>> layout=h {{-horizontal asm 1 src 1} 1 status 0 cmd 1} \
>> {{0 0 40 15} {39 0 41 15}} {0x[0-9A-Fa-f]+ <main>.*21.*return 0}: \
>> contents in layout h
>> ...
>> where using a small asm window now means that the address and the
>> symbolic
>> address are no longer there:
>> ...
>> 0 +--------------------------------------+
>> 1 | push %rbp |
>> 2 | mov %rsp,%rbp |
>> 3 | mov $0x0,%eax |
>> 4 | pop %rbp |
>> 5 | ret |
>> 6 | |
>> 7 | |
>> 8 | |
>> 9 | |
>> 10 | |
>> 11 | |
>> 12 | |
>> 13 | |
>> 14 +--------------------------------------+
>> ...
>> ---
>> gdb/defs.h | 3 +
>> gdb/printcmd.c | 80 +++++++++++++++++++++++++
>> gdb/testsuite/gdb.tui/new-layout.exp | 2 +-
>> gdb/tui/tui-disasm.c | 88 ++++++++++++++++++++++------
>> 4 files changed, 153 insertions(+), 20 deletions(-)
>>
>> diff --git a/gdb/defs.h b/gdb/defs.h
>> index bb1e11925da..569bc4f88ac 100644
>> --- a/gdb/defs.h
>> +++ b/gdb/defs.h
>> @@ -209,6 +209,9 @@ extern int print_address_symbolic (struct gdbarch
>> *, CORE_ADDR,
>> struct ui_file *, int,
>> const char *);
>> +extern int print_address_symbolic_maxlen (struct gdbarch *, CORE_ADDR,
>> + struct ui_file *, int, int);
>> +
>> extern void print_address (struct gdbarch *, CORE_ADDR, struct
>> ui_file *);
>> extern const char *pc_prefix (CORE_ADDR);
>> diff --git a/gdb/printcmd.c b/gdb/printcmd.c
>> index 19fbc20074e..2857ee02ed9 100644
>> --- a/gdb/printcmd.c
>> +++ b/gdb/printcmd.c
>> @@ -56,6 +56,7 @@
>> #include <optional>
>> #include "gdbsupport/gdb-safe-ctype.h"
>> #include "inferior.h"
>> +#include <math.h>
>> /* Chain containing all defined memory-tag subcommands. */
>> @@ -591,6 +592,85 @@ print_address_symbolic (struct gdbarch *gdbarch,
>> CORE_ADDR addr,
>> return 1;
>> }
>> +/* Return the number of digits required to display COUNT in decimal. */
>> +
>> +static int
>> +index_digits (unsigned int count)
>> +{
>> + return ((int) log10 ((double) count)) + 1;
>> +}
>> +
>> +/* Optionally print address ADDR symbolically as <SYMBOL+OFFSET> on
>> STREAM,
>> + using not more than MAX chars, possibly abbreviating SYMBOL.
>> + Print nothing if no symbolic name is found nearby.
>> + DO_DEMANGLE controls whether to print a symbol in its native "raw"
>> form,
>> + or to interpret it as a possible C++ name and convert it back to
>> source
>> + form. However note that DO_DEMANGLE can be overridden by the
>> specific
>> + settings of the demangle and asm_demangle variables. Returns
>> + non-zero if anything was printed; zero otherwise. */
>> +
>> +int
>> +print_address_symbolic_maxlen (struct gdbarch *gdbarch, CORE_ADDR addr,
>> + struct ui_file *stream,
>> + int do_demangle, int max)
>> +{
>> + std::string name, filename;
>> + int unmapped = 0;
>> + int offset = 0;
>> + int line = 0;
>> +
>> + if (build_address_symbolic (gdbarch, addr, do_demangle, false, &name,
>> + &offset, &filename, &line, &unmapped))
>> + return 0;
>> +
>> + /* Count "<**> or "<>". */
>> + max -= unmapped ? 4 : 2;
>> + /* Count offset. */
>> + max -= index_digits (max_symbolic_offset);
>> +
>> + bool do_abbrev = false;
>> + if (strlen (name.c_str ()) > max)
>> + {
>> + /* If we don't have at least 3 chars on either size of the
>> "...", we
>> + consider there's not enough space. */
>> + if (max < 9)
>> + return 0;
>> +
>> + do_abbrev = true;
>> + }
>> +
>> + if (unmapped)
>> + gdb_puts ("<*", stream);
>> + else
>> + gdb_puts ("<", stream);
>> +
>> + if (do_abbrev)
>> + {
>> + max -= 3;
>> + int start_len = max / 2 + max % 2;
>> + int end_len = max / 2 + max % 2;
>> +
>> + fputs_styled (name.substr (0, start_len).c_str (),
>> + function_name_style.style (), stream);
>> +
>> + fputs_styled ("...", function_name_style.style (), stream);
>> +
>> + fputs_styled (name.c_str () + strlen (name.c_str ()) - end_len,
>> + function_name_style.style (), stream);
>> + }
>> + else
>> + fputs_styled (name.c_str (), function_name_style.style (), stream);
>> +
>> + if (offset != 0)
>> + gdb_printf (stream, "%+d", offset);
>> + if (unmapped)
>> + gdb_puts ("*>", stream);
>> + else
>> + gdb_puts (">", stream);
>> +
>> + return 1;
>> +}
>> +
>> /* See valprint.h. */
>> int
>> diff --git a/gdb/testsuite/gdb.tui/new-layout.exp b/gdb/testsuite/
>> gdb.tui/new-layout.exp
>> index f5179973e5b..355ab2bf80a 100644
>> --- a/gdb/testsuite/gdb.tui/new-layout.exp
>> +++ b/gdb/testsuite/gdb.tui/new-layout.exp
>> @@ -70,7 +70,7 @@ set layouts \
>> {{0 0 80 15}} ""] \
>> [list h "{-horizontal asm 1 src 1} 1 status 0 cmd 1" \
>> {{0 0 40 15} {39 0 41 15}} \
>> - "$hex <main>.*21.*return 0"] \
>> + "21.*return 0"] \
>> [list example3 "{-horizontal src 1 cmd 1} 1 status 0 asm 1" \
>> {{0 0 40 11} {0 12 80 12}} \
>> "21.*return 0.*$hex <main>"] \
>> diff --git a/gdb/tui/tui-disasm.c b/gdb/tui/tui-disasm.c
>> index 627f71cb366..85aae4eedea 100644
>> --- a/gdb/tui/tui-disasm.c
>> +++ b/gdb/tui/tui-disasm.c
>> @@ -44,6 +44,8 @@ struct tui_asm_line
>> CORE_ADDR addr;
>> std::string addr_string;
>> size_t addr_size;
>> + std::string sym_addr_string;
>> + size_t sym_addr_size;
>> std::string insn;
>> };
>> @@ -80,6 +82,11 @@ len_without_escapes (const std::string &str)
>> return len;
>> }
>> +extern int
>> +print_address_symbolic_maxlen (struct gdbarch *gdbarch, CORE_ADDR addr,
>> + struct ui_file *stream,
>> + int do_demangle, int max);
>> +
>> /* Function to disassemble up to COUNT instructions starting from
>> address
>> PC into the ASM_LINES vector (which will be emptied of any previous
>> contents). Return the address of the COUNT'th instruction after pc.
>> @@ -129,20 +136,23 @@ tui_disassemble (struct gdbarch *gdbarch,
>> /* And capture the address the instruction is at. */
>> tal.addr = orig_pc;
>> - print_address (gdbarch, orig_pc, &gdb_dis_out);
>> + fputs_styled (paddress (gdbarch, tal.addr), address_style.style
>> (),
>> + &gdb_dis_out);
>> tal.addr_string = gdb_dis_out.release ();
>> + tal.addr_size = (term_out
>> + ? len_without_escapes (tal.addr_string)
>> + : tal.addr_string.size ());
>> +
>> + /* And capture the symbolic address the instruction is at. */
>> + print_address_symbolic (gdbarch, orig_pc, &gdb_dis_out,
>> + asm_demangle, "");
>> + tal.sym_addr_string = gdb_dis_out.release ();
>> + tal.sym_addr_size = (term_out
>> + ? len_without_escapes (tal.sym_addr_string)
>> + : tal.sym_addr_string.size ());
>> if (addr_size != nullptr)
>> - {
>> - size_t new_size;
>> -
>> - if (term_out)
>> - new_size = len_without_escapes (tal.addr_string);
>> - else
>> - new_size = tal.addr_string.size ();
>> - *addr_size = std::max (*addr_size, new_size);
>> - tal.addr_size = new_size;
>> - }
>> + *addr_size = std::max (*addr_size, tal.addr_size);
>> asm_lines.push_back (std::move (tal));
>> }
>> @@ -321,8 +331,6 @@ tui_disasm_window::set_contents (struct gdbarch
>> *arch,
>> int i;
>> int max_lines;
>> CORE_ADDR cur_pc;
>> - int tab_len = tui_tab_width;
>> - int insn_pos;
>> CORE_ADDR pc = sal.pc;
>> if (pc == 0)
>> @@ -341,10 +349,26 @@ tui_disasm_window::set_contents (struct gdbarch
>> *arch,
>> size_t addr_size = 0;
>> tui_disassemble (m_gdbarch, asm_lines, pc, max_lines, &addr_size);
>> - /* Align instructions to the same column. */
>> - insn_pos = (1 + (addr_size / tab_len)) * tab_len;
>> + const size_t min_sym_addr_size = 30;
>> + const size_t min_insn_size = 30;
>> +
>> + size_t sym_addr_size = 0;
>> + bool have_addr = false;
>> + bool have_sym_addr = false;
>> + int avail_width = width - box_size () - TUI_EXECINFO_SIZE;
>> + if (avail_width >= addr_size + 1 + min_sym_addr_size + 1 +
>> min_insn_size)
>> + {
>> + have_addr = true;
>> + have_sym_addr = true;
>> + sym_addr_size = (avail_width - addr_size) / 2;
>> + }
>> + else if (avail_width >= addr_size + 1 + min_insn_size)
>> + have_addr = true;
>> /* Now construct each line. */
>> + bool term_out
>> + = disassembler_styling && gdb_stdout->can_emit_style_escape ();
>> + string_file gdb_dis_out (term_out);
>> m_content.resize (max_lines);
>> m_max_length = -1;
>> for (i = 0; i < max_lines; i++)
>> @@ -356,10 +380,36 @@ tui_disasm_window::set_contents (struct gdbarch
>> *arch,
>> if (i < asm_lines.size ())
>> {
>> - line
>> - = (asm_lines[i].addr_string
>> - + n_spaces (insn_pos - asm_lines[i].addr_size)
>> - + asm_lines[i].insn);
>> + if (have_sym_addr && asm_lines[i].sym_addr_size <= sym_addr_size)
>> + line
>> + = (asm_lines[i].addr_string
>> + + " "
>> + + asm_lines[i].sym_addr_string
>> + + n_spaces (sym_addr_size - asm_lines[i].sym_addr_size)
>> + + " "
>> + + asm_lines[i].insn);
>> + else if (have_sym_addr)
>> + {
>> + print_address_symbolic_maxlen (m_gdbarch, asm_lines[i].addr,
>> + &gdb_dis_out, asm_demangle,
>> + sym_addr_size);
>> + std::string abbrev = gdb_dis_out.release ();
>> + int abbrev_len = (term_out
>> + ? len_without_escapes (abbrev)
>> + : abbrev.size ());
>> + line
>> + = (asm_lines[i].addr_string
>> + + " "
>> + + abbrev
>> + + n_spaces (sym_addr_size - abbrev_len)
>> + + " "
>> + + asm_lines[i].insn);
>> + }
>> + else if (have_addr)
>> + line = asm_lines[i].addr_string + " " + asm_lines[i].insn;
>> + else
>> + line = asm_lines[i].insn;
>> +
>> addr = asm_lines[i].addr;
>> }
>> else
>>
>> base-commit: 891d1654d7314fa520f708dbc5f1bf855d15bd40
>
prev parent reply other threads:[~2025-08-04 13:23 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-03 15:00 Tom de Vries
2025-08-03 17:21 ` Tom de Vries
2025-08-04 13:20 ` Tom de Vries [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=31f5329b-e7eb-414f-84a0-a8b12a81ef2f@suse.de \
--to=tdevries@suse.de \
--cc=gdb-patches@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox