Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Tom de Vries <tdevries@suse.de>
To: gdb-patches@sourceware.org
Subject: Re: [PATCH] [gdb/tui] Improve asm window
Date: Sun, 3 Aug 2025 19:21:59 +0200	[thread overview]
Message-ID: <53f6f2e9-d15e-4302-a1c8-cefaa2ae3790@suse.de> (raw)
In-Reply-To: <20250803150031.28468-1-tdevries@suse.de>

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.

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


  reply	other threads:[~2025-08-03 17:22 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 [this message]
2025-08-04 13:20   ` Tom de Vries

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=53f6f2e9-d15e-4302-a1c8-cefaa2ae3790@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