bug-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Gud.el Update - Improved Java Debugging Support for Emacs version 20


From: Zoltan Kemenczy
Subject: Gud.el Update - Improved Java Debugging Support for Emacs version 20
Date: Mon, 12 Nov 2001 21:28:43 -0500

This is compatible with Emacs version 20(.7), I'm posting a separate patch for
version 21(.1).  The changes affect jdb only.
 
Apply to your current 20.7 gud.el using GNU patch (e.g. cygwin on Win):
patch -c gud.el  file_containing_patch_text_below.
 
Don't forget to (byte-compile-file gud.el)  :-)
 
Regards, Zoltan
-----------------------------------------------------------------------------------------------------------------
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.

---------------------------------- patch text - cut here -------------------------------------------------------
*** /emacs-20.7/lisp/gud.el Thu Oct 21 17:32:28 1999
--- gud.el Sun Nov 11 13:08:32 2001
***************
*** 1429,1436 ****
--- 1429,1438 ----
  ;; 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:
  ;;
***************
*** 1486,1491 ****
--- 1488,1495 ----
  ;; 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.
***************
*** 1505,1533 ****
  ;; ======================================================================
  ;; 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 ".")
!   "*A list of directories that gud jdb should search for source code.
! The file names should be absolute, or relative to the current directory.")
!
! ;; 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) (directory-files d t extn nil)) path)))
 
  ;; Move point past whitespace.
  (defun gud-jdb-skip-whitespace ()
--- 1509,1586 ----
  ;; ======================================================================
  ;; 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, does not require the setting `gud-jdb-directories', but
! searches for Java source files using `gud-jdb-classpath' (typically
! obtained from jdb - see `gud-jdb-classpath' documentation) by
! converting the class information output by jdb to a file pathname
! relative to one of the `gud-jdb-classpath' directories.
!
! 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.
! "
!   :type 'boolean
!   :group 'gud)
 
  ;; List of Java source file directories.
! (defcustom gud-jdb-directories (list ".")
!   "If `gud-jdb-use-classpath' is nil, this is the list of directories that
! gud jdb should search for source code. The file names should be absolute,
! or relative to the current directory."
!   :type '(repeat :value (".") directory)
!   :group 'gud)
!
! (defvar gud-jdb-classpath nil
!  "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
!   "(jdb only:) Bound for maximum amount of debugger output history to keep while \n\
! searching for source file information.")
!
!
! (defvar gud-jdb-history nil
! "History of argument lists passed to jdb.")
!
! (defvar gud-jdb-source-files nil
! "List of the java source files for this debugging session.")
!
! (defvar gud-jdb-class-source-alist nil
! "Association list of fully qualified class names (package + class name) and
! their source files.")
 
  ;; This is used to hold a source file during analysis.
  (defvar gud-jdb-analysis-buffer nil)
 
! (defvar gud-jdb-classpath-string nil
! "This is used to hold 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 'append (mapcar (lambda (d) (directory-files d t extn nil)) path)))
 
  ;; Move point past whitespace.
  (defun gud-jdb-skip-whitespace ()
***************
*** 1774,1782 ****
       massaged-args
       (list "-classpath")
       (list
!       (substring
!        (car args)
!        (match-beginning 1) (match-end 1)))
       (cdr args)))
     massaged-args))))
 
--- 1827,1836 ----
       massaged-args
       (list "-classpath")
       (list
!       (setq gud-jdb-classpath-string
!                           (substring
!                            (car args)
!                            (match-beginning 1) (match-end 1))))
       (cdr args)))
     massaged-args))))
 
***************
*** 1786,1791 ****
--- 1840,1881 ----
  (defun gud-jdb-find-source-file (p)
    (cdr (assoc p gud-jdb-class-source-alist)))
 
+ (defvar gud-jdb-lowest-stack-level 999) ; Note: Reset to this value every time a prompt is seen
+
+ (defun gud-jdb-find-source-using-classpath (p)
+ "Finds source file by converting a a fully qualified class
+ specification p (obtained from jdb's output) 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)
+   "Internal gud-jdb function, get aliased to
+ `gud-jdb-find-source-using-classpath' if `gud-jdb-use-classpath' is t, otherwise it aliases to `gud-jdb-find-source-file'"
+ 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)
***************
*** 1796,1809 ****
       (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?
--- 1886,1906 ----
       (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?
***************
*** 1829,1859 ****
    ;;
    ;; 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)
--- 1926,1975 ----
    ;;
    ;; 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)
***************
*** 1862,1875 ****
    (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 (read-from-minibuffer "Run jdb (like this): "
            (if (consp gud-jdb-history)
--- 1978,1997 ----
    (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 (read-from-minibuffer "Run jdb (like this): "
            (if (consp gud-jdb-history)
***************
*** 1878,1904 ****
            nil nil
            '(gud-jdb-history . 1))))
 
    (gud-common-init command-line 'gud-jdb-massage-args
      'gud-jdb-marker-filter 'gud-jdb-find-file)
 
!   (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$")))))
 
 
  ;;
--- 2000,2052 ----
            nil nil
            '(gud-jdb-history . 1))))
 
+   ;; Set gud-jdb-classpath from the CLASSPATH environment variable, if it 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)
 
!   ;; 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)))
 
 
  ;;
***************
*** 1955,1963 ****
    "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
--- 2103,2111 ----
    "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
***************
*** 2271,2277 ****
    (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
***************
*** 2299,2304 ****
--- 2447,2456 ----
     (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
***************
*** 2515,2520 ****
--- 2667,2696 ----
     ((= span-end ?[) t)
     (t nil)))
       (t nil))))
+
+ (defun gud-find-class (f)
+   "Find class corresponding to file f using the gud-jdb-classpath list
+ The values in this list 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)
 
---------------------- cut end here ---------------------------------------------------------------------

reply via email to

[Prev in Thread] Current Thread [Next in Thread]