home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / lucid / lemacs-19.6 / lisp / comint / gdb.el < prev    next >
Encoding:
Text File  |  1993-02-17  |  15.6 KB  |  446 lines

  1. ;; Run gdb under Emacs
  2. ;; Author: W. Schelter, University of Texas
  3. ;;     wfs@rascal.ics.utexas.edu
  4. ;; Rewritten by rms.
  5.  
  6. ;; Some ideas are due to  Masanobu. 
  7.  
  8. ;; This file is part of GNU Emacs.
  9. ;; Copyright (C) 1988 Free Software Foundation, Inc.
  10.  
  11. ;; GNU Emacs is distributed in the hope that it will be useful, but
  12. ;; WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
  13. ;; to anyone for the consequences of using it or for whether it serves
  14. ;; any particular purpose or works at all, unless he says so in writing.
  15. ;; Refer to the GNU Emacs General Public License for full details.
  16.  
  17. ;; Everyone is granted permission to copy, modify and redistribute GNU
  18. ;; Emacs, but only under the conditions described in the GNU Emacs
  19. ;; General Public License.  A copy of this license is supposed to have
  20. ;; been given to you along with GNU Emacs so you can know your rights and
  21. ;; responsibilities.  It should be in a file named COPYING.  Among other
  22. ;; things, the copyright notice and this notice must be preserved on all
  23. ;; copies.
  24.  
  25. ;; Description of GDB interface:
  26.  
  27. ;; A facility is provided for the simultaneous display of the source code
  28. ;; in one window, while using gdb to step through a function in the
  29. ;; other.  A small arrow in the source window, indicates the current
  30. ;; line.
  31.  
  32. ;; Starting up:
  33.  
  34. ;; In order to use this facility, invoke the command GDB to obtain a
  35. ;; shell window with the appropriate command bindings.  You will be asked
  36. ;; for the name of a file to run.  Gdb will be invoked on this file, in a
  37. ;; window named *gdb-foo* if the file is foo.
  38.  
  39. ;; M-s steps by one line, and redisplays the source file and line.
  40.  
  41. ;; You may easily create additional commands and bindings to interact
  42. ;; with the display.  For example to put the gdb command next on \M-n
  43. ;; (def-gdb next "\M-n")
  44.  
  45. ;; This causes the emacs command gdb-next to be defined, and runs
  46. ;; gdb-display-frame after the command.
  47.  
  48. ;; gdb-display-frame is the basic display function.  It tries to display
  49. ;; in the other window, the file and line corresponding to the current
  50. ;; position in the gdb window.  For example after a gdb-step, it would
  51. ;; display the line corresponding to the position for the last step.  Or
  52. ;; if you have done a backtrace in the gdb buffer, and move the cursor
  53. ;; into one of the frames, it would display the position corresponding to
  54. ;; that frame.
  55.  
  56. ;; gdb-display-frame is invoked automatically when a filename-and-line-number
  57. ;; appears in the output.
  58.  
  59.  
  60. (require 'comint)
  61. (require 'shell)
  62.  
  63. (defvar gdb-last-frame)
  64. (defvar gdb-delete-prompt-marker)
  65. (defvar gdb-filter-accumulator)
  66. (defvar gdb-last-frame-displayed-p)
  67.  
  68. (defvar gdb-prompt-pattern "^(.*gdb[+]?) *"
  69.   "A regexp to recognize the prompt for gdb or gdb+.") 
  70.  
  71. (defvar gdb-mode-map nil
  72.   "Keymap for gdb-mode.")
  73.  
  74. (if gdb-mode-map
  75.    nil
  76.   (setq gdb-mode-map (copy-keymap comint-mode-map))
  77.   (define-key gdb-mode-map "\C-l" 'gdb-refresh)
  78.   (define-key gdb-mode-map "\C-c\C-c" 'gdb-control-c-subjob)
  79.   (define-key gdb-mode-map "\t" 'comint-dynamic-complete)
  80.   (define-key gdb-mode-map "\M-?" 'comint-dynamic-list-completions))
  81.  
  82. (define-key ctl-x-map " " 'gdb-break)
  83. (define-key ctl-x-map "&" 'send-gdb-command)
  84.  
  85. ;;Of course you may use `def-gdb' with any other gdb command, including
  86. ;;user defined ones.   
  87.  
  88. (defmacro def-gdb (name key &optional doc)
  89.   (let* ((fun (intern (format "gdb-%s" name)))
  90.      (cstr (list 'if '(not (= 1 arg))
  91.              (list 'format "%s %s" name 'arg)
  92.              name)))
  93.     (list 'progn
  94.        (list 'defun fun '(arg)
  95.         (or doc "")
  96.         '(interactive "p")
  97.         (list 'gdb-call cstr))
  98.       (if key
  99.           (list 'define-key 'gdb-mode-map key  (list 'quote fun))
  100.         nil))))
  101.  
  102. (def-gdb "step"   "\M-s" "Step one source line with display")
  103. (def-gdb "stepi"  "\M-i" "Step one instruction with display")
  104. (def-gdb "finish" "\C-c\C-f" "Finish executing current function")
  105.  
  106. ;;"next" and "cont" were bound to M-n and M-c in Emacs 18, but these are
  107. ;;poor choices, since M-n is used for history navigation and M-c is
  108. ;;capitalize-word.  These are defined without key bindings so that users
  109. ;;may choose their own bindings.
  110. (def-gdb "next"   nil "Step one source line (skip functions)")
  111. (def-gdb "cont"   nil "Proceed with the program")
  112.  
  113. (def-gdb "up"     "\C-c<" "Go up N stack frames (numeric arg) with display")
  114. (def-gdb "down"   "\C-c>" "Go down N stack frames (numeric arg) with display")
  115.  
  116.  
  117. (defun gdb-mode ()
  118.   "Major mode for interacting with an inferior Gdb process.
  119. The following commands are available:
  120.  
  121. \\{gdb-mode-map}
  122.  
  123. \\[gdb-display-frame] displays in the other window
  124. the last line referred to in the gdb buffer.
  125.  
  126. \\[gdb-step],\\[gdb-next], and \\[gdb-nexti] in the gdb window,
  127. call gdb to step,next or nexti and then update the other window
  128. with the current file and position.
  129.  
  130. If you are in a source file, you may select a point to break
  131. at, by doing \\[gdb-break].
  132.  
  133. Commands:
  134. Many commands are inherited from comint mode. 
  135. Additionally we have:
  136.  
  137. \\[gdb-display-frame] display frames file in other window
  138. \\[gdb-step] advance one line in program
  139. \\[send-gdb-command] used for special printing of an arg at the current point.
  140. C-x SPACE sets break point at current line."
  141.   (interactive)
  142.   (comint-mode)
  143.   (use-local-map gdb-mode-map)
  144.   (set-syntax-table c-mode-syntax-table)
  145.   (mapcar 'make-local-variable
  146.       '(gdb-last-frame-displayed-p  gdb-last-frame
  147.         gdb-delete-prompt-marker    gdb-filter-accumulator))
  148.   (setq
  149.    gdb-last-frame nil
  150.    gdb-delete-prompt-marker nil
  151.    gdb-filter-accumulator nil
  152.    major-mode 'gdb-mode
  153.    mode-name "Inferior GDB"
  154.    comint-prompt-regexp gdb-prompt-pattern
  155.    gdb-last-frame-displayed-p t)
  156.   (make-local-variable 'shell-dirtrackp)
  157.   (setq shell-dirtrackp t)
  158.   (setq comint-input-sentinel 'shell-directory-tracker)
  159.   (run-hooks 'gdb-mode-hook))
  160.  
  161. (defvar current-gdb-buffer nil)
  162.  
  163. (defvar gdb-command-name "gdb"
  164.   "Pathname for executing gdb.")
  165.  
  166. (defun gdb (path)
  167.   "Run gdb on program FILE in buffer *gdb-FILE*.
  168. The directory containing FILE becomes the initial working directory
  169. and source-file directory for GDB.  If you wish to change this, use
  170. the GDB commands `cd DIR' and `directory'."
  171.   (interactive "fRun gdb on file: ")
  172.   (setq path (expand-file-name path))
  173.   (let ((file (file-name-nondirectory path)))
  174.     (switch-to-buffer (concat "*gdb-" file "*"))
  175.     (setq default-directory (file-name-directory path))
  176.     (or (bolp) (newline))
  177.     (insert "Current directory is " default-directory "\n")
  178.     (make-comint (concat "gdb-" file) (substitute-in-file-name gdb-command-name)
  179.          nil "-fullname"
  180.          "-cd" default-directory file)
  181.     (gdb-mode)
  182.     (set-process-filter (get-buffer-process (current-buffer)) 'gdb-filter)
  183.     (set-process-sentinel (get-buffer-process (current-buffer)) 'gdb-sentinel)
  184.     (gdb-set-buffer)))
  185.  
  186. (defun gdb-set-buffer ()
  187.   (cond ((eq major-mode 'gdb-mode)
  188.     (setq current-gdb-buffer (current-buffer)))))
  189.  
  190. ;; This function is responsible for inserting output from GDB
  191. ;; into the buffer.
  192. ;; Aside from inserting the text, it notices and deletes
  193. ;; each filename-and-line-number;
  194. ;; that GDB prints to identify the selected frame.
  195. ;; It records the filename and line number, and maybe displays that file.
  196. (defun gdb-filter (proc string)
  197.   (let ((inhibit-quit t))
  198.     (if gdb-filter-accumulator
  199.     (gdb-filter-accumulate-marker proc
  200.                       (concat gdb-filter-accumulator string))
  201.     (gdb-filter-scan-input proc string))))
  202.  
  203. (defun gdb-filter-accumulate-marker (proc string)
  204.   (setq gdb-filter-accumulator nil)
  205.   (if (> (length string) 1)
  206.       (if (= (aref string 1) ?\032)
  207.       (let ((end (string-match "\n" string)))
  208.         (if end
  209.         (progn
  210.           (let* ((first-colon (string-match ":" string 2))
  211.              (second-colon
  212.               (string-match ":" string (1+ first-colon))))
  213.             (setq gdb-last-frame
  214.               (cons (substring string 2 first-colon)
  215.                 (string-to-int
  216.                  (substring string (1+ first-colon)
  217.                         second-colon)))))
  218.           (setq gdb-last-frame-displayed-p nil)
  219.           (gdb-filter-scan-input proc
  220.                      (substring string (1+ end))))
  221.           (setq gdb-filter-accumulator string)))
  222.     (gdb-filter-insert proc "\032")
  223.     (gdb-filter-scan-input proc (substring string 1)))
  224.     (setq gdb-filter-accumulator string)))
  225.  
  226. (defun gdb-filter-scan-input (proc string)
  227.   (if (equal string "")
  228.       (setq gdb-filter-accumulator nil)
  229.       (let ((start (string-match "\032" string)))
  230.     (if start
  231.         (progn (gdb-filter-insert proc (substring string 0 start))
  232.            (gdb-filter-accumulate-marker proc
  233.                          (substring string start)))
  234.         (gdb-filter-insert proc string)))))
  235.  
  236. (defun gdb-filter-insert (proc string)
  237.   (let ((moving (= (point) (process-mark proc)))
  238.     (output-after-point (< (point) (process-mark proc)))
  239.     (old-buffer (current-buffer))
  240.     start)
  241.     (set-buffer (process-buffer proc))
  242.     (unwind-protect
  243.     (save-excursion
  244.       ;; Insert the text, moving the process-marker.
  245.       (goto-char (process-mark proc))
  246.       (setq start (point))
  247.       (insert-before-markers string)
  248.       (set-marker (process-mark proc) (point))
  249.       (gdb-maybe-delete-prompt)
  250.       ;; Check for a filename-and-line number.
  251.       (gdb-display-frame
  252.        ;; Don't display the specified file
  253.        ;; unless (1) point is at or after the position where output appears
  254.        ;; and (2) this buffer is on the screen.
  255.        (or output-after-point
  256.            (not (get-buffer-window (current-buffer))))
  257.        ;; Display a file only when a new filename-and-line-number appears.
  258.        t))
  259.       (set-buffer old-buffer))
  260.     (if moving (goto-char (process-mark proc)))))
  261.  
  262. (defun gdb-sentinel (proc msg)
  263.   (cond ((null (buffer-name (process-buffer proc)))
  264.      ;; buffer killed
  265.      ;; Stop displaying an arrow in a source file.
  266.      (setq overlay-arrow-position nil)
  267.      (set-process-buffer proc nil))
  268.     ((memq (process-status proc) '(signal exit))
  269.      ;; Stop displaying an arrow in a source file.
  270.      (setq overlay-arrow-position nil)
  271.      ;; Fix the mode line.
  272.      (setq mode-line-process
  273.            (concat ": "
  274.                (symbol-name (process-status proc))))
  275.      (let* ((obuf (current-buffer)))
  276.        ;; save-excursion isn't the right thing if
  277.        ;;  process-buffer is current-buffer
  278.        (unwind-protect
  279.            (progn
  280.          ;; Write something in *compilation* and hack its mode line,
  281.          (set-buffer (process-buffer proc))
  282.          ;; Force mode line redisplay soon
  283.          (set-buffer-modified-p (buffer-modified-p))
  284.          (if (eobp)
  285.              (insert ?\n mode-name " " msg)
  286.            (save-excursion
  287.              (goto-char (point-max))
  288.              (insert ?\n mode-name " " msg)))
  289.          ;; If buffer and mode line will show that the process
  290.          ;; is dead, we can delete it now.  Otherwise it
  291.          ;; will stay around until M-x list-processes.
  292.          (delete-process proc))
  293.          ;; Restore old buffer, but don't restore old point
  294.          ;; if obuf is the gdb buffer.
  295.          (set-buffer obuf))))))
  296.  
  297.  
  298. (defun gdb-refresh (&optional arg)
  299.   "Fix up a possibly garbled display, and redraw the arrow."
  300.   (interactive "P")
  301.   (recenter arg)
  302.   (gdb-display-frame))
  303.  
  304. (defun gdb-display-frame (&optional nodisplay noauto)
  305.   "Find, obey and delete the last filename-and-line marker from GDB.
  306. The marker looks like \\032\\032FILENAME:LINE:CHARPOS\\n.
  307. Obeying it means displaying in another window the specified file and line."
  308.   (interactive)
  309.   (gdb-set-buffer)
  310.   (and gdb-last-frame (not nodisplay)
  311.        (or (not gdb-last-frame-displayed-p) (not noauto))
  312.        (progn (gdb-display-line (car gdb-last-frame) (cdr gdb-last-frame))
  313.           (setq gdb-last-frame-displayed-p t))))
  314.  
  315. ;; Make sure the file named TRUE-FILE is in a buffer that appears on the screen
  316. ;; and that its line LINE is visible.
  317. ;; Put the overlay-arrow on the line LINE in that buffer.
  318.  
  319. (defun gdb-display-line (true-file line)
  320.   (let* ((pre-display-buffer-function nil) ; screw it, put it all in one screen
  321.      (pop-up-windows t)
  322.      (buffer (find-file-noselect true-file))
  323.      (window (display-buffer buffer t))
  324.      (pos))
  325.     (save-excursion
  326.       (set-buffer buffer)
  327.       (save-restriction
  328.     (widen)
  329.     (goto-line line)
  330.     (setq pos (point))
  331.     (setq overlay-arrow-string "=>")
  332.     (or overlay-arrow-position
  333.         (setq overlay-arrow-position (make-marker)))
  334.     (set-marker overlay-arrow-position (point) (current-buffer)))
  335.       (cond ((or (< pos (point-min)) (> pos (point-max)))
  336.          (widen)
  337.          (goto-char pos))))
  338.     (set-window-point window overlay-arrow-position)))
  339.  
  340. (defun gdb-call (command)
  341.   "Invoke gdb COMMAND displaying source in other window."
  342.   (interactive)
  343.   (goto-char (point-max))
  344.   ;; Record info on the last prompt in the buffer and its position.
  345.   ;; This is used in  gdb-maybe-delete-prompt
  346.   ;; to prevent multiple prompts from accumulating.
  347.   (save-excursion
  348.     (goto-char (process-mark (get-buffer-process current-gdb-buffer)))
  349.     (let ((pt (point)))
  350.       (beginning-of-line)
  351.       (setq gdb-delete-prompt-marker
  352.         (if (= (point) pt)
  353.         nil
  354.           (list (point-marker) (- pt (point))
  355.             (buffer-substring (point) pt))))))
  356.   (gdb-set-buffer)
  357.   (send-string (get-buffer-process current-gdb-buffer)
  358.            (concat command "\n")))
  359.  
  360. (defun gdb-maybe-delete-prompt ()
  361.   (if gdb-delete-prompt-marker
  362.       ;; Get the string that we used as the prompt before.
  363.       (let ((prompt (nth 2 gdb-delete-prompt-marker))
  364.         (length (nth 1 gdb-delete-prompt-marker)))
  365.     ;; Position after it.
  366.     (goto-char (+ (car gdb-delete-prompt-marker) length))
  367.     ;; Delete any duplicates of it which follow right after.
  368.     (while (and (<= (+ (point) length) (point-max))
  369.             (string= prompt
  370.                  (buffer-substring (point) (+ (point) length))))
  371.       (delete-region (point) (+ (point) length)))
  372.     ;; If that didn't take us to where output is arriving,
  373.     ;; we have encountered something other than a prompt,
  374.     ;; so stop trying to delete any more prompts.
  375.     (if (not (= (point)
  376.             (process-mark (get-buffer-process current-gdb-buffer))))
  377.         (progn
  378.           (set-marker (car gdb-delete-prompt-marker) nil)
  379.           (setq gdb-delete-prompt-marker nil))))))
  380.  
  381. (defun gdb-break (temp)
  382.   "Set GDB breakpoint at this source line.  With ARG set temporary breakpoint."
  383.   (interactive "P")
  384.   (let ((file-name (file-name-nondirectory buffer-file-name))
  385.     (line (save-restriction
  386.         (widen)
  387.         (beginning-of-line)
  388.         (1+ (count-lines 1 (point))))))
  389.     (send-string (get-buffer-process current-gdb-buffer)
  390.          (concat (if temp "tbreak " "break ")
  391.              file-name ":" line "\n"))))
  392.  
  393. (defun gdb-read-address()
  394.   "Return a string containing the core-address found in the buffer at point."
  395.   (save-excursion
  396.    (let ((pt (point)) found begin)
  397.      (setq found (if (search-backward "0x" (- pt 7) t)(point)))
  398.      (cond (found (forward-char 2)
  399.           (buffer-substring found
  400.                     (progn (re-search-forward "[^0-9a-f]")
  401.                        (forward-char -1)
  402.                        (point))))
  403.        (t (setq begin (progn (re-search-backward "[^0-9]") (forward-char 1)
  404.                  (point)))
  405.           (forward-char 1)
  406.           (re-search-forward "[^0-9]")
  407.           (forward-char -1)
  408.           (buffer-substring begin (point)))))))
  409.  
  410.  
  411. (defvar gdb-commands nil
  412.   "List of strings or functions used by send-gdb-command.
  413. It is for customization by you.")
  414.  
  415. (defun send-gdb-command (arg)
  416.  
  417.   "This command reads the number where the cursor is positioned.  It
  418.  then inserts this ADDR at the end of the gdb buffer.  A numeric arg
  419.  selects the ARG'th member COMMAND of the list gdb-print-command.  If
  420.  COMMAND is a string, (format COMMAND ADDR) is inserted, otherwise
  421.  (funcall COMMAND ADDR) is inserted.  eg. \"p (rtx)%s->fld[0].rtint\"
  422.  is a possible string to be a member of gdb-commands.  "
  423.  
  424.  
  425.   (interactive "P")
  426.   (let (comm addr)
  427.     (if arg (setq comm (nth arg gdb-commands)))
  428.     (setq addr (gdb-read-address))
  429.     (if (eq (current-buffer) current-gdb-buffer)
  430.     (set-mark (point)))
  431.     (cond (comm
  432.        (setq comm
  433.          (if (stringp comm) (format comm addr) (funcall comm addr))))
  434.       (t (setq comm addr)))
  435.     (switch-to-buffer current-gdb-buffer)
  436.     (goto-char (point-max))
  437.     (insert-string comm)))
  438.  
  439. (defun gdb-control-c-subjob ()
  440.   "Send a Control-C to the subprocess."
  441.   (interactive)
  442.   (process-send-string (get-buffer-process (current-buffer))
  443.                "\C-c"))
  444.  
  445. (provide 'gdb)
  446.