xref: /dflybsd-src/contrib/binutils-2.27/binutils/dwarf-mode.el (revision e656dc90e3d65d744d534af2f5ea88cf8101ebcf)
1*a9fa9459Szrj;;; dwarf-mode.el --- Browser for DWARF information.
2*a9fa9459Szrj
3*a9fa9459Szrj;; Version: 1.2
4*a9fa9459Szrj
5*a9fa9459Szrj;; Copyright (C) 2012-2016 Free Software Foundation, Inc.
6*a9fa9459Szrj
7*a9fa9459Szrj;; This file is not part of GNU Emacs, but is distributed under the
8*a9fa9459Szrj;; same terms:
9*a9fa9459Szrj
10*a9fa9459Szrj;; GNU Emacs is free software: you can redistribute it and/or modify
11*a9fa9459Szrj;; it under the terms of the GNU General Public License as published by
12*a9fa9459Szrj;; the Free Software Foundation, either version 3 of the License, or
13*a9fa9459Szrj;; (at your option) any later version.
14*a9fa9459Szrj
15*a9fa9459Szrj;; GNU Emacs is distributed in the hope that it will be useful,
16*a9fa9459Szrj;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17*a9fa9459Szrj;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18*a9fa9459Szrj;; GNU General Public License for more details.
19*a9fa9459Szrj
20*a9fa9459Szrj;; You should have received a copy of the GNU General Public License
21*a9fa9459Szrj;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
22*a9fa9459Szrj
23*a9fa9459Szrj;;; Code:
24*a9fa9459Szrj
25*a9fa9459Szrj(defvar dwarf-objdump-program "objdump")
26*a9fa9459Szrj
27*a9fa9459Szrj(defconst dwarf-font-lock-keywords
28*a9fa9459Szrj  '(
29*a9fa9459Szrj    ;; Name and linkage name.
30*a9fa9459Szrj    ("DW_AT_[a-z_]*name\\s *: .*:\\(.*\\)\\s *$"
31*a9fa9459Szrj     (1 font-lock-function-name-face))
32*a9fa9459Szrj
33*a9fa9459Szrj    ("Compilation Unit @ offset 0x[0-9a-f]+"
34*a9fa9459Szrj     (0 font-lock-string-face))
35*a9fa9459Szrj    ))
36*a9fa9459Szrj
37*a9fa9459Szrj(defvar dwarf-file nil
38*a9fa9459Szrj  "Buffer-local variable holding the file name passed to objdump.")
39*a9fa9459Szrj
40*a9fa9459Szrj;; Expand a "..." to show all the child DIES.  NEW-DEPTH controls how
41*a9fa9459Szrj;; deep to display the new dies; `nil' means display all of them.
42*a9fa9459Szrj(defun dwarf-do-insert-substructure (new-depth die)
43*a9fa9459Szrj  (let ((inhibit-read-only t))
44*a9fa9459Szrj    (beginning-of-line)
45*a9fa9459Szrj    (delete-region (point) (progn
46*a9fa9459Szrj			     (end-of-line)
47*a9fa9459Szrj			     (forward-char)
48*a9fa9459Szrj			     (point)))
49*a9fa9459Szrj    (save-excursion
50*a9fa9459Szrj      (apply #'call-process dwarf-objdump-program nil (current-buffer) nil
51*a9fa9459Szrj	     "-Wi" (concat "--dwarf-start=0x" die)
52*a9fa9459Szrj	     (expand-file-name dwarf-file)
53*a9fa9459Szrj	     (if new-depth (list (concat "--dwarf-depth="
54*a9fa9459Szrj					 (int-to-string new-depth))))))
55*a9fa9459Szrj    (set-buffer-modified-p nil)))
56*a9fa9459Szrj
57*a9fa9459Szrj(defun dwarf-insert-substructure-button (die)
58*a9fa9459Szrj  (beginning-of-line)
59*a9fa9459Szrj  (unless (looking-at "^ <\\([0-9]+\\)>")
60*a9fa9459Szrj    (error "Unrecognized line."))
61*a9fa9459Szrj  (let ((new-depth (1+ (string-to-number (match-string 1)))))
62*a9fa9459Szrj    (dwarf-do-insert-substructure new-depth die)))
63*a9fa9459Szrj
64*a9fa9459Szrj(defun dwarf-insert-substructure (arg)
65*a9fa9459Szrj  "Expand a `...' to show children of the current DIE.
66*a9fa9459SzrjBy default, expands just one level of children.
67*a9fa9459SzrjA prefix argument means expand all children."
68*a9fa9459Szrj  (interactive "P")
69*a9fa9459Szrj  (beginning-of-line)
70*a9fa9459Szrj  (unless (looking-at "^ <\\([0-9]+\\)><\\([0-9a-f]+\\)>")
71*a9fa9459Szrj    (error "Unrecognized line."))
72*a9fa9459Szrj  (let ((die (match-string 2)))
73*a9fa9459Szrj    (if arg
74*a9fa9459Szrj	(dwarf-do-insert-substructure nil die)
75*a9fa9459Szrj      (dwarf-insert-substructure-button die))))
76*a9fa9459Szrj
77*a9fa9459Szrj;; Called when a button is pressed.
78*a9fa9459Szrj;; Either follows a DIE reference, or expands a "...".
79*a9fa9459Szrj(defun dwarf-die-button-action (button)
80*a9fa9459Szrj  (let* ((die (button-get button 'die))
81*a9fa9459Szrj	 ;; Note that the first number can only be decimal.
82*a9fa9459Szrj	 (die-rx (concat "^\\s *\\(<[0-9]+>\\)?<"
83*a9fa9459Szrj			 die ">[^<]"))
84*a9fa9459Szrj	 (old (point))
85*a9fa9459Szrj	 (is-ref (button-get button 'die-ref)))
86*a9fa9459Szrj    (if is-ref
87*a9fa9459Szrj	(progn
88*a9fa9459Szrj	  (goto-char (point-min))
89*a9fa9459Szrj	  (if (re-search-forward die-rx nil 'move)
90*a9fa9459Szrj	      (push-mark old)
91*a9fa9459Szrj	    (goto-char old)
92*a9fa9459Szrj	    (error "Could not find DIE <0x%s>" die)))
93*a9fa9459Szrj      (dwarf-insert-substructure-button die))))
94*a9fa9459Szrj
95*a9fa9459Szrj;; Button definition.
96*a9fa9459Szrj(define-button-type 'dwarf-die-button
97*a9fa9459Szrj  'follow-link t
98*a9fa9459Szrj  'action #'dwarf-die-button-action)
99*a9fa9459Szrj
100*a9fa9459Szrj;; Helper regexp to match a DIE reference.
101*a9fa9459Szrj(defconst dwarf-die-reference "\\(<0x\\([0-9a-f]+\\)>\\)")
102*a9fa9459Szrj
103*a9fa9459Szrj;; Helper regexp to match a `...' indicating that there are hidden
104*a9fa9459Szrj;; children.
105*a9fa9459Szrj(defconst dwarf-die-more "^ <[0-9]+><\\([0-9a-z]+\\)>: \\([.][.][.]\\)")
106*a9fa9459Szrj
107*a9fa9459Szrj;; jit-lock callback function to fontify a region.  This applies the
108*a9fa9459Szrj;; buttons, since AFAICT there is no good way to apply buttons via
109*a9fa9459Szrj;; font-lock.
110*a9fa9459Szrj(defun dwarf-fontify-region (start end)
111*a9fa9459Szrj  (save-excursion
112*a9fa9459Szrj    (let ((beg-line (progn (goto-char start) (line-beginning-position)))
113*a9fa9459Szrj	  (end-line (progn (goto-char end) (line-end-position))))
114*a9fa9459Szrj      (goto-char beg-line)
115*a9fa9459Szrj      (while (re-search-forward dwarf-die-reference end-line 'move)
116*a9fa9459Szrj	(let ((b-start (match-beginning 1))
117*a9fa9459Szrj	      (b-end (match-end 1))
118*a9fa9459Szrj	      (hex (match-string-no-properties 2)))
119*a9fa9459Szrj	  (make-text-button b-start b-end :type 'dwarf-die-button
120*a9fa9459Szrj			    'die hex 'die-ref t)))
121*a9fa9459Szrj      ;; This is a bogus approach.  Why can't we make buttons from the
122*a9fa9459Szrj      ;; font-lock defaults?
123*a9fa9459Szrj      (goto-char beg-line)
124*a9fa9459Szrj      (while (re-search-forward dwarf-die-more end-line 'move)
125*a9fa9459Szrj	(let ((hex (match-string-no-properties 1))
126*a9fa9459Szrj	      (b-start (match-beginning 2))
127*a9fa9459Szrj	      (b-end (match-end 2)))
128*a9fa9459Szrj	  (make-text-button b-start b-end :type 'dwarf-die-button
129*a9fa9459Szrj			    'die hex 'die-ref nil))))))
130*a9fa9459Szrj
131*a9fa9459Szrj;; Run objdump and insert the contents into the buffer.  The arguments
132*a9fa9459Szrj;; are the way they are because this is also called as a
133*a9fa9459Szrj;; revert-buffer-function.
134*a9fa9459Szrj(defun dwarf-do-refresh (&rest ignore)
135*a9fa9459Szrj  (let ((inhibit-read-only t))
136*a9fa9459Szrj    (erase-buffer)
137*a9fa9459Szrj    (save-excursion
138*a9fa9459Szrj      (call-process dwarf-objdump-program
139*a9fa9459Szrj		    nil (current-buffer) nil
140*a9fa9459Szrj		    "-Wi" "--dwarf-depth=1"
141*a9fa9459Szrj		    (expand-file-name dwarf-file)))
142*a9fa9459Szrj    (set-buffer-modified-p nil)))
143*a9fa9459Szrj
144*a9fa9459Szrj(define-derived-mode dwarf-mode special-mode "DWARF"
145*a9fa9459Szrj  "Major mode for browsing DWARF output.
146*a9fa9459Szrj
147*a9fa9459Szrj\\{dwarf-mode-map}"
148*a9fa9459Szrj
149*a9fa9459Szrj  (set (make-local-variable 'font-lock-defaults) '(dwarf-font-lock-keywords))
150*a9fa9459Szrj  ;; FIXME: we could be smarter and check the file time.
151*a9fa9459Szrj  (set (make-local-variable 'revert-buffer-function) #'dwarf-do-refresh)
152*a9fa9459Szrj  (jit-lock-register #'dwarf-fontify-region))
153*a9fa9459Szrj
154*a9fa9459Szrj(define-key dwarf-mode-map [(control ?m)] #'dwarf-insert-substructure)
155*a9fa9459Szrj
156*a9fa9459Szrj;;;###autoload
157*a9fa9459Szrj(defun dwarf-browse (file)
158*a9fa9459Szrj  "Invoke `objdump' and put output into a `dwarf-mode' buffer.
159*a9fa9459SzrjThis is the main interface to `dwarf-mode'."
160*a9fa9459Szrj  (interactive "fFile name: ")
161*a9fa9459Szrj  (let* ((base-name (file-name-nondirectory file))
162*a9fa9459Szrj	 (buffer (generate-new-buffer (concat "*DWARF for " base-name "*"))))
163*a9fa9459Szrj    (pop-to-buffer buffer)
164*a9fa9459Szrj    (dwarf-mode)
165*a9fa9459Szrj    (set (make-local-variable 'dwarf-file) file)
166*a9fa9459Szrj    (dwarf-do-refresh)))
167*a9fa9459Szrj
168*a9fa9459Szrj(provide 'dwarf-mode)
169*a9fa9459Szrj
170*a9fa9459Szrj;;; dwarf-mode.el ends here
171