|
From: | Zoltan Kemenczy |
Subject: | Corrected: Gud.el Update - Improved Java Debugging Support for Emacs version 21 |
Date: | Mon, 19 Nov 2001 21:15:33 -0500 |
This is a
corrected patch for Emacs 21 lisp/gud.el that:
- incorporates feedback from RMS, - eliminates long lines in patch output that were wrapped by the mailer. ------------------------------------------------------------------ Major Improvements - Search for source files using jdb classpath and class information. Fast startup since there is no need to scan all source files up front. There is also no need to create and maintain lists of source directories to scan. - Supports the standard breakpoint (gud-break, gud-clear) set/clear operations from java source files under the classpath, stack traversal (gud-up, gud-down), and run until current stack finish (gud-finish). - Supports new jdb (Java 1.2 and later) in addition to oldjdb (Java 1.1 jdb). - The previous method of searching for source files has been preserved in case someone still wants/needs to use it. Set gud-jdb-use-classpath to nil. Added Customization Variables - gud-jdb-command-name. What command line to use to invoke jdb. - gud-jdb-use-classpath. Allows selection of java source file searching method: set to t for new method, nil to scan gud-jdb-directories for java sources (previous method). - gud-jdb-directories. List of directories to scan and search for java classes using the original gud-jdb method (if gud-jdb-use-classpath is nil). Minor Improvements - Do not allow debugger output history variable to grow without bounds. ---------------------------------------------------------------------- *** /emacs-21.1/lisp/gud.el Tue Sep 11 14:28:22 2001 --- gud.el Wed Nov 14 10:08:30 2001 *************** *** 104,115 **** :enable (memq gud-minor-mode '(gdb sdb xdb))) ([break] "Set Breakpoint" . gud-break) ([up] menu-item "Up Stack" gud-up ! :enable (memq gud-minor-mode '(gdb dbx xdb))) ([down] menu-item "Down Stack" gud-down ! :enable (memq gud-minor-mode '(gdb dbx xdb))) ([print] "Print _expression_" . gud-print) ([finish] menu-item "Finish Function" gud-finish ! :enable (memq gud-minor-mode '(gdb xdb))) ([stepi] "Step Instruction" . gud-stepi) ([step] "Step Line" . gud-step) ([next] "Next Line" . gud-next) --- 104,115 ---- :enable (memq gud-minor-mode '(gdb sdb xdb))) ([break] "Set Breakpoint" . gud-break) ([up] menu-item "Up Stack" gud-up ! :enable (memq gud-minor-mode '(gdb dbx xdb jdb))) ([down] menu-item "Down Stack" gud-down ! :enable (memq gud-minor-mode '(gdb dbx xdb jdb))) ([print] "Print _expression_" . gud-print) ([finish] menu-item "Finish Function" gud-finish ! :enable (memq gud-minor-mode '(gdb xdb jdb))) ([stepi] "Step Instruction" . gud-stepi) ([step] "Step Line" . gud-step) ([next] "Next Line" . gud-next) *************** *** 1425,1432 **** --- 1425,1434 ---- ;; JDB support. ;; ;; AUTHOR: Derek Davies <address@hidden> + ;; Zoltan Kemenczy <address@hidden;address@hidden> ;; ;; CREATED: Sun Feb 22 10:46:38 1998 Derek Davies. + ;; UPDATED: Nov 11, 2001 Zoltan Kemenczy ;; ;; INVOCATION NOTES: ;; *************** *** 1482,1487 **** --- 1484,1491 ---- ;; search for java classes even though it is required when invoking jdb ;; from the command line. See gud-jdb-massage-args for details. ;; + ;; Note: The following applies only if `gud-jdb-use-classpath' is nil: + ;; ;; If any of the source files in the directories listed in ;; gud-jdb-directories won't parse you'll have problems. Make sure ;; every file ending in ".java" in these directories parses without error. *************** *** 1501,1508 **** ;; ====================================================================== ;; gud jdb variables and functions ! ;; History of argument lists passed to jdb. ! (defvar gud-jdb-history nil) ;; List of Java source file directories. (defvar gud-jdb-directories (list ".") --- 1505,1558 ---- ;; ====================================================================== ;; gud jdb variables and functions ! (defcustom gud-jdb-command-name "jdb" ! "Command that executes the Java debugger." ! :type 'string ! :group 'gud) ! ! (defcustom gud-jdb-use-classpath t ! "If t, search for Java source files in classpath directories. ! The list of directories to search is the value of `gud-jdb-classpath'. ! The file pathname is obtained by converting the fully qualified ! class information output by jdb to a relative pathname and appending ! it to `gud-jdb-classpath' element by element until a match is found. ! ! This method has a significant jdb startup time reduction advantage ! since it does not require the scanning of all `gud-jdb-directories' ! and parsing all Java files for class information. ! ! Set to nil to use `gud-jdb-directories' to scan java sources for ! class information on jdb startup (original method)." ! :type 'boolean ! :group 'gud) ! ! (defvar gud-jdb-classpath nil ! "Java/jdb classpath directories list. ! If `gud-jdb-use-classpath' is t, gud-jdb derives the`gud-jdb-classpath' ! list automatically using the following three methods in sequence ! (with subsequent successful steps overriding the results of previous ! steps): ! ! 1) Read the CLASSPATH environment variable, ! 2) Read any \"-classpath\" argument used to invoke jdb, ! 3) Send a \"classpath\" command to jdb and scan jdb output for ! classpath information if jdb is invoked with an \"-attach\" (to ! an already running VM) argument (This case typically does not ! have a \"-classpath\" command line argument - that is provided ! to the VM when it is started). ! ! Note that method 3 cannot be used with oldjdb (or Java 1 jdb) since ! those debuggers do not support the classpath command. Use 1) or 2).") ! ! (defvar gud-marker-acc-max-length 4000 ! "Maximum number of debugger output characters to keep. ! This variable limits the size of `gud-marker-acc' which holds ! the most recent debugger output history while searching for ! source file information.") ! ! (defvar gud-jdb-history nil ! "History of argument lists passed to jdb.") ! ;; List of Java source file directories. (defvar gud-jdb-directories (list ".") *************** *** 1517,1536 **** file from which the class originated. This allows gud mode to keep the source code display in sync with the debugging session.") ! ;; List of the java source files for this debugging session. ! (defvar gud-jdb-source-files nil) ! ;; Association list of fully qualified class names (package + class name) and ! ;; their source files. ! (defvar gud-jdb-class-source-alist nil) ;; This is used to hold a source file during analysis. (defvar gud-jdb-analysis-buffer nil) ! ;; Return a list of java source files. PATH gives the directories in ! ;; which to search for files with extension EXTN. Normally EXTN is ! ;; given as the regular _expression_ "\\.java$" . (defun gud-jdb-build-source-files-list (path extn) (apply 'nconc (mapcar (lambda (d) (when (file-directory-p d) (directory-files d t extn nil))) --- 1567,1591 ---- file from which the class originated. This allows gud mode to keep the source code display in sync with the debugging session.") ! (defvar gud-jdb-source-files nil ! "List of the java source files for this debugging session.") ! ;; Association list of fully qualified class names (package + class name) ! ;; and their source files. ! (defvar gud-jdb-class-source-alist nil ! "Association list of fully qualified class names and source files.") ;; This is used to hold a source file during analysis. (defvar gud-jdb-analysis-buffer nil) ! (defvar gud-jdb-classpath-string nil ! "Holds temporary classpath values.") ! (defun gud-jdb-build-source-files-list (path extn) + "Return a list of java source files. + PATH gives the directories in which to search for files with + extension EXTN. Normally EXTN is given as the regular _expression_ + \"\\.java$\" ." (apply 'nconc (mapcar (lambda (d) (when (file-directory-p d) (directory-files d t extn nil))) *************** *** 1781,1789 **** massaged-args (list "-classpath") (list ! (substring ! (car args) ! (match-beginning 1) (match-end 1))) (cdr args))) massaged-args)))) --- 1836,1845 ---- massaged-args (list "-classpath") (list ! (setq gud-jdb-classpath-string ! (substring ! (car args) ! (match-beginning 1) (match-end 1)))) (cdr args))) massaged-args)))) *************** *** 1793,1798 **** --- 1849,1898 ---- (defun gud-jdb-find-source-file (p) (cdr (assoc p gud-jdb-class-source-alist))) + ;; Note: Reset to this value every time a prompt is seen + (defvar gud-jdb-lowest-stack-level 999) + + (defun gud-jdb-find-source-using-classpath (p) + "Find source file corresponding to fully qualified class p. + Convert p from jdb's output, converted to a pathname + relative to a classpath directory." + (save-match-data + (let + (;; Replace dots with slashes and append ".java" to generate file + ;; name relative to classpath + (filename + (concat + (mapconcat (lambda (x) x) + (split-string + ;; Eliminate any subclass references in the class + ;; name string. These start with a "$" + ((lambda (x) + (if (string-match "$.*" x) + (replace-match "" t t x) p)) + p) + "\\.") "/") + ".java")) + (cplist gud-jdb-classpath) + found-file) + (while (and cplist + (not (setq found-file + (file-readable-p + (concat (car cplist) "/" filename))))) + (setq cplist (cdr cplist))) + (if found-file (concat (car cplist) "/" filename))))) + + (defun gud-jdb-find-source (string) + "Alias for function used to locate source files. + Set to `gud-jdb-find-source-using-classpath' or `gud-jdb-find-source-file' + during jdb initialization depending on the value of + `gud-jdb-use-classpath'." + nil) + + (defun gud-jdb-parse-classpath-string (string) + "Parse the classpath list and convert each item to an absolute pathname." + (mapcar 'file-truename (split-string string + (concat "[ \t\n\r,\"" path-separator "]+")))) + ;; See comentary for other debugger's marker filters - there you will find ;; important notes about STRING. (defun gud-jdb-marker-filter (string) *************** *** 1803,1816 **** (concat gud-marker-acc string) string)) ! ;; We process STRING from left to right. Each time through the following ! ;; loop we process at most one marker. The start variable keeps track of ! ;; where we are in the input string through the iterations of this loop. ! (let (start file-found) ! ! ;; Process each complete marker in the input. There may be an incomplete ! ;; marker at the end of the input string. Incomplete markers are left ! ;; in the accumulator for processing the next time the function is called. (while ;; Do we see a marker? --- 1903,1923 ---- (concat gud-marker-acc string) string)) ! ;; Look for classpath information until gud-jdb-classpath-string is found ! (if (and gud-jdb-use-classpath ! (not gud-jdb-classpath-string) ! (string-match "classpath:[ \t[]+\\([^]]+\\)" gud-marker-acc)) ! (setq gud-jdb-classpath ! (gud-jdb-parse-classpath-string ! (setq gud-jdb-classpath-string ! (substring gud-marker-acc ! (match-beginning 1) (match-end 1)))))) ! ! ;; We process STRING from left to right. Each time through the ! ;; following loop we process at most one marker. After we've found a ! ;; marker, delete gud-marker-acc up to and including the match ! (let (file-found) ! ;; Process each complete marker in the input. (while ;; Do we see a marker? *************** *** 1836,1866 **** ;; ;; FIXME: Java ID's are UNICODE strings, this matches ASCII ;; ID's only. ! "\\([a-zA-Z0-9.$_]+\\)\\.[a-zA-Z0-9$_<>]+ (\\([a-zA-Z0-9$_]+\\):\\([0-9]+\\))" ! gud-marker-acc start) ! ;; Figure out the line on which to position the debugging arrow. ;; Return the info as a cons of the form: ;; ;; (<file-name> . <line-number>) . ! (if (setq ! file-found ! (gud-jdb-find-source-file ! (substring gud-marker-acc ! (match-beginning 1) ! (match-end 1)))) ! (setq gud-last-frame ! (cons ! file-found ! (string-to-int ! (substring gud-marker-acc ! (match-beginning 3) ! (match-end 3))))) ! (message "Could not find source file.")) ! ! ;; Set start after the last character of STRING that we've looked at ! ;; and loop to look for another marker. ! (setq start (match-end 0)))) ;; We don't filter any debugger output so just return what we were given. string) --- 1943,1996 ---- ;; ;; FIXME: Java ID's are UNICODE strings, this matches ASCII ;; ID's only. ! "\\(\[[0-9]+\] \\)*\\([a-zA-Z0-9.$_]+\\)\\.[a-zA-Z0-9$_<>(),]+ \ ! \\(([a-zA-Z0-9.$_]+:\\|line=\\)\\([0-9]+\\)" ! gud-marker-acc) ! ! ;; A good marker is one that: ! ;; 1) does not have a "[n] " prefix (not part of a stack backtrace) ! ;; 2) does have an "[n] " prefix and n is the lowest prefix seen ! ;; since the last prompt ;; Figure out the line on which to position the debugging arrow. ;; Return the info as a cons of the form: ;; ;; (<file-name> . <line-number>) . ! (if (if (match-beginning 1) ! (let (n) ! (setq n (string-to-int (substring ! gud-marker-acc ! (1+ (match-beginning 1)) ! (- (match-end 1) 2)))) ! (if (< n gud-jdb-lowest-stack-level) ! (progn (setq gud-jdb-lowest-stack-level n) t))) ! t) ! (if (setq file-found ! (gud-jdb-find-source ! (substring gud-marker-acc ! (match-beginning 2) ! (match-end 2)))) ! (setq gud-last-frame ! (cons file-found ! (string-to-int ! (substring gud-marker-acc ! (match-beginning 4) ! (match-end 4))))) ! (message "Could not find source file."))) ! ! ;; Set the accumulator to the remaining text. ! (setq gud-marker-acc (substring gud-marker-acc (match-end 0)))) ! ! (if (string-match comint-prompt-regexp gud-marker-acc) ! (setq gud-jdb-lowest-stack-level 999)) ! ) ! ! ;; Do not allow gud-marker-acc to grow without bound. If the source ! ;; file information is not within the last 3/4 ! ;; gud-marker-acc-max-length characters, well,... ! (if (> (length gud-marker-acc) gud-marker-acc-max-length) ! (setq gud-marker-acc ! (substring gud-marker-acc ! (- (/ (* gud-marker-acc-max-length 3) 4))))) ;; We don't filter any debugger output so just return what we were given. string) *************** *** 1869,1907 **** (and (file-readable-p f) (find-file-noselect f))) - (defvar gud-jdb-command-name "jdb" "Command that executes the Java debugger.") - ;;;###autoload (defun jdb (command-line) "Run jdb with command line COMMAND-LINE in a buffer. The buffer is named \"*gud*\" if no initial class is given or \"*gud-<initial-class-basename>*\" if there is. If the \"-classpath\" switch is given, omit all whitespace ! between it and it's value." (interactive (list (gud-query-cmdline 'jdb))) (gud-common-init command-line 'gud-jdb-massage-args 'gud-jdb-marker-filter 'gud-jdb-find-file) (set (make-local-variable 'gud-minor-mode) 'jdb) ! (gud-def gud-break "stop at %F:%l" "\C-b" "Set breakpoint at current line.") ! (gud-def gud-remove "clear %l" "\C-d" "Remove breakpoint at current line") ! (gud-def gud-step "step" "\C-s" "Step one source line with display.") ! (gud-def gud-next "next" "\C-n" "Step one line (skip functions).") ! (gud-def gud-cont "cont" "\C-r" "Continue with display.") ! (setq comint-prompt-regexp "^> \\|^.+\\[[0-9]+\\] ") (setq paragraph-start comint-prompt-regexp) (run-hooks 'jdb-mode-hook) ! ;; Create and bind the class/source association list as well as the source ! ;; file list. ! (setq ! gud-jdb-class-source-alist ! (gud-jdb-build-class-source-alist ! (setq ! gud-jdb-source-files ! (gud-jdb-build-source-files-list gud-jdb-directories "\\.java$"))))) ;; --- 1999,2073 ---- (and (file-readable-p f) (find-file-noselect f))) ;;;###autoload (defun jdb (command-line) "Run jdb with command line COMMAND-LINE in a buffer. The buffer is named \"*gud*\" if no initial class is given or \"*gud-<initial-class-basename>*\" if there is. If the \"-classpath\" switch is given, omit all whitespace ! between it and it's value. ! ! See `gud-jdb-use-classpath' and `gud-jdb-classpath' documentation for ! information on how jdb accesses source files. Alternatively (if ! `gud-jdb-use-classpath' is nil), see `gud-jdb-directories' for the ! original source file access method. ! ! For general information about commands available to control jdb from ! gud, see `gud-mode'." (interactive (list (gud-query-cmdline 'jdb))) + ;; Set gud-jdb-classpath from the CLASSPATH environment variable, + ;; if CLASSPATH is set. + (setq gud-jdb-classpath-string (getenv "CLASSPATH")) + (if gud-jdb-classpath-string + (setq gud-jdb-classpath + (gud-jdb-parse-classpath-string gud-jdb-classpath-string))) + (setq gud-jdb-classpath-string nil) ; prepare for next + (gud-common-init command-line 'gud-jdb-massage-args 'gud-jdb-marker-filter 'gud-jdb-find-file) (set (make-local-variable 'gud-minor-mode) 'jdb) ! ;; If a -classpath option was provided, set gud-jdb-classpath ! (if gud-jdb-classpath-string ! (setq gud-jdb-classpath ! (gud-jdb-parse-classpath-string gud-jdb-classpath-string))) ! (setq gud-jdb-classpath-string nil) ; prepare for next ! ! (gud-def gud-break "stop at %c:%l" "\C-b" "Set breakpoint at current line.") ! (gud-def gud-remove "clear %c:%l" "\C-d" "Remove breakpoint at current line") ! (gud-def gud-step "step" "\C-s" "Step one source line with display.") ! (gud-def gud-next "next" "\C-n" "Step one line (skip functions).") ! (gud-def gud-cont "cont" "\C-r" "Continue with display.") ! (gud-def gud-finish "step up" "\C-f" "Continue until current method returns.") ! (gud-def gud-up "up\C-Mwhere" "<" "Up one stack frame.") ! (gud-def gud-down "down\C-Mwhere" ">" "Up one stack frame.") ! (local-set-key [menu-bar debug finish] '("Finish Function" . gud-finish)) ! (local-set-key [menu-bar debug up] '("Up Stack" . gud-up)) ! (local-set-key [menu-bar debug down] '("Down Stack" . gud-down)) ! (setq comint-prompt-regexp "^> \\|^[^ ]+\\[[0-9]+\\] ") (setq paragraph-start comint-prompt-regexp) (run-hooks 'jdb-mode-hook) ! (if gud-jdb-use-classpath ! ;; Get the classpath information from the debugger (this is much ! ;; faster) and does not need the constant updating of ! ;; gud-jdb-directories ! (progn ! (if (string-match "-attach" command-line) ! (gud-call "classpath")) ! (fset 'gud-jdb-find-source ! 'gud-jdb-find-source-using-classpath)) ! ! ;; Else create and bind the class/source association list as well ! ;; as the source file list. ! (setq gud-jdb-class-source-alist ! (gud-jdb-build-class-source-alist ! (setq gud-jdb-source-files ! (gud-jdb-build-source-files-list gud-jdb-directories ! "\\.java$")))) ! (fset 'gud-jdb-find-source 'gud-jdb-find-source-file))) ;; *************** *** 1958,1966 **** "Major mode for interacting with an inferior debugger process. You start it up with one of the commands M-x gdb, M-x sdb, M-x dbx, ! M-x perldb, or M-x xdb. Each entry point finishes by executing a hook; `gdb-mode-hook', `sdb-mode-hook', `dbx-mode-hook', ! `perldb-mode-hook', or `xdb-mode-hook' respectively. After startup, the following commands are available in both the GUD interaction buffer and any source buffer GUD visits due to a breakpoint stop --- 2124,2132 ---- "Major mode for interacting with an inferior debugger process. You start it up with one of the commands M-x gdb, M-x sdb, M-x dbx, ! M-x perldb, M-x xdb, or M-x jdb. Each entry point finishes by executing a hook; `gdb-mode-hook', `sdb-mode-hook', `dbx-mode-hook', ! `perldb-mode-hook', `xdb-mode-hook', or `jdb-mode-hook' respectively. After startup, the following commands are available in both the GUD interaction buffer and any source buffer GUD visits due to a breakpoint stop *************** *** 2253,2259 **** (let ((insource (not (eq (current-buffer) gud-comint-buffer))) (frame (or gud-last-frame gud-last-last-frame)) result) ! (while (and str (string-match "\\([^%]*\\)%\\([adeflp]\\)" str)) (let ((key (string-to-char (substring str (match-beginning 2)))) subst) (cond --- 2419,2425 ---- (let ((insource (not (eq (current-buffer) gud-comint-buffer))) (frame (or gud-last-frame gud-last-last-frame)) result) ! (while (and str (string-match "\\([^%]*\\)%\\([adeflpc]\\)" str)) (let ((key (string-to-char (substring str (match-beginning 2)))) subst) (cond *************** *** 2282,2287 **** --- 2448,2457 ---- (setq subst (gud-find-c-expr))) ((eq key ?a) (setq subst (gud-read-address))) + ((eq key ?c) + (setq subst (gud-find-class (if insource + (buffer-file-name) + (car frame))))) ((eq key ?p) (setq subst (if arg (int-to-string arg))))) (setq result (concat result (match-string 1 str) subst))) *************** *** 2482,2487 **** --- 2652,2684 ---- ((= span-end ?[) t) (t nil))) (t nil)))) + + (defun gud-find-class (f) + "Find fully qualified class corresponding to file f. + This function uses the `gud-jdb-classpath' list to derive a file + pathname relative to its classpath directory The values in + `gud-jdb-classpath' are assumed to have been converted to absolute + pathname standards using file-truename." + ;; Convert f to a standard representation and remove suffix + (setq f (file-name-sans-extension (file-truename f))) + (if gud-jdb-classpath + (save-match-data + (let ((cplist gud-jdb-classpath) + class-found) + ;; Search through classpath list for an entry that is + ;; contained in f + (while (and cplist (not class-found)) + (if (string-match (car cplist) f) + (setq class-found + (mapconcat (lambda(x) x) + (split-string + (substring f (+ (match-end 0) 1)) + "/") "."))) + (setq cplist (cdr cplist))) + (if (not class-found) + (message "gud-find-class: class for file %s not found!" f)) + class-found)) + (message "gud-find-class: classpath information not available!"))) (provide 'gud) |
[Prev in Thread] | Current Thread | [Next in Thread] |