]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb: add 'maint info blocks' command
authorAndrew Burgess <aburgess@redhat.com>
Thu, 18 Jul 2024 10:16:13 +0000 (11:16 +0100)
committerAndrew Burgess <aburgess@redhat.com>
Wed, 28 Aug 2024 09:30:31 +0000 (10:30 +0100)
While reviewing a patch I wanted to understand which blocks existed at
a given address.

The 'maint print symbols' command does provide some of this
information, but that command displays all blocks within a given
symtab.  If I want to know which blocks are at a given address I have
to figure that out for myself based on the output of 'maint print
symbols' ... and I'm too lazy for that!

So this command lists just those blocks at a given address, along with
information about the blocks type.  This new command doesn't list the
symbols within each block, for that my expectation is that you'd cross
reference the output with that of 'maint print symbols'.

The new command format is:

  maintenance info blocks
  maintenance info blocks ADDRESS

This lists the blocks at ADDRESS, or at the current $pc if ADDRESS is
not given.  Blocks are listed starting at the global block, then the
static block, and then the progressively narrower scoped blocks.

For each block we list the internal block pointer (which allows easy
cross referencing with 'maint print symbols'), the inferior address
range, along with other useful information.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Approved-By: Simon Marchi <simon.marchi@efficios.com>
gdb/NEWS
gdb/block.c
gdb/doc/gdb.texinfo
gdb/testsuite/gdb.base/maint-info-inline-frames-and-blocks.exp

index a65c557e334682131c8716f80505ec5e0aa5a0ea..38807dfcf18f50a458c3b0abd47dd12216348e0c 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -47,6 +47,12 @@ maintenance info inline-frames [ADDRESS]
   current address, or for ADDRESS if specified.  The output identifies
   inlined frames which start at the specified address.
 
+maintenance info blocks [ADDRESS]
+  New command which displays information about all of the blocks at
+  ADDRESS, or at the current address if ADDRESS is not given.  Blocks
+  are listed starting at the inner global block out to the most inner
+  block.
+
 *** Changes in GDB 15
 
 * The MPX commands "show/set mpx bound" have been deprecated, as Intel
index d4baaae4a5fee8daa6b0ccad5455f9fa8ba604a4..6f60877785499db98c6e964c3c8f56a32fb2182d 100644 (file)
@@ -25,6 +25,8 @@
 #include "addrmap.h"
 #include "gdbtypes.h"
 #include "objfiles.h"
