Commit | Line | Data |
---|---|---|
359f46a4 LC |
1 | ;;; GDB debugging support for Guile. |
2 | ;;; | |
ef52b399 | 3 | ;;; Copyright 2014, 2015 Free Software Foundation, Inc. |
359f46a4 LC |
4 | ;;; |
5 | ;;; This program is free software; you can redistribute it and/or modify it | |
6 | ;;; under the terms of the GNU General Public License as published by | |
7 | ;;; the Free Software Foundation; either version 3 of the License, or (at | |
8 | ;;; your option) any later version. | |
9 | ;;; | |
10 | ;;; This program is distributed in the hope that it will be useful, but | |
11 | ;;; WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | ;;; GNU General Public License for more details. | |
14 | ;;; | |
15 | ;;; You should have received a copy of the GNU General Public License | |
16 | ;;; along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | ||
18 | (define-module (guile-gdb) | |
19 | #:use-module (system base types) | |
ef52b399 AW |
20 | #:use-module (system vm debug) |
21 | #:use-module ((gdb) #:hide (symbol? frame?)) | |
22 | #:use-module ((gdb) #:select ((symbol? . gdb:symbol?) (frame? . gdb:frame?))) | |
359f46a4 | 23 | #:use-module (gdb printing) |
ef52b399 | 24 | #:use-module (srfi srfi-9) |
359f46a4 | 25 | #:use-module (srfi srfi-11) |
ef52b399 AW |
26 | #:use-module (srfi srfi-41) |
27 | #:use-module (ice-9 match) | |
28 | #:use-module (ice-9 binary-ports) | |
359f46a4 LC |
29 | #:export (%gdb-memory-backend |
30 | display-vm-frames)) | |
31 | ||
32 | ;;; Commentary: | |
33 | ;;; | |
34 | ;;; This file defines GDB extensions to pretty-print 'SCM' objects, and | |
35 | ;;; to walk Guile's virtual machine stack. | |
36 | ;;; | |
37 | ;;; This file is installed under a name that follows the convention that | |
38 | ;;; allows GDB to auto-load it anytime the user is debugging libguile | |
39 | ;;; (info "(gdb) objfile-gdbdotext file"). | |
40 | ;;; | |
41 | ;;; Code: | |
42 | ||
43 | (define (type-name-from-descriptor descriptor-array type-number) | |
44 | "Return the name of the type TYPE-NUMBER as seen in DESCRIPTOR-ARRAY, or #f | |
45 | if the information is not available." | |
46 | (let ((descriptors (lookup-global-symbol descriptor-array))) | |
47 | (and descriptors | |
48 | (let ((code (type-code (symbol-type descriptors)))) | |
49 | (or (= TYPE_CODE_ARRAY code) | |
50 | (= TYPE_CODE_PTR code))) | |
51 | (let* ((type-descr (value-subscript (symbol-value descriptors) | |
52 | type-number)) | |
53 | (name (value-field type-descr "name"))) | |
54 | (value->string name))))) | |
55 | ||
56 | (define %gdb-memory-backend | |
57 | ;; The GDB back-end to access the inferior's memory. | |
58 | (let ((void* (type-pointer (lookup-type "void")))) | |
59 | (define (dereference-word address) | |
60 | ;; Return the word at ADDRESS. | |
61 | (value->integer | |
62 | (value-dereference (value-cast (make-value address) | |
63 | (type-pointer void*))))) | |
64 | ||
65 | (define (open address size) | |
66 | ;; Return a port to the SIZE bytes starting at ADDRESS. | |
67 | (if size | |
68 | (open-memory #:start address #:size size) | |
69 | (open-memory #:start address))) | |
70 | ||
71 | (define (type-name kind number) | |
72 | ;; Return the type name of KIND type NUMBER. | |
73 | (type-name-from-descriptor (case kind | |
74 | ((smob) "scm_smobs") | |
75 | ((port) "scm_ptobs")) | |
76 | number)) | |
77 | ||
78 | (memory-backend dereference-word open type-name))) | |
79 | ||
80 | \f | |
81 | ;;; | |
82 | ;;; GDB pretty-printer registration. | |
83 | ;;; | |
84 | ||
85 | (define scm-value->string | |
86 | (lambda* (value #:optional (backend %gdb-memory-backend)) | |
87 | "Return a representation of value VALUE as a string." | |
88 | (object->string (scm->object (value->integer value) backend)))) | |
89 | ||
90 | (define %scm-pretty-printer | |
91 | (make-pretty-printer "SCM" | |
92 | (lambda (pp value) | |
93 | (let ((name (type-name (value-type value)))) | |
94 | (and (and name (string=? name "SCM")) | |
95 | (make-pretty-printer-worker | |
96 | #f ; display hint | |
97 | (lambda (printer) | |
98 | (scm-value->string value %gdb-memory-backend)) | |
99 | #f)))))) | |
100 | ||
101 | (define* (register-pretty-printer #:optional objfile) | |
102 | (prepend-pretty-printer! objfile %scm-pretty-printer)) | |
103 | ||
104 | (register-pretty-printer) | |
105 | ||
106 | \f | |
107 | ;;; | |
108 | ;;; VM stack walking. | |
109 | ;;; | |
110 | ||
ef52b399 AW |
111 | (define ip-type (type-pointer (lookup-type "scm_t_uint32"))) |
112 | (define fp-type (type-pointer (lookup-type "SCM"))) | |
113 | (define sp-type (type-pointer (lookup-type "SCM"))) | |
359f46a4 | 114 | |
ef52b399 AW |
115 | (define-record-type <vm-frame> |
116 | (make-vm-frame ip sp fp saved-ip saved-fp) | |
117 | vm-frame? | |
118 | (ip vm-frame-ip) | |
119 | (sp vm-frame-sp) | |
120 | (fp vm-frame-fp) | |
121 | (saved-ip vm-frame-saved-ip) | |
122 | (saved-fp vm-frame-saved-fp)) | |
123 | ||
124 | ;; See libguile/frames.h. | |
125 | (define* (vm-frame ip sp fp #:optional (backend %gdb-memory-backend)) | |
126 | "Return the components of the stack frame at FP." | |
127 | (make-vm-frame ip | |
128 | sp | |
129 | fp | |
130 | (value-dereference (value-cast (value-sub fp 1) | |
131 | (type-pointer ip-type))) | |
132 | (value-dereference (value-cast (value-sub fp 2) | |
133 | (type-pointer fp-type))))) | |
134 | ||
135 | (define (vm-engine-frame? frame) | |
136 | (let ((sym (frame-function frame))) | |
137 | (and sym | |
138 | (member (symbol-name sym) | |
139 | '("vm_debug_engine" "vm_regular_engine"))))) | |
140 | ||
141 | (define (find-vp) | |
142 | "Find the scm_vm pointer for the current thread." | |
359f46a4 LC |
143 | (let loop ((frame (newest-frame))) |
144 | (and frame | |
145 | (if (vm-engine-frame? frame) | |
ef52b399 | 146 | (frame-read-var frame "vp") |
359f46a4 LC |
147 | (loop (frame-older frame)))))) |
148 | ||
ef52b399 AW |
149 | (define (newest-vm-frame) |
150 | "Return the newest VM frame or #f." | |
151 | (let ((vp (find-vp))) | |
152 | (and vp | |
153 | (vm-frame (value-field vp "ip") | |
154 | (value-field vp "sp") | |
155 | (value-field vp "fp"))))) | |
359f46a4 | 156 | |
ef52b399 AW |
157 | (define* (vm-frame-older frame #:optional (backend %gdb-memory-backend)) |
158 | (let ((ip (vm-frame-saved-ip frame)) | |
159 | (sp (value-sub (vm-frame-fp frame) 3)) | |
160 | (fp (vm-frame-saved-fp frame))) | |
161 | (and (not (zero? (value->integer fp))) | |
162 | (vm-frame ip sp fp backend)))) | |
163 | ||
164 | (define (vm-frames) | |
165 | "Return a SRFI-41 stream of the current VM frame stack." | |
166 | (stream-unfold identity | |
167 | vm-frame? | |
168 | vm-frame-older | |
169 | (newest-vm-frame))) | |
170 | ||
171 | (define (vm-frame-locals frame) | |
172 | (let ((fp (vm-frame-fp frame)) | |
173 | (sp (vm-frame-sp frame))) | |
174 | (let lp ((slot 0) (ptr fp)) | |
175 | (if (value<=? ptr sp) | |
176 | (acons (string-append "v" (number->string slot)) | |
177 | (value-dereference ptr) | |
178 | (lp (1+ slot) (value-add ptr 1))) | |
179 | '())))) | |
180 | ||
181 | (define (lookup-symbol-or-false name) | |
182 | (match (lookup-symbol name) | |
183 | (#f #f) | |
184 | ((sym _) sym))) | |
185 | ||
186 | (define (find-mapped-elf-image addr) | |
187 | (let ((array (lookup-symbol-or-false "mapped_elf_images")) | |
188 | (count (lookup-symbol-or-false "mapped_elf_images_count"))) | |
189 | (and array count | |
190 | (let ((array (symbol-value array)) | |
191 | (count (value->integer (symbol-value count)))) | |
192 | (let lp ((start 0) (end count)) | |
193 | (if (< start end) | |
194 | (let ((n (+ start (ash (- end start) -1)))) | |
195 | (if (value<? addr (value-field (value-add array n) "end")) | |
196 | (lp start n) | |
197 | (lp (1+ n) end))) | |
198 | (let ((mei (value-add array start))) | |
199 | (and (value<=? (value-field mei "start") addr) | |
200 | mei)))))))) | |
201 | ||
202 | (define (vm-frame-program-debug-info frame) | |
203 | (let ((addr (vm-frame-ip frame))) | |
204 | (and=> (find-mapped-elf-image addr) | |
205 | (lambda (mei) | |
206 | (let* ((start (value->integer (value-field mei "start"))) | |
207 | (size (- (value->integer (value-field mei "end")) | |
208 | start)) | |
209 | (mem-port (open-memory #:start start #:size size)) | |
210 | (bv (get-bytevector-all mem-port)) | |
211 | (ctx (debug-context-from-image bv))) | |
212 | ;; The image is in this process at "bv", but in the | |
213 | ;; inferior at mei.start. Therefore we relocate addr | |
214 | ;; before we look for the PDI. | |
215 | (let ((addr (+ (value->integer addr) | |
216 | (- (debug-context-base ctx) start)))) | |
217 | (find-program-debug-info addr ctx))))))) | |
218 | ||
219 | (define (vm-frame-function-name frame) | |
220 | (define (default-name) | |
221 | (format #f "0x~x" (value->integer (vm-frame-ip frame)))) | |
222 | (cond | |
223 | ((vm-frame-program-debug-info frame) | |
224 | => (lambda (pdi) | |
225 | (or (and=> (program-debug-info-name pdi) symbol->string) | |
226 | (default-name)))) | |
227 | (else | |
228 | (let ((ip (vm-frame-ip frame))) | |
229 | (define (ip-in-symbol? name) | |
230 | (let ((sym (lookup-symbol-or-false name))) | |
231 | (and sym | |
232 | (let* ((val (symbol-value sym)) | |
233 | (size (type-sizeof (value-type val))) | |
234 | (char* (type-pointer (arch-char-type (current-arch)))) | |
235 | (val-as-char* (value-cast val char*))) | |
236 | (and (value<=? val-as-char* ip) | |
237 | (value<? ip (value-add val-as-char* size))))))) | |
238 | (cond | |
239 | ((ip-in-symbol? "vm_boot_continuation_code") "[boot continuation]") | |
240 | ;; FIXME: For subrs, read the name from slot 0 in the frame. | |
241 | ((ip-in-symbol? "subr_stub_code") "[subr call]") | |
242 | ((ip-in-symbol? "vm_builtin_apply_code") "apply") | |
243 | ((ip-in-symbol? "vm_builtin_values_code") "values") | |
244 | ((ip-in-symbol? "vm_builtin_abort_to_prompt_code") "abort-to-prompt") | |
245 | ((ip-in-symbol? "vm_builtin_call_with_values_code") "call-with-values") | |
246 | ((ip-in-symbol? "vm_builtin_call_with_current_continuation_code") | |
247 | "call-with-current-continuation") | |
248 | ((ip-in-symbol? "continuation_stub_code") "[continuation]") | |
249 | ((ip-in-symbol? "compose_continuation_code") "[delimited continuation]") | |
250 | ((ip-in-symbol? "foreign_stub_code") "[ffi call]") | |
251 | (else (default-name))))))) | |
252 | ||
253 | (define* (dump-vm-frame frame #:optional (port (current-output-port))) | |
254 | (format port " name: ~a~%" (vm-frame-function-name frame)) | |
255 | (format port " ip: 0x~x~%" (value->integer (vm-frame-ip frame))) | |
256 | (format port " fp: 0x~x~%" (value->integer (vm-frame-fp frame))) | |
257 | (for-each (match-lambda | |
258 | ((name . val) | |
259 | (let ((obj (scm->object (value->integer val) %gdb-memory-backend))) | |
260 | (format port " ~a: ~a~%" name obj)))) | |
261 | (vm-frame-locals frame))) | |
359f46a4 LC |
262 | |
263 | (define* (display-vm-frames #:optional (port (current-output-port))) | |
264 | "Display the VM frames on PORT." | |
ef52b399 AW |
265 | (stream-for-each (lambda (frame) |
266 | (dump-vm-frame frame port)) | |
267 | (vm-frames))) | |
359f46a4 | 268 | |
475772ea | 269 | ;;; libguile-2.2-gdb.scm ends here |