3 ;; Author: Nick Roberts <nickrob@gnu.org>
4 ;; Maintainer: Nick Roberts <nickrob@gnu.org>
5 ;; Keywords: unix, tools
7 ;; Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
9 ;; This file is part of GNU GDB.
11 ;; GNU GDB is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
23 ;; This mode acts as a graphical user interface to GDB and works with Emacs
24 ;; 22.x and the version of GDB with which it is distributed. You can interact
25 ;; with GDB through the GUD buffer in the usual way, but there are also
26 ;; buffers which control the execution and describe the state of your program.
27 ;; It separates the input/output of your program from that of GDB and displays
28 ;; expressions and their current values in their own buffers. It also uses
29 ;; features of Emacs 21 such as the fringe/display margin for breakpoints, and
30 ;; the toolbar (see the GDB Graphical Interface section in the Emacs info
33 ;; Start the debugger with M-x gdbmi.
35 ;; This file uses GDB/MI as the primary interface to GDB. It is still under
36 ;; development and is part of a process to migrate Emacs from annotations (as
37 ;; used in gdb-ui.el) to GDB/MI. It runs gdb with GDB/MI (-interp=mi) and
38 ;; access CLI using "-interpreter-exec console cli-command".
42 ;; 1) To handle program input, if required, and to avoid extra output in the
43 ;; GUD buffer you must not use run, step, next or continue etc but their MI
44 ;; counterparts through gud-run, gud-step etc, e.g clicking on the appropriate
45 ;; icon in the toolbar.
46 ;; 2) Some commands send extra prompts to the GUD buffer.
47 ;; 3) Doesn't list catchpoints in breakpoints buffer.
50 ;; 1) Prefix MI commands with a token instead of queueing commands.
51 ;; 2) Use MI command -data-read-memory for memory window.
52 ;; 3) Use MI command -data-disassemble for disassembly window.
53 ;; 4) Allow separate buffers for Inferior IO and GDB IO.
54 ;; 5) Watch windows to work with threads.
61 (defvar gdb-last-command nil
)
62 (defvar gdb-prompt-name nil
)
65 (defun gdbmi (command-line)
66 "Run gdb on program FILE in buffer *gud-FILE*.
67 The directory containing FILE becomes the initial working directory
68 and source-file directory for your debugger.
70 If `gdb-many-windows' is nil (the default value) then gdb just
71 pops up the GUD buffer unless `gdb-show-main' is t. In this case
72 it starts with two windows: one displaying the GUD buffer and the
73 other with the source file with the main routine of the inferior.
75 If `gdb-many-windows' is t, regardless of the value of
76 `gdb-show-main', the layout below will appear. Keybindings are
77 given in relevant buffer.
79 Watch expressions appear in the speedbar/slowbar.
81 The following commands help control operation :
83 `gdb-many-windows' - Toggle the number of windows gdb uses.
84 `gdb-restore-windows' - To restore the window layout.
86 See Info node `(emacs)GDB Graphical Interface' for a more
87 detailed description of this mode.
90 +--------------------------------------------------------------+
92 +-------------------------------+------------------------------+
93 | GUD buffer (I/O of GDB) | Locals buffer |
97 +-------------------------------+------------------------------+
106 +-------------------------------+------------------------------+
107 | Stack buffer | Breakpoints buffer |
108 | RET gdb-frames-select | SPC gdb-toggle-breakpoint |
109 | | RET gdb-goto-breakpoint |
110 | | d gdb-delete-breakpoint |
111 +-------------------------------+------------------------------+"
113 (interactive (list (gud-query-cmdline 'gdbmi
)))
115 ;; Let's start with a basic gud-gdb buffer and then modify it a bit.
118 (setq gdb-debug-ring nil
)
119 (set (make-local-variable 'gud-minor-mode
) 'gdbmi
)
120 (set (make-local-variable 'gud-marker-filter
) 'gud-gdbmi-marker-filter
)
122 (gud-def gud-step
"-exec-step %p" "\C-s"
123 "Step one source line with display.")
124 (gud-def gud-stepi
"-exec-step-instruction %p" "\C-i"
125 "Step one instruction with display.")
126 (gud-def gud-next
"-exec-next %p" "\C-n"
127 "Step one line (skip functions).")
128 (gud-def gud-cont
"-exec-continue" "\C-r"
129 "Continue with display.")
130 (gud-def gud-finish
"-exec-finish" "\C-f"
131 "Finish executing current function.")
132 (gud-def gud-run
"-exec-run" nil
"Run the program.")
133 (gud-def gud-break
(if (not (string-equal mode-name
"Machine"))
134 (gud-call "break %f:%l" arg
)
138 (gud-call "break *%a" arg
)))
139 "\C-b" "Set breakpoint at current line or address.")
141 (gud-def gud-remove
(if (not (string-equal mode-name
"Machine"))
142 (gud-call "clear %f:%l" arg
)
146 (gud-call "clear *%a" arg
)))
147 "\C-d" "Remove breakpoint at current line or address.")
149 (gud-def gud-until
(if (not (string-equal mode-name
"Machine"))
150 (gud-call "-exec-until %f:%l" arg
)
154 (gud-call "-exec-until *%a" arg
)))
155 "\C-u" "Continue to current line or address.")
157 (define-key gud-minor-mode-map
[left-margin mouse-1
]
158 'gdb-mouse-set-clear-breakpoint
)
159 (define-key gud-minor-mode-map
[left-fringe mouse-1
]
160 'gdb-mouse-set-clear-breakpoint
)
161 (define-key gud-minor-mode-map
[left-fringe mouse-2
]
163 (define-key gud-minor-mode-map
[left-fringe drag-mouse-1
]
165 (define-key gud-minor-mode-map
[left-margin mouse-3
]
166 'gdb-mouse-toggle-breakpoint-margin
)
167 (define-key gud-minor-mode-map
[left-fringe mouse-3
]
168 'gdb-mouse-toggle-breakpoint-fringe
)
170 (setq comint-input-sender
'gdbmi-send
)
173 (setq gdb-pc-address
(if gdb-show-main
"main" nil
)
174 gdb-previous-frame-address nil
175 gdb-memory-address
"main"
176 gdb-previous-frame nil
177 gdb-selected-frame nil
184 gdb-pending-triggers nil
185 gdb-output-sink
'user
186 gdb-server-prefix nil
187 gdb-flush-pending-output nil
188 gdb-location-alist nil
189 gdb-source-file-list nil
192 gdb-buffer-fringe-width
(car (window-fringes)))
194 (setq gdb-buffer-type
'gdbmi
)
196 ;; FIXME: use tty command to separate io.
197 ;;(gdb-clear-inferior-io)
199 (if (eq window-system
'w32
)
200 (gdb-enqueue-input (list "-gdb-set new-console off\n" 'ignore
)))
201 (gdb-enqueue-input (list "-gdb-set height 0\n" 'ignore
))
202 ;; find source file and compilation directory here
204 ; Needs GDB 6.2 onwards.
205 (list "-file-list-exec-source-files\n"
206 'gdb-set-gud-minor-mode-existing-buffers-1
))
208 ; Needs GDB 6.0 onwards.
209 (list "-file-list-exec-source-file\n" 'gdb-get-source-file
))
211 (list "-data-list-register-names\n" 'gdb-get-register-names
))
213 (list "-gdb-show prompt\n" 'gdb-get-prompt
))
215 (setq gdb-locals-font-lock-keywords gdb-locals-font-lock-keywords-2
)
216 (run-hooks 'gdbmi-mode-hook
))
219 (defun gdbmi-send (proc string
)
220 "A comint send filter for gdb."
222 (process-send-string proc
(concat string
"\n"))
223 (with-current-buffer gud-comint-buffer
224 (remove-text-properties (point-min) (point-max) '(face)))
225 (setq gdb-output-sink
'user
)
226 (setq gdb-prompting nil
)
227 ;; mimic <RET> key to repeat previous command in GDB
228 (if (not (string-match "^\\s+$" string
))
229 (setq gdb-last-command string
)
230 (if gdb-last-command
(setq string gdb-last-command
)))
232 (push (cons 'mi-send
(concat string
"\n")) gdb-debug-ring
))
233 (if (string-match "^-" string
)
235 (process-send-string proc
(concat string
"\n"))
237 (if (string-match "\\\\$" string
)
238 (setq gdb-continuation
(concat gdb-continuation string
"\n"))
239 (process-send-string proc
240 (concat "-interpreter-exec console \""
241 gdb-continuation string
"\"\n"))
242 (setq gdb-continuation nil
)))))
244 (defcustom gud-gdbmi-command-name
"gdb -interp=mi"
245 "Default command to execute an executable under the GDB-UI debugger."
249 (defconst gdb-gdb-regexp
"(gdb) \n")
251 (defconst gdb-running-regexp
(concat "\\^running\n" gdb-gdb-regexp
))
253 ;; fullname added GDB 6.4+.
254 ;; Probably not needed. -stack-info-frame computes filename and line.
255 (defconst gdb-stopped-regexp
256 "\\*stopped,reason=.*?,file=\".*?\"\
257 ,fullname=\"\\(.*?\\)\",line=\"\\(.*?\\)\"}\n")
259 (defconst gdb-error-regexp
"\\^error,msg=\\(\".+\"\\)\n")
261 (defconst gdb-done-regexp
"\\^done,*\n*")
263 (defconst gdb-console-regexp
"~\\(\".*?[^\\]\"\\)\n")
265 (defconst gdb-internals-regexp
"&\\(\".*?\\n\"\\)\n")
267 (defun gdbmi-prompt1 ()
268 "Queue any GDB commands that the user interface needs."
269 (unless gdb-pending-triggers
270 (when (and (boundp 'speedbar-frame
) (frame-live-p speedbar-frame
))
271 (setq gdb-force-update t
)
272 (dolist (var gdb-var-list
)
273 (setcar (nthcdr 5 var
) nil
))
275 (gdbmi-get-selected-frame)
276 (gdbmi-invalidate-frames)
277 (gdbmi-invalidate-breakpoints)
278 (gdb-get-changed-registers)
279 (gdb-invalidate-registers-1)
280 (gdb-invalidate-locals-1)))
282 (defun gdbmi-prompt2 ()
283 "Handle any output and send next GDB command."
284 (let ((sink gdb-output-sink
))
285 (when (eq sink
'emacs
)
287 (car (cdr gdb-current-item
))))
288 (with-current-buffer (gdb-get-buffer-create 'gdb-partial-output-buffer
)
289 (funcall handler
)))))
290 (let ((input (gdb-dequeue-input)))
292 (gdb-send-item input
)
294 (setq gud-running nil
)
295 (setq gdb-prompting t
)
296 (gud-display-frame)))))
298 (defun gud-gdbmi-marker-filter (string)
299 "Filter GDB/MI output."
300 (if gdb-flush-pending-output
302 (if gdb-enable-debug
(push (cons 'recv
(list string gdb-output-sink
))
304 ;; Recall the left over gud-marker-acc from last time
305 (setq gud-marker-acc
(concat gud-marker-acc string
))
306 ;; Start accumulating output for the GUD buffer
309 (if (string-match gdb-running-regexp gud-marker-acc
)
312 (concat (substring gud-marker-acc
0 (match-beginning 0))
313 (substring gud-marker-acc
(match-end 0)))
316 (if (string-match gdb-stopped-regexp gud-marker-acc
)
319 ;; Extract the frame position from the marker.
320 gud-last-frame
(cons (match-string 1 gud-marker-acc
)
322 (match-string 2 gud-marker-acc
)))
325 (concat (substring gud-marker-acc
0 (match-beginning 0))
326 (substring gud-marker-acc
(match-end 0)))))
328 ;; Filter error messages going to GUD buffer and
329 ;; display in minibuffer.
330 (if (eq gdb-output-sink
'user
)
331 (while (string-match gdb-error-regexp gud-marker-acc
)
332 (message (read (match-string 1 gud-marker-acc
)))
335 (concat (substring gud-marker-acc
0 (match-beginning 0))
336 (substring gud-marker-acc
(match-end 0))))))
338 (if (string-match gdb-done-regexp gud-marker-acc
)
341 (concat (substring gud-marker-acc
0 (match-beginning 0))
342 (substring gud-marker-acc
(match-end 0)))))
344 (when (string-match gdb-gdb-regexp gud-marker-acc
)
347 (concat (substring gud-marker-acc
0 (match-beginning 0))
348 (substring gud-marker-acc
(match-end 0))))
350 ;; Remove the trimmings from the console stream.
351 (while (string-match gdb-console-regexp gud-marker-acc
)
353 gud-marker-acc
(concat
354 (substring gud-marker-acc
0 (match-beginning 0))
355 (read (match-string 1 gud-marker-acc
))
356 (substring gud-marker-acc
(match-end 0)))))
358 ;; Remove the trimmings from log stream containing debugging messages
359 ;; being produced by GDB's internals and use warning face.
360 (while (string-match gdb-internals-regexp gud-marker-acc
)
363 (concat (substring gud-marker-acc
0 (match-beginning 0))
365 (read (match-string 1 gud-marker-acc
))))
367 0 (length error-message
)
368 'face font-lock-warning-face
371 (substring gud-marker-acc
(match-end 0)))))
373 (setq output
(gdbmi-concat-output output gud-marker-acc
))
374 (setq gud-marker-acc
"")
376 (unless gdb-input-queue
377 (setq output
(concat output gdb-prompt-name
)))
381 (setq output
(gdbmi-concat-output output gud-marker-acc
))
382 (setq gud-marker-acc
""))
386 (defun gdbmi-concat-output (so-far new
)
387 (let ((sink gdb-output-sink
))
389 ((eq sink
'user
) (concat so-far new
))
391 (gdb-append-to-partial-output new
)
394 (gdb-append-to-inferior-io new
)
398 ;; Breakpoint buffer : This displays the output of `-break-list'.
400 (def-gdb-auto-update-trigger gdbmi-invalidate-breakpoints
401 (gdb-get-buffer 'gdb-breakpoints-buffer
)
403 gdb-break-list-handler
)
405 (defconst gdb-break-list-regexp
406 "bkpt={.*?number=\"\\(.*?\\)\",.*?type=\"\\(.*?\\)\",.*?disp=\"\\(.*?\\)\",.*?\
407 enabled=\"\\(.\\)\",.*?addr=\"\\(.*?\\)\",\\(?:.*?func=\"\\(.*?\\)\",.*?\
408 file=\"\\(.*?\\)\",.*?fullname=\".*?\",.*?line=\"\\(.*?\\)\",\
409 \\|\\(?:.*?what=\"\\(.*?\\)\",\\)*\\).*?times=\"\\(.*?\\)\".*?}")
411 (defun gdb-break-list-handler ()
412 (setq gdb-pending-triggers
(delq 'gdbmi-invalidate-breakpoints
413 gdb-pending-triggers
))
414 (let ((breakpoint) (breakpoints-list))
415 (with-current-buffer (gdb-get-buffer-create 'gdb-partial-output-buffer
)
416 (goto-char (point-min))
417 (while (re-search-forward gdb-break-list-regexp nil t
)
418 (let ((breakpoint (list (match-string 1)
428 (push breakpoint breakpoints-list
))))
429 (let ((buf (gdb-get-buffer 'gdb-breakpoints-buffer
)))
430 (and buf
(with-current-buffer buf
432 (buffer-read-only nil
))
434 (insert "Num Type Disp Enb Hits Addr What\n")
435 (dolist (breakpoint breakpoints-list
)
438 (nth 0 breakpoint
) " "
439 (nth 1 breakpoint
) " "
440 (nth 2 breakpoint
) " "
441 (nth 3 breakpoint
) " "
442 (nth 9 breakpoint
) " "
443 (nth 4 breakpoint
) " "
444 (if (nth 5 breakpoint
)
445 (concat "in " (nth 5 breakpoint
) " at " (nth 6 breakpoint
) ":" (nth 7 breakpoint
) "\n")
446 (concat (nth 8 breakpoint
) "\n")))))
448 (gdb-info-breakpoints-custom))
450 (defun gdbmi-get-location (bptno line flag
)
451 "Find the directory containing the relevant source file.
452 Put in buffer and place breakpoint icon."
453 (goto-char (point-min))
454 (catch 'file-not-found
455 (if (re-search-forward gdb-source-file-regexp-1 nil t
)
456 (delete (cons bptno
"File not found") gdb-location-alist
)
457 (push (cons bptno
(match-string 1)) gdb-location-alist
)
459 (unless (assoc bptno gdb-location-alist
)
460 (push (cons bptno
"File not found") gdb-location-alist
)
461 (message-box "Cannot find source file for breakpoint location.
462 Add directory to search path for source files using the GDB command, dir."))
463 (throw 'file-not-found nil
))
465 (find-file-noselect (match-string 1))
467 (set (make-local-variable 'gud-minor-mode
) 'gdbmi
)
468 (set (make-local-variable 'tool-bar-map
) gud-tool-bar-map
))
469 ;; only want one breakpoint icon at each location
471 (goto-line (string-to-number line
))
472 (gdb-put-breakpoint-icon (eq flag ?y
) bptno
)))))
474 ;; Frames buffer. This displays a perpetually correct bactrack trace.
476 (def-gdb-auto-update-trigger gdbmi-invalidate-frames
477 (gdb-get-buffer 'gdb-stack-buffer
)
478 "-stack-list-frames\n"
479 gdb-stack-list-frames-handler
)
481 (defconst gdb-stack-list-frames-regexp
482 "{.*?level=\"\\(.*?\\)\",.*?addr=\"\\(.*?\\)\",.*?func=\"\\(.*?\\)\",\
483 \\(?:.*?file=\".*?\",.*?fullname=\"\\(.*?\\)\",.*?line=\"\\(.*?\\)\".*?}\\|\
484 from=\"\\(.*?\\)\"\\)")
486 (defun gdb-stack-list-frames-handler ()
487 (setq gdb-pending-triggers
(delq 'gdbmi-invalidate-frames
488 gdb-pending-triggers
))
491 (with-current-buffer (gdb-get-buffer-create 'gdb-partial-output-buffer
)
492 (goto-char (point-min))
493 (while (re-search-forward gdb-stack-list-frames-regexp nil t
)
494 (let ((frame (list (match-string 1)
500 (push frame call-stack
))))
501 (let ((buf (gdb-get-buffer 'gdb-stack-buffer
)))
502 (and buf
(with-current-buffer buf
504 (buffer-read-only nil
))
506 (insert "Level\tAddr\tFunc\tFile:Line\n")
507 (dolist (frame (nreverse call-stack
))
514 (concat "at "(nth 3 frame
) ":" (nth 4 frame
) "\n")
515 (concat "from " (nth 5 frame
) "\n")))))
517 (gdb-stack-list-frames-custom))
519 (defun gdb-stack-list-frames-custom ()
520 (with-current-buffer (gdb-get-buffer 'gdb-stack-buffer
)
522 (let ((buffer-read-only nil
))
523 (goto-char (point-min))
525 (while (< (point) (point-max))
526 (add-text-properties (point-at-bol) (point-at-eol)
527 '(mouse-face highlight
528 help-echo
"mouse-2, RET: Select frame"))
530 (when (and (looking-at "^[0-9]+\\s-+\\(\\S-+\\)")
531 (equal (match-string 1) gdb-selected-frame
))
532 (put-text-property (point-at-bol) (point-at-eol)
533 'face
'(:inverse-video t
)))
534 (forward-line 1))))))
537 ;; gdb-ui.el uses "info source" to find out if macro information is present.
538 (defun gdb-get-source-file ()
539 "Find the source file where the program starts and display it with related
540 buffers, if required."
541 (goto-char (point-min))
542 (if (re-search-forward gdb-source-file-regexp-1 nil t
)
543 (setq gdb-main-file
(match-string 1)))
546 (gdb-get-buffer-create 'gdb-breakpoints-buffer
)
548 (let ((pop-up-windows t
))
549 (display-buffer (gud-find-file gdb-main-file
))))))
551 (defun gdbmi-get-selected-frame ()
552 (if (not (member 'gdbmi-get-selected-frame gdb-pending-triggers
))
555 (list "-stack-info-frame\n" 'gdbmi-frame-handler
))
556 (push 'gdbmi-get-selected-frame
557 gdb-pending-triggers
))))
559 (defun gdbmi-frame-handler ()
560 (setq gdb-pending-triggers
561 (delq 'gdbmi-get-selected-frame gdb-pending-triggers
))
562 (with-current-buffer (gdb-get-buffer-create 'gdb-partial-output-buffer
)
563 (goto-char (point-min))
564 (when (re-search-forward gdb-stack-list-frames-regexp nil t
)
565 (setq gdb-frame-number
(match-string 1))
566 (setq gdb-pc-address
(match-string 2))
567 (setq gdb-selected-frame
(match-string 3))
568 (when (match-string 4)
570 (cons (match-string 4) (string-to-number (match-string 5))))
572 (if gud-overlay-arrow-position
573 (let ((buffer (marker-buffer gud-overlay-arrow-position
))
574 (position (marker-position gud-overlay-arrow-position
)))
576 (with-current-buffer buffer
577 (setq fringe-indicator-alist
578 (if (string-equal gdb-frame-number
"0")
580 '((overlay-arrow . hollow-right-triangle
))))
581 (setq gud-overlay-arrow-position
(make-marker))
582 (set-marker gud-overlay-arrow-position position
))))))
583 (if (gdb-get-buffer 'gdb-locals-buffer
)
584 (with-current-buffer (gdb-get-buffer 'gdb-locals-buffer
)
585 (setq mode-name
(concat "Locals:" gdb-selected-frame
))))
586 (if (gdb-get-buffer 'gdb-assembler-buffer
)
587 (with-current-buffer (gdb-get-buffer 'gdb-assembler-buffer
)
588 (setq mode-name
(concat "Machine:" gdb-selected-frame
)))))))
590 (defvar gdb-prompt-name-regexp
"value=\"\\(.*?\\)\"")
592 (defun gdb-get-prompt ()
593 "Find prompt for GDB session."
594 (goto-char (point-min))
595 (setq gdb-prompt-name nil
)
596 (re-search-forward gdb-prompt-name-regexp nil t
)
597 (setq gdb-prompt-name
(match-string 1)))
600 ;;; gdbmi.el ends here