+#include "cli/cli-cmds.h"
+#include "inferior.h"
 
 /* This is used by struct block to store namespace-related info for
    C++ files, namely using declarations and the current namespace in
@@ -819,3 +821,124 @@ make_blockranges (struct objfile *objfile,
   return blr;
 }
 
+/* Implement 'maint info blocks' command.  If passed an argument then
+   print a list of all blocks at the given address.  With no arguments
+   then list all blocks at the current address of the current inferior.  */
+
+static void
+maintenance_info_blocks (const char *arg, int from_tty)
+{
+  CORE_ADDR address;
+
+  /* With no argument use the program counter of the current thread.  If
+     there is an argument then use this as the address to examine.  */
+  if (arg == nullptr)
+    {
+      if (inferior_ptid == null_ptid)
+       error (_("no inferior thread"));
+
+      struct regcache *regcache = get_thread_regcache (inferior_thread ());
+      address = regcache_read_pc (regcache);
+    }
+  else
+    address = parse_and_eval_address (arg);
+
+  /* Find the inner most block for ADDRESS.  */
+  const struct block *cur_block = block_for_pc (address);
+  if (cur_block == nullptr)
+    {
+      gdb_printf (_("No blocks at %s\n"), core_addr_to_string_nz (address));
+      return;
+    }
+
+  gdb_printf (_("Blocks at %s:\n"), core_addr_to_string_nz (address));
+
+  const struct objfile *toplevel_objfile = cur_block->objfile ();
+  if (toplevel_objfile != nullptr)
+    gdb_printf (_("  from objfile: [(objfile *) %s] %s\n"),
+               host_address_to_string (toplevel_objfile),
+               objfile_name (toplevel_objfile));
+
+  gdb_printf ("\n");
+
+  /* List the blocks backwards; global block (widest scope) first, down to
+     the smallest scoped block last.  To do this we need to build the list
+     of blocks starting from the inner block, then print that list
+     backwards.  */
+  std::vector<const struct block *> blocks;
+  while (cur_block != nullptr)
+    {
+      blocks.emplace_back (cur_block);
+      cur_block = cur_block->superblock ();
+    }
+
+  for (auto it = blocks.rbegin (); it != blocks.rend (); ++it)
+    {
+      cur_block = *it;
+
+      gdb_assert (cur_block->objfile () == toplevel_objfile);
+
+      gdb_printf (_("[(block *) %s] %s..%s\n"),
+                 host_address_to_string (cur_block),
+                 core_addr_to_string_nz (cur_block->start ()),
+                 core_addr_to_string_nz (cur_block->end ()));
+      gdb_printf (_("  entry pc: %s\n"),
+                 core_addr_to_string_nz (cur_block->entry_pc ()));
+
+      if (cur_block->is_static_block ())
+       gdb_printf (_("  is static block\n"));
+
+      if (cur_block->is_global_block ())
+       gdb_printf (_("  is global block\n"));
+
+      if (cur_block->function () != nullptr)
+       {
+         if (cur_block->inlined_p ())
+           gdb_printf (_("  inline function: %s\n"),
+                       cur_block->function ()->print_name ());
+         else
+           gdb_printf (_("  function: %s\n"),
+                       cur_block->function ()->print_name ());
+       }
+
+      if (cur_block->scope () != nullptr
+         && *cur_block->scope () != '\0')
+       gdb_printf (_("  scope: %s\n"), cur_block->scope ());
+
+      if (int symbol_count = mdict_size (cur_block->multidict ());
+         symbol_count > 0)
+       gdb_printf (_("  symbol count: %d\n"), symbol_count);
+
+      if (cur_block->is_contiguous ())
+       gdb_printf (_("  is contiguous\n"));
+      else
+       {
+         gdb_printf (_("  address ranges:\n"));
+         for (const blockrange &rng : cur_block->ranges ())
+           gdb_printf (_("    %s..%s\n"),
+                       core_addr_to_string_nz (rng.start ()),
+                       core_addr_to_string_nz (rng.end ()));
+       }
+    }
+}
+
+\f
+
+void _initialize_block ();
+void
+_initialize_block ()
+{
+  add_cmd ("blocks", class_maintenance, maintenance_info_blocks,
+          _("\
+Display block information for current thread.\n\
+\n\
+Usage:\n\
+\n\
+  maintenance info blocks [ADDRESS]\n\
+\n\
+With no ADDRESS show all blocks at the current address, starting with the\n\
+global block and working down to the inner most block.\n\
+\n\
+When ADDRESS is given, list the blocks at ADDRESS."),
+          &maintenanceinfolist);
+}
index e85099f7d650bdce08321735c3b60d221545a6d9..8cde2f22637b65afacf3a35a454c123be090d64f 100644 (file)
@@ -20419,6 +20419,7 @@ libraries.  When set to @code{off} no messages are printed.
 Show whether messages will be printed when a @value{GDBN} command
 entered from the keyboard causes symbol information to be loaded.
 
+@anchor{maint print symbols}
 @kindex maint print symbols
 @cindex symbol dump
 @kindex maint print psymbols
@@ -41969,6 +41970,65 @@ skipped frames = 0
 @end group
 @end smallexample
 
+@kindex maint info blocks
+@item maint info blocks
+@itemx maint info blocks @var{address}
+Print information about all blocks at @var{address}, or at the current
+@code{$pc} if @var{address} is not given.
+
+For information about what blocks are in @value{GDBN} see @ref{Blocks
+In Python}.
+
+Blocks are listed starting from the global block, then the static
+block, and then proceeding through progressively narrower scopes.
+
+Here is an example of the command's output:
+@smallexample
+@group
+(@value{GDBP}) maintenance info blocks
+Blocks at 0x401137:
+  from objfile: [(objfile *) 0x50507d0] /tmp/inline_func_demo
+
+[(block *) 0x504da90] 0x401106..0x40119a
+  entry pc: 0x401106
+  is global block
+  symbol count: 2
+  is contiguous
+@end group
+@group
+[(block *) 0x504d9f0] 0x401106..0x40119a
+  entry pc: 0x401106
+  is static block
+  symbol count: 1
+  is contiguous
+@end group
+@group
+[(block *) 0x504d9a0] 0x401106..0x40119a
+  entry pc: 0x401106
+  function: main
+  is contiguous
+@end group
+@group
+[(block *) 0x504d900] 0x401137..0x401166
+  entry pc: 0x401137
+  inline function: foo
+  symbol count: 1
+  is contiguous
+@end group
+@group
+[(block *) 0x504d860] 0x401137..0x401165
+  entry pc: 0x401137
+  inline function: bar
+  symbol count: 1
+  is contiguous
+@end group
+@end smallexample
+
+The command @kbd{maint info blocks} lists the symbol count for each
+block but doesn't print the symbols themselves.  The symbol names can
+be found using @kbd{maint print symbols} (@pxref{maint print
+symbols}).
+
 @kindex maint print registers
 @kindex maint print raw-registers
 @kindex maint print cooked-registers
