This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Patch: DWARF browser
- From: Tom Tromey <tromey at redhat dot com>
- To: Binutils Development <binutils at sourceware dot org>
- Date: Tue, 26 Apr 2011 14:09:15 -0600
- Subject: Patch: DWARF browser
I have been wanting a nicer way to browse DWARF, so I came up with the
appended. It is an Emacs mode for DWARF browsing, plus a couple of new
supporting readelf features.
The simplest way to browse is just M-x dwarf-browse, then enter a file
name. This will show a high-level view of the debug info (just the CU
DIEs). If you want to see more details, you can click or RET on the
`...' fields, and the mode will expand more DIEs.
The mode has a bit of font-lock support and it also has support for
following DIE references (pushing the mark while doing so).
I needed 2 readelf features to make this work nicely. One, a way to
elide DIEs at a certain depth. Two, a way to then print sibling DIEs
starting with a certain DIE.
I don't know if you'd want the .el code in binutils or not. It is no
problem if not, I will just put it in ELPA. I'll probably add features
to it as I need them.
It would be handy if the readelf changes could go in regardless of what
happens to the lisp code.
Let me know what you think.
Tom
2011-04-26 Tom Tromey <tromey@redhat.com>
* dwarf-mode.el: New file.
* dwarf.c (dwarf_cutoff_level, dwarf_start_die): New globals.
(read_and_display_attr_value): Also check debug_info_p.
(process_debug_info): Handle dwarf_start_die and
dwarf_cutoff_level.
* dwarf.h (dwarf_cutoff_level, dwarf_start_die): Declare.
* readelf.c (usage): Update.
(OPTION_DWARF_DEPTH): New macro.
(OPTION_DWARF_START): Likewise.
(options): Add dwarf-depth and dwarf-start entries.
(parse_args): Handle OPTION_DWARF_START and OPTION_DWARF_DEPTH.
>From a913cbeb77711c1d3d2c7332b003c2899df447b8 Mon Sep 17 00:00:00 2001
From: Tom Tromey <tromey@redhat.com>
Date: Mon, 25 Apr 2011 07:07:04 -0600
Subject: [PATCH] add dwarf-depth and dwarf-start
---
binutils/ChangeLog | 14 ++++
binutils/doc/binutils.texi | 24 ++++++
binutils/dwarf-mode.el | 167 ++++++++++++++++++++++++++++++++++++++++++++
binutils/dwarf.c | 56 ++++++++++++---
binutils/dwarf.h | 3 +
binutils/readelf.c | 23 ++++++
6 files changed, 275 insertions(+), 12 deletions(-)
create mode 100644 binutils/dwarf-mode.el
diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi
index 45a7ff7..0499021 100644
--- a/binutils/doc/binutils.texi
+++ b/binutils/doc/binutils.texi
@@ -3957,6 +3957,8 @@ readelf [@option{-a}|@option{--all}]
[@option{-c}|@option{--archive-index}]
[@option{-w[lLiaprmfFsoRt]}|
@option{--debug-dump}[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges,=pubtypes,=trace_info,=trace_abbrev,=trace_aranges,=gdb_index]]
+ [@option{--dwarf-depth=@var{n}}]
+ [@option{--dwarf-start=@var{n}}]
[@option{-I}|@option{--histogram}]
[@option{-v}|@option{--version}]
[@option{-W}|@option{--wide}]
@@ -4124,6 +4126,28 @@ Note: the @option{=frames-interp} option will display the interpreted
contents of a .debug_frame section whereas the @option{=frames} option
dumps the contents in a raw format.
+Note: the output from the @option{=info} option can also be affected
+by the options @option{--dwarf-depth} and @option{--dwarf-start}.
+
+@item --dwarf-depth=@var{n}
+Limit the dump of the @code{.debug_info} section to @var{n} children.
+This is only useful with @option{--debug-dump=info}. The default is
+to print all DIEs; the special value 0 for @var{n} will also have this
+effect.
+
+With a non-zero value for @var{n}, DIEs at or deeper than @var{n}
+levels will not be printed. The range for @var{n} is zero-based.
+
+@item --dwarf-start=@var{n}
+Print only DIEs beginning with the DIE numbered @var{n}. This is only
+useful with @option{--debug-dump=info}.
+
+If specified, this option will suppress printing of any header
+information and all DIEs before the DIE numbered @var{n}. Only
+siblings and children of the specified DIE will be printed.
+
+This can be used in conjunction with @option{--dwarf-depth}.
+
@item -I
@itemx --histogram
Display a histogram of bucket list lengths when displaying the contents
diff --git a/binutils/dwarf-mode.el b/binutils/dwarf-mode.el
new file mode 100644
index 0000000..a1d6856
--- /dev/null
+++ b/binutils/dwarf-mode.el
@@ -0,0 +1,167 @@
+;;; dwarf-mode.el --- Browser for DWARF information.
+
+;; Version: 1.0
+
+;; This file is not part of GNU Emacs, but is distributed under the
+;; same terms:
+
+;; GNU Emacs 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.
+
+;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(defvar dwarf-readelf-program "readelf")
+
+(defconst dwarf-font-lock-keywords
+ '(
+ ;; Name and linkage name.
+ ("DW_AT_[a-z_]*name\\s *: .*:\\(.*\\)\\s *$"
+ (1 font-lock-function-name-face))
+
+ ("Compilation Unit @ offset 0x[0-9a-f]+"
+ (0 font-lock-string-face))
+ ))
+
+(defvar dwarf-file nil
+ "Buffer-local variable holding the file name passed to readelf.")
+
+;; Expand a "..." to show all the child DIES. NEW-DEPTH controls how
+;; deep to display the new dies; `nil' means display all of them.
+(defun dwarf-do-insert-substructure (new-depth die)
+ (let ((inhibit-read-only t))
+ (beginning-of-line)
+ (delete-region (point) (progn
+ (end-of-line)
+ (forward-char)
+ (point)))
+ (save-excursion
+ (apply #'call-process dwarf-readelf-program nil (current-buffer) nil
+ "-wi" (concat "--dwarf-start=0x" die)
+ dwarf-file
+ (if new-depth (list (concat "--dwarf-depth="
+ (int-to-string new-depth))))))
+ (set-buffer-modified-p nil)))
+
+(defun dwarf-insert-substructure-button (die)
+ (beginning-of-line)
+ (unless (looking-at "^ <\\([0-9]+\\)>")
+ (error "Unrecognized line."))
+ (let ((new-depth (1+ (string-to-int (match-string 1)))))
+ (dwarf-do-insert-substructure new-depth die)))
+
+(defun dwarf-insert-substructure (arg)
+ "Expand a `...' to show children of the current DIE.
+By default, expands just one level of children.
+A prefix argument means expand all children."
+ (interactive "P")
+ (beginning-of-line)
+ (unless (looking-at "^ <\\([0-9]+\\)><\\([0-9a-f]+\\)>")
+ (error "Unrecognized line."))
+ (let ((die (match-string 2)))
+ (if arg
+ (dwarf-do-insert-substructure nil die)
+ (dwarf-insert-substructure-button die))))
+
+;; Called when a button is pressed.
+;; Either follows a DIE reference, or expands a "...".
+(defun dwarf-die-button-action (button)
+ (let* ((die (button-get button 'die))
+ ;; Note that the first number can only be decimal.
+ (die-rx (concat "^\\s *\\(<[0-9]+>\\)?<"
+ die ">[^<]"))
+ (old (point))
+ (is-ref (button-get button 'die-ref)))
+ (if is-ref
+ (progn
+ (goto-char (point-min))
+ (if (re-search-forward die-rx nil 'move)
+ (push-mark old)
+ (goto-char old)
+ (error "Could not find DIE <0x%s>" die)))
+ (dwarf-insert-substructure-button die))))
+
+;; Button definition.
+(define-button-type 'dwarf-die-button
+ 'follow-link t
+ 'action #'dwarf-die-button-action)
+
+;; Helper regexp to match a DIE reference.
+(defconst dwarf-die-reference ": \\(<0x\\([0-9a-f]+\\)>\\)\\s *$")
+
+;; Helper regexp to match a `...' indicating that there are hidden
+;; children.
+(defconst dwarf-die-more "^ <[0-9]+><\\([0-9a-z]+\\)>: \\([.][.][.]\\)")
+
+;; jit-lock callback function to fontify a region. This applies the
+;; buttons, since AFAICT there is no good way to apply buttons via
+;; font-lock.
+(defun dwarf-fontify-region (start end)
+ (save-excursion
+ (let ((beg-line (progn (goto-char start) (line-beginning-position)))
+ (end-line (progn (goto-char end) (line-end-position))))
+ (goto-char beg-line)
+ (while (re-search-forward dwarf-die-reference end-line 'move)
+ (let ((b-start (match-beginning 1))
+ (b-end (match-end 1))
+ (hex (match-string-no-properties 2)))
+ (make-text-button b-start b-end :type 'dwarf-die-button
+ 'die hex 'die-ref t)))
+ ;; This is a bogus approach. Why can't we make buttons from the
+ ;; font-lock defaults?
+ (goto-char beg-line)
+ (while (re-search-forward dwarf-die-more end-line 'move)
+ (let ((hex (match-string-no-properties 1))
+ (b-start (match-beginning 2))
+ (b-end (match-end 2)))
+ (make-text-button b-start b-end :type 'dwarf-die-button
+ 'die hex 'die-ref nil))))))
+
+;; Run readelf and insert the contents into the buffer. The arguments
+;; are the way they are because this is also called as a
+;; revert-buffer-function.
+(defun dwarf-do-refresh (&rest ignore)
+ (let ((inhibit-read-only t))
+ (erase-buffer)
+ (save-excursion
+ (call-process dwarf-readelf-program
+ nil (current-buffer) nil
+ "-wi" "--dwarf-depth=1"
+ dwarf-file))
+ (set-buffer-modified-p nil)))
+
+;;;###autoload
+(define-derived-mode dwarf-mode special-mode "DWARF"
+ "Major mode for browsing DWARF output.
+
+\\{dwarf-mode-map}"
+
+ (set (make-local-variable 'font-lock-defaults) '(dwarf-font-lock-keywords))
+ ;; FIXME: we could be smarter and check the file time.
+ (set (make-local-variable 'revert-buffer-function) #'dwarf-do-refresh)
+ (jit-lock-register #'dwarf-fontify-region))
+
+(define-key dwarf-mode-map [(control ?m)] #'dwarf-insert-substructure)
+
+;;:###autoload
+(defun dwarf-browse (file)
+ "Invoke `readelf' and put output into a `dwarf-mode' buffer.
+This is the main interface to `dwarf-mode'."
+ (interactive "fFile name: ")
+ (let* ((base-name (file-name-nondirectory file))
+ (buffer (generate-new-buffer (concat "*DWARF for " base-name "*"))))
+ (pop-to-buffer buffer)
+ (dwarf-mode)
+ (set (make-local-variable 'dwarf-file) file)
+ (dwarf-do-refresh)))
+
+(provide 'dwarf-mode)
diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index 8846e87..3086d33 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -63,6 +63,9 @@ int do_trace_abbrevs;
int do_trace_aranges;
int do_wide;
+int dwarf_cutoff_level = -1;
+unsigned long dwarf_start_die;
+
/* Values for do_debug_lines. */
#define FLAG_DEBUG_LINES_RAW 1
#define FLAG_DEBUG_LINES_DECODED 2
@@ -1367,7 +1370,8 @@ read_and_display_attr_value (unsigned long attribute,
}
if ((do_loc || do_debug_loc || do_debug_ranges)
- && num_debug_info_entries == 0)
+ && num_debug_info_entries == 0
+ && debug_info_p != NULL)
{
switch (attribute)
{
@@ -1981,7 +1985,8 @@ process_debug_info (struct dwarf_section *section,
if (!do_loc)
{
- printf (_("Contents of the %s section:\n\n"), section->name);
+ if (dwarf_start_die == 0)
+ printf (_("Contents of the %s section:\n\n"), section->name);
load_debug_section (str, file);
}
@@ -1999,7 +2004,7 @@ process_debug_info (struct dwarf_section *section,
DWARF2_Internal_CompUnit compunit;
unsigned char *hdrptr;
unsigned char *tags;
- int level;
+ int level, last_level, saved_level;
dwarf_vma cu_offset;
int offset_size;
int initial_length_size;
@@ -2068,7 +2073,7 @@ process_debug_info (struct dwarf_section *section,
debug_information [unit].num_range_lists = 0;
}
- if (!do_loc)
+ if (!do_loc && dwarf_start_die == 0)
{
printf (_(" Compilation Unit @ offset 0x%s:\n"),
dwarf_vmatoa ("x", cu_offset));
@@ -2129,6 +2134,8 @@ process_debug_info (struct dwarf_section *section,
+ debug_displays [abbrev_sec].section.size);
level = 0;
+ last_level = level;
+ saved_level = -1;
while (tags < start)
{
unsigned int bytes_read;
@@ -2136,6 +2143,7 @@ process_debug_info (struct dwarf_section *section,
unsigned long die_offset;
abbrev_entry *entry;
abbrev_attr *attr;
+ int do_printing = 1;
die_offset = tags - section_begin;
@@ -2172,12 +2180,30 @@ process_debug_info (struct dwarf_section *section,
warn (_("Further warnings about bogus end-of-sibling markers suppressed\n"));
}
}
+ if (dwarf_start_die != 0 && level < saved_level)
+ return 1;
continue;
}
if (!do_loc)
- printf (_(" <%d><%lx>: Abbrev Number: %lu"),
- level, die_offset, abbrev_number);
+ {
+ if (dwarf_start_die != 0 && die_offset < dwarf_start_die)
+ do_printing = 0;
+ else
+ {
+ if (dwarf_start_die != 0 && die_offset == dwarf_start_die)
+ saved_level = level;
+ do_printing = (dwarf_cutoff_level == -1
+ || level < dwarf_cutoff_level);
+ if (do_printing)
+ printf (_(" <%d><%lx>: Abbrev Number: %lu"),
+ level, die_offset, abbrev_number);
+ else if (dwarf_cutoff_level == -1
+ || last_level < dwarf_cutoff_level)
+ printf (_(" <%d><%lx>: ...\n"), level, die_offset);
+ last_level = level;
+ }
+ }
/* Scan through the abbreviation list until we reach the
correct entry. */
@@ -2188,7 +2214,7 @@ process_debug_info (struct dwarf_section *section,
if (entry == NULL)
{
- if (!do_loc)
+ if (!do_loc && do_printing)
{
printf ("\n");
fflush (stdout);
@@ -2198,7 +2224,7 @@ process_debug_info (struct dwarf_section *section,
return 0;
}
- if (!do_loc)
+ if (!do_loc && do_printing)
printf (" (%s)\n", get_TAG_name (entry->tag));
switch (entry->tag)
@@ -2219,9 +2245,15 @@ process_debug_info (struct dwarf_section *section,
for (attr = entry->first_attr; attr; attr = attr->next)
{
- if (! do_loc)
+ debug_info *arg;
+
+ if (! do_loc && do_printing)
/* Show the offset from where the tag was extracted. */
- printf (" <%2lx>", (unsigned long)(tags - section_begin));
+ printf (" <%lx>", (unsigned long)(tags - section_begin));
+
+ arg = debug_information;
+ if (debug_information)
+ arg += unit;
tags = read_and_display_attr (attr->attribute,
attr->form,
@@ -2229,8 +2261,8 @@ process_debug_info (struct dwarf_section *section,
compunit.cu_pointer_size,
offset_size,
compunit.cu_version,
- debug_information + unit,
- do_loc, section);
+ arg,
+ do_loc || ! do_printing, section);
}
if (entry->children)
diff --git a/binutils/dwarf.h b/binutils/dwarf.h
index 62b57a6..07b95b0 100644
--- a/binutils/dwarf.h
+++ b/binutils/dwarf.h
@@ -200,6 +200,9 @@ extern int do_trace_abbrevs;
extern int do_trace_aranges;
extern int do_wide;
+extern int dwarf_cutoff_level;
+extern unsigned long dwarf_start_die;
+
extern void init_dwarf_regnames (unsigned int);
extern void init_dwarf_regnames_i386 (void);
extern void init_dwarf_regnames_x86_64 (void);
diff --git a/binutils/readelf.c b/binutils/readelf.c
index c4df8d9..6c8b966 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -3073,6 +3073,8 @@ get_section_type_name (unsigned int sh_type)
#define OPTION_DEBUG_DUMP 512
#define OPTION_DYN_SYMS 513
+#define OPTION_DWARF_DEPTH 514
+#define OPTION_DWARF_START 515
static struct option options[] =
{
@@ -3106,6 +3108,9 @@ static struct option options[] =
#endif
{"debug-dump", optional_argument, 0, OPTION_DEBUG_DUMP},
+ {"dwarf-depth", required_argument, 0, OPTION_DWARF_DEPTH},
+ {"dwarf-start", required_argument, 0, OPTION_DWARF_START},
+
{"version", no_argument, 0, 'v'},
{"wide", no_argument, 0, 'W'},
{"help", no_argument, 0, 'H'},
@@ -3149,6 +3154,10 @@ usage (FILE * stream)
=frames-interp,=str,=loc,=Ranges,=pubtypes,\n\
=gdb_index,=trace_info,=trace_abbrev,=trace_aranges]\n\
Display the contents of DWARF2 debug sections\n"));
+ fprintf (stream, _("\
+ --dwarf-depth=N Do not display DIEs at depth N or greater\n\
+ --dwarf-start=N Display DIEs starting with N, at the same depth\n\
+ or deeper\n"));
#ifdef SUPPORT_DISASSEMBLY
fprintf (stream, _("\
-i --instruction-dump=<number|name>\n\
@@ -3355,6 +3364,20 @@ parse_args (int argc, char ** argv)
dwarf_select_sections_by_names (optarg);
}
break;
+ case OPTION_DWARF_DEPTH:
+ {
+ char *cp;
+
+ dwarf_cutoff_level = strtoul (optarg, & cp, 0);
+ }
+ break;
+ case OPTION_DWARF_START:
+ {
+ char *cp;
+
+ dwarf_start_die = strtoul (optarg, & cp, 0);
+ }
+ break;
case OPTION_DYN_SYMS:
do_dyn_syms++;
break;
--
1.7.3.4