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 21


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

This is compatible with Emacs version 21(.1), I'm posting a separate patch
for
version 20(.7).  The changes affect jdb only.

Apply to your current emacs-21.1/lisp/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
address@hidden
----------------------------------------------------------------------------
-------------------------------------
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-21.1/lisp/gud.el Tue Sep 11 14:28:22 2001
--- gud.el Sun Nov 11 18:38:32 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,1554 ----
  ;; ======================================================================
  ;; 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)
!
! (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.")
!

  ;; 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)))
--- 1563,1585 ----
  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.")

! (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 '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))))

--- 1830,1839 ----
       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 ****
--- 1843,1884 ----
  (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)
***************
*** 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?
--- 1889,1909 ----
       (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)
--- 1929,1978 ----
    ;;
    ;; 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$")))))
  

  ;;
--- 1981,2051 ----
    (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 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)
    (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
--- 2102,2110 ----
    "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
--- 2397,2403 ----
    (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 ****
--- 2426,2435 ----
     (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 ****
--- 2630,2659 ----
     ((= 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)

------------------ patch text - end cut
here -------------------------------------------------




reply via email to

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