index cbf090deba21d48165e411d8a8ef04b63782b28f..16be22a99635a72f152a8ad28f9dd0622d7d8b4e 100644 (file)
@@ -13,7 +13,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-# Check the 'maint info inline-frames' command.
+# Check the 'maint info inline-frames' and 'maint info blocks'
+# commands.
 
 standard_testfile
 
@@ -26,6 +27,45 @@ if {![runto normal_func]} {
     return 0
 }
 
+# Make a pattern to match 'maint info blocks' output.  ARGS is the
+# list of function names we expect to see.  If the function name
+# starts with 'inline_func' then we expect to see an inline block,
+# otherwise blocks are not expected to be inline.
+proc make_blocks_result { args } {
+    set result \
+       [list \
+            "Blocks at $::hex:" \
+            "  from objfile: \\\[\\(objfile \\*\\) $::hex\\\] [string_to_regexp $::binfile]" \
+            ""\
+            "\\\[\\(block \\*\\) $::hex\\\] $::hex\\.\\.$::hex" \
+            "  entry pc: $::hex" \
+            "  is global block" \
+            ".*" \
+            "\\\[\\(block \\*\\) $::hex\\\] $::hex\\.\\.$::hex" \
+            "  entry pc: $::hex" \
+            "  is static block" \
+            ".*" ]
+
+    foreach func $args {
+       lappend result \
+           "\\\[\\(block \\*\\) $::hex\\\] $::hex\\.\\.$::hex" \
+           "  entry pc: $::hex"
+
+       if { [string range $func 0 10] eq "inline_func" } {
+           lappend result"  inline function: $func"
+       } else {
+           lappend result"  function: $func"
+       }
+
+       lappend result ".*"
+    }
+
+    return [multi_line {*}$result]
+}
+
+gdb_test "maint info blocks" [make_blocks_result normal_func] \
+    "maint info blocks in normal_func only"
+
 # Next forward until we find the call to inline_func_a().  The hope is
 # that when we see the 'inline_func_a()' line this will be the start of
 # the inlined function.  This might not be the case on all
@@ -54,6 +94,10 @@ gdb_test_multiple "next" "next forward to inline_func_a" {
     }
 }
 
+gdb_test "maint info blocks" [make_blocks_result normal_func \
+                                 inline_func_a inline_func_b] \
+    "maint info blocks when all blocks visible"
+
 # View the inline frame information.  This should display that we are
 # at the start of inline_func_a() within normal_func().
 gdb_test "maint info inline-frames" \
@@ -108,6 +152,10 @@ gdb_test "maint info inline-frames" \
         "  normal_func"] \
     "check inline-frames state when just entered inline_func_b"
 
+gdb_test "maint info blocks" [make_blocks_result normal_func \
+                                 inline_func_a inline_func_b] \
+    "maint info blocks when all blocks still visible"
+
 gdb_test "step" ".*" \
     "step into the body of inline_func_b"
 
@@ -121,6 +169,10 @@ gdb_test "maint info inline-frames" \
         "> inline_func_b"] \
     "check inline-frames state when within inline_func_b"
 
+gdb_test "maint info blocks" [make_blocks_result normal_func \
+                                 inline_func_a inline_func_b] \
+    "maint info blocks within inline function, all blocks still visible"
+
 # Use the recorded $pc value to check inline frames.
 gdb_test "maint info inline-frames $pc" \
     [multi_line \
@@ -131,6 +183,10 @@ gdb_test "maint info inline-frames $pc" \
         "> normal_func"] \
     "check inline-frames state at recorded \$pc"
 
+gdb_test "maint info blocks" [make_blocks_result normal_func \
+                                 inline_func_a inline_func_b] \
+    "maint info blocks using stored \$pc, inferior still running"
+
 clean_restart $binfile
 
 # Use the recorded $pc value to check inline frames when the inferior
@@ -144,8 +200,15 @@ gdb_test "maint info inline-frames $pc" \
         "> normal_func"] \
     "check inline-frames state at recorded \$pc before execution starts"
 
+gdb_test "maint info blocks $pc" [make_blocks_result normal_func \
+                                     inline_func_a inline_func_b] \
+    "maint info blocks using stored \$pc, inferior not running"
+
 # Trying to read the $pc from the current thread should fail if the
 # inferior is not yet running.
 gdb_test "maint info inline-frames" \
     "^no inferior thread" \
     "check inline-frames state of current thread before execution starts"
+
+gdb_test "maint info blocks" "^no inferior thread" \
+    "maint info blocks with no \$pc and inferior not running"