]> code.delx.au - gnu-emacs-elpa/blob - packages/realgud/realgud/common/buffer/backtrace.el
7eb11bd9563236fa15a8d781c3a6f5370c7fde13
[gnu-emacs-elpa] / packages / realgud / realgud / common / buffer / backtrace.el
1 ;;; Backtrace buffer
2 ;;; Copyright (C) 2010-2015 Rocky Bernstein <rocky@gnu.org>
3 (require 'load-relative)
4 (eval-when-compile (require 'cl-lib))
5 (require-relative-list
6 '("../key" "helper" "follow" "loc") "realgud-")
7
8 (require-relative-list
9 '("command") "realgud-buffer-")
10
11 (declare-function realgud-cmdbuf-debugger-name 'realgud-buffer-command)
12 (declare-function realgud-cmdbuf? 'realgud-buffer-command)
13 (declare-function realgud-backtrace-mode (cmdbuf))
14 (declare-function realgud:cmd-backtrace (arg))
15 (declare-function realgud-cmdbuf-pat(key))
16 (declare-function realgud-cmdbuf-info-in-srcbuf?= (arg))
17 (declare-function realgud-get-cmdbuf 'realgud-buffer-helper)
18 (declare-function realgud:file-loc-from-line 'realgud-file)
19
20 (defstruct realgud-backtrace-info
21 "debugger object/structure specific to a (top-level) program to be debugged."
22 (cmdbuf nil) ;; buffer of the associated debugger process
23 (cur-pos 0) ;; Frame we are at
24 frame-ring ;; ring of marks in buffer of frame numbers. The
25 ;; text at that marker has additional properties about the
26 ;; frame
27 )
28
29 (declare-function realgud:cmd-frame 'realgud-buffer-command)
30 (declare-function realgud-get-cmdbuf(&optional opt-buffer))
31 (declare-function realgud-command (fmt &optional arg no-record?
32 frame-switch? realgud-prompts?))
33
34 (make-variable-buffer-local (defvar realgud-backtrace-info))
35
36 ;: FIXME: not picked up from track. Why?
37 (defvar realgud-track-divert-string nil)
38
39 (defvar realgud-goto-entry-acc "")
40
41 (defun realgud-get-buffer-base-name(string)
42 "Leading and ending * in string. For example:
43 *shell<2>* -> shell<2>
44 *foo shell* -> foo
45 buffer.c -> buffer.c"
46 (if (string-match "^[*]?\\([^*]+\\)[*]?$" string)
47 (let ((string-sans-stars (match-string 1 string)))
48 (if (string-match "\\(.+\\) shell" string-sans-stars)
49 (match-string 1 string-sans-stars)
50 string-sans-stars)
51 )
52 string
53 )
54 )
55
56 (defun realgud:backtrace-describe (&optional buffer)
57 (interactive "")
58 (unless buffer (setq buffer (current-buffer)))
59 (with-current-buffer buffer
60 (let ((frames (realgud-backtrace-info-frame-ring realgud-backtrace-info))
61 (frame)
62 (loc)
63 (i 0))
64 (switch-to-buffer (get-buffer-create "*Describe*"))
65 (while (and (< i (ring-length frames)) (setq frame (ring-ref frames i)))
66 (insert (format "*** %d\n" i))
67 (insert (format "%s\n" frame))
68 (when (markerp frame)
69 (with-current-buffer (marker-buffer frame)
70 (goto-char frame)
71 (setq loc (get-text-property (point) 'loc))
72 )
73 (when loc (realgud:loc-describe loc)))
74 (setq i (1+ i))
75 )
76 )
77 ))
78
79 ;; FIXME: create this in a new frame.
80 (defun realgud:backtrace-init ()
81 (interactive)
82 (let ((buffer (current-buffer))
83 (cmdbuf (realgud-get-cmdbuf))
84 (process)
85 )
86 (with-current-buffer-safe cmdbuf
87 (let ((frame-pat (realgud-cmdbuf-pat "debugger-backtrace"))
88 (indicator-re (or (realgud-cmdbuf-pat "selected-frame-indicator")
89 "->"))
90 (selected-frame-num)
91 (frame-pos-ring)
92 (sleep-count 0)
93 )
94 (unless frame-pat
95 (error "No 'debugger-backtrace' regular expression recorded for debugger %s"
96 (realgud-cmdbuf-debugger-name)))
97 (setq process (get-buffer-process (current-buffer)))
98 (realgud-cmdbuf-info-in-srcbuf?= (not (realgud-cmdbuf? buffer)))
99 (realgud-cmdbuf-info-divert-output?= 't)
100 (setq realgud-track-divert-string nil)
101 (realgud:cmd-backtrace 0)
102 (while (and (eq 'run (process-status process))
103 (null realgud-track-divert-string)
104 (> 1000 (setq sleep-count (1+ sleep-count))))
105 (sleep-for 0.001)
106 )
107 (if (>= sleep-count 1000)
108 (message "Timeout on running debugger command")
109 ;; else
110 ;; (message "+++4 %s" realgud-track-divert-string)
111 (let ((bt-buffer (get-buffer-create
112 (format "*Backtrace %s*"
113 (realgud-get-buffer-base-name
114 (buffer-name)))))
115 (divert-string realgud-track-divert-string)
116 )
117 (realgud-cmdbuf-info-bt-buf= bt-buffer)
118 (with-current-buffer bt-buffer
119 (setq buffer-read-only nil)
120 (delete-region (point-min) (point-max))
121 (if divert-string
122 (let* ((triple
123 (realgud:backtrace-add-text-properties
124 frame-pat cmdbuf indicator-re))
125 (string-with-props
126 (ansi-color-filter-apply (car triple)))
127 (frame-num-pos-list (caddr triple))
128 )
129 (setq selected-frame-num (cadr triple))
130 (insert string-with-props)
131 ;; add marks for each position
132 (realgud-backtrace-mode cmdbuf)
133 (setq frame-pos-ring
134 (make-ring (length frame-num-pos-list)))
135 (dolist (pos frame-num-pos-list)
136 (goto-char (1+ pos))
137 (ring-insert-at-beginning frame-pos-ring (point-marker))
138 )
139 )
140 )
141 ;; realgud-backtrace-mode kills all local variables so
142 ;; we set this after. Alternatively change realgud-backtrace-mode.
143 (set (make-local-variable 'realgud-backtrace-info)
144 (make-realgud-backtrace-info
145 :cmdbuf cmdbuf
146 :frame-ring frame-pos-ring
147 ))
148 (if selected-frame-num
149 (realgud-backtrace-moveto-frame selected-frame-num))
150 )
151 )
152 )
153 )
154 )
155 (unless cmdbuf
156 (message "Unable to find debugger command buffer for %s" buffer))
157 )
158 )
159
160 (defun realgud-backtrace? ( &optional buffer)
161 "Return true if BUFFER is a debugger command buffer."
162 (with-current-buffer-safe
163 (or buffer (current-buffer))
164 (realgud-backtrace-info-set?)))
165
166
167 (defalias 'realgud-backtrace-info? 'realgud-backtrace-info-p)
168
169 (defun realgud-backtrace-info-set? ()
170 "Return true if realgud-backtrace-info is set."
171 (and (boundp 'realgud-backtrace-info)
172 realgud-backtrace-info
173 (realgud-backtrace-info? realgud-backtrace-info)))
174
175
176 (defun realgud-backtrace-moveto-frame-selected ()
177 "Set point to the selected frame."
178 (interactive)
179 (if (realgud-backtrace?)
180 (let* ((cur-pos (realgud-sget 'backtrace-info 'cur-pos))
181 (ring-size (ring-size (realgud-sget 'backtrace-info 'frame-ring)))
182 )
183 (if (and cur-pos (> ring-size 0))
184 (realgud-backtrace-moveto-frame cur-pos)
185 ;else
186 (message "No frame information recorded")
187 )
188 )
189 )
190 )
191
192 (defun realgud-backtrace-moveto-frame (num &optional opt-buffer)
193 (if (integerp num)
194 (if (realgud-backtrace?)
195 (let* ((ring (realgud-sget 'backtrace-info 'frame-ring))
196 (marker (ring-ref ring num)))
197 (setf (realgud-backtrace-info-cur-pos realgud-backtrace-info) num)
198 (goto-char marker)
199 )
200 )
201 ; else
202 (message "frame number %s is not an integer" num)
203 )
204 )
205
206 (defun realgud-backtrace-moveto-frame-next ()
207 "Set point to the next frame. If we are at the end, wrap to the
208 beginning. Note that we are just moving in the backtrace buffer,
209 not updating the frame stack."
210 (interactive)
211 (if (realgud-backtrace?)
212 (let* ((cur-pos (realgud-sget 'backtrace-info 'cur-pos))
213 (ring-size (ring-size (realgud-sget 'backtrace-info 'frame-ring)))
214 )
215 (if (and cur-pos (> ring-size 0))
216 (realgud-backtrace-moveto-frame (ring-plus1 cur-pos ring-size))
217 ;else
218 (message "No frame information recorded")
219 )
220 )
221 )
222 )
223
224 (defun realgud-backtrace-moveto-frame-prev ()
225 "Set point to the next frame. If we are at the beginning, wrap to the
226 end. Note that we are just moving in the backtrace buffer,
227 not updating the frame stack."
228 (interactive)
229 (if (realgud-backtrace?)
230 (let* ((cur-pos (realgud-sget 'backtrace-info 'cur-pos))
231 (ring-size (ring-size (realgud-sget 'backtrace-info 'frame-ring)))
232 )
233 (if (and cur-pos (> ring-size 0))
234 (realgud-backtrace-moveto-frame (ring-minus1 cur-pos ring-size))
235 ;else
236 (message "No frame information recorded")
237 )
238 )
239 )
240 )
241
242 (defun realgud-goto-frame-n-internal (keys)
243 (if (and (stringp keys)
244 (= (length keys) 1))
245 (progn
246 (setq realgud-goto-entry-acc (concat realgud-goto-entry-acc keys))
247 ;; Try to find the longest suffix.
248 (let ((acc realgud-goto-entry-acc))
249 (while (not (string= acc ""))
250 (if (not (realgud-goto-entry-try acc))
251 (setq acc (substring acc 1))
252 (realgud:cmd-frame (string-to-number acc))
253 ;; Break loop.
254 (setq acc "")))))
255 (message "`realgud-goto-frame-n' must be bound to a number key")))
256
257 ;; FIXME: replace with ring.
258 (defun realgud-goto-entry-try (str)
259 "See if there is an entry with number STR. If not return nil."
260 (goto-char (point-min))
261 (if (re-search-forward (concat "^[^0-9]*\\(" str "\\)[^0-9]") nil t)
262 (progn
263 (goto-char (match-end 1))
264 t)
265 nil))
266
267
268 ;; The following is split in two to facilitate debugging.
269 (defun realgud-goto-entry-n-internal (keys)
270 (if (and (stringp keys)
271 (= (length keys) 1))
272 (progn
273 (setq realgud-goto-entry-acc (concat realgud-goto-entry-acc keys))
274 ;; Try to find the longest suffix.
275 (let ((acc realgud-goto-entry-acc)
276 (p (point)))
277 (while (not (string= acc ""))
278 (if (not (realgud-goto-entry-try acc))
279 (setq acc (substring acc 1))
280 (setq p (point))
281 ;; Break loop.
282 (setq acc "")))
283 (goto-char p)))
284 (message "`realgud-goto-entry-n' must be bound to a number key")))
285
286
287 (defun realgud-goto-entry-n ()
288 "Go to an entry number.
289
290 Breakpoints, Display expressions and Stack Frames all have
291 numbers associated with them which are distinct from line
292 numbers. In a secondary buffer, this function is usually bound to
293 a numeric key which will position you at that entry number. To
294 go to an entry above 9, just keep entering the number. For
295 example, if you press 1 and then 9, you should jump to entry
296 1 (if it exists) and then 19 (if that exists). Entering any
297 non-digit will start entry number from the beginning again."
298 (interactive)
299 (if (not (eq last-command 'realgud-goto-entry-n))
300 (setq realgud-goto-entry-acc ""))
301 (realgud-goto-entry-n-internal (this-command-keys)))
302
303 (defun realgud-goto-frame ()
304 "Go to the frame number. We get the frame number from the
305 'frame-num property"
306 (interactive)
307 (if (realgud-backtrace?)
308 (let ((frame-num (get-text-property (point) 'frame-num)))
309 (if frame-num
310 (realgud:cmd-frame frame-num)
311 (message "No frame property found at this point")
312 )
313 )
314 )
315 )
316
317 (defun realgud-goto-frame-1 ()
318 "Go to the frame 1"
319 (interactive)
320 (if (realgud-backtrace?)
321 (realgud:cmd-frame 1)
322 )
323 )
324
325 (defun realgud-goto-frame-2 ()
326 "Go to the frame 2"
327 (interactive)
328 (if (realgud-backtrace?)
329 (realgud:cmd-frame 2)
330 )
331 )
332
333 (defun realgud-goto-frame-3 ()
334 "Go to the frame 3"
335 (interactive)
336 (if (realgud-backtrace?)
337 (realgud:cmd-frame 3)
338 )
339 )
340
341 (defun realgud-goto-frame-mouse (event)
342 (interactive "e")
343 (let* ((pos (posn-point (event-end event)))
344 (frame-num (get-text-property pos 'frame-num)))
345 (if (realgud-backtrace?)
346 (if frame-num
347 (realgud:cmd-frame frame-num)
348 (message "No frame property found at this point")
349 )
350 )
351 )
352 )
353
354 (defun realgud-goto-frame-n ()
355 "Go to the frame number indicated by the accumulated numeric keys just entered.
356
357 This function is usually bound to a numeric key in a 'frame'
358 secondary buffer. To go to an entry above 9, just keep entering
359 the number. For example, if you press 1 and then 9, frame 1 is selected
360 \(if it exists) and then frame 19 (if that exists). Entering any
361 non-digit will start entry number from the beginning again."
362 (interactive)
363 (if (not (eq last-command 'realgud-goto-frame-n))
364 (setq realgud-goto-entry-acc ""))
365 (realgud-goto-frame-n-internal (this-command-keys)))
366
367 (defun realgud:backtrace-add-text-properties(frame-pat cmdbuf &optional opt-string
368 frame-indicator-re)
369 "Parse OPT-STRING or the current buffer and add frame properties: frame number,
370 filename, line number, whether the frame is selected as text properties."
371
372 (let* ((string (or opt-string
373 (buffer-substring (point-min) (point-max))
374 ))
375 (stripped-string (ansi-color-filter-apply string))
376 (frame-regexp (realgud-loc-pat-regexp frame-pat))
377 (frame-group-pat (realgud-loc-pat-num frame-pat))
378 (file-group-pat (realgud-loc-pat-file-group frame-pat))
379 (line-group-pat (realgud-loc-pat-line-group frame-pat))
380 (alt-frame-num -1)
381 (last-pos 0)
382 (selected-frame-num nil)
383 (frame-num-pos-list '())
384 )
385 (while (string-match frame-regexp stripped-string last-pos)
386 (let ((frame-num-str) (frame-num) (line-num) (filename)
387 ;; FIXME: Remove hack that group 1 is always the frame indicator.
388 (frame-indicator
389 (substring stripped-string (match-beginning 1) (match-end 1)))
390 (frame-num-pos)
391
392 )
393 (if frame-group-pat
394 (progn
395 (setq frame-num-str
396 (substring stripped-string
397 (match-beginning frame-group-pat)
398 (match-end frame-group-pat)))
399 (setq frame-num (string-to-number frame-num-str))
400 (setq frame-num-pos (match-beginning frame-group-pat))
401 (add-to-list 'frame-num-pos-list frame-num-pos 't)
402 (add-text-properties (match-beginning frame-group-pat)
403 (match-end frame-group-pat)
404 (list 'mouse-face 'highlight
405 'help-echo "mouse-2: goto this frame"
406 'frame frame-num)
407 string)
408 )
409 ; else
410 (progn
411 (setq frame-num-str
412 (substring stripped-string (match-beginning 0)
413 (match-end 0)))
414 (setq frame-num (incf alt-frame-num))
415 (setq frame-num-pos (match-beginning 0))
416 (add-to-list 'frame-num-pos-list frame-num-pos 't)
417 (add-text-properties (match-beginning 0) (match-end 0)
418 (list 'mouse-face 'highlight
419 'help-echo "mouse-2: goto this frame"
420 'frame frame-num)
421 string)
422 )
423 )
424 (when file-group-pat
425 (setq filename (substring stripped-string
426 (match-beginning file-group-pat)
427 (match-end file-group-pat)))
428 (add-text-properties (match-beginning file-group-pat)
429 (match-end file-group-pat)
430 (list 'mouse-face 'highlight
431 'help-echo "mouse-2: goto this file"
432 'action 'realgud:follow-event
433 'file filename)
434 string)
435 )
436 (when line-group-pat
437 (let ((line-num-str (substring stripped-string
438 (match-beginning line-group-pat)
439 (match-end line-group-pat))))
440 (setq line-num (string-to-number (or line-num-str "1")))
441 ))
442
443 (when (and (stringp filename) (numberp line-num))
444 (let ((loc (realgud:file-loc-from-line filename line-num cmdbuf)))
445 (put-text-property (match-beginning 0) (match-end 0)
446 'loc loc string)
447 ))
448 (put-text-property (match-beginning 0) (match-end 0)
449 'frame-num frame-num string)
450 (setq last-pos (match-end 0))
451
452 (if (string-match frame-indicator-re frame-indicator)
453 (setq selected-frame-num frame-num))
454 ))
455
456 (list string selected-frame-num frame-num-pos-list)
457 )
458 )
459
460 (provide-me "realgud-buffer-")