CLJDB is a hack that adds some Clojure support to emacs jdb, (command line java debug interface,) mode. It still a work in progress, and has some funky aspects due to language differences between Clojure and Java, but you can:
all from within emacs.
In your .emacs, add:
(load-file "<path>/cljdb.el")
Then evaluate that sexp, or restart emacs.
The file is located here: http://georgejahad.com/clojure/cljdb.el
Emacs jdb mode assumes that all source can be found from the class path, so you need to update your classpath to include a pointer to any directories that have source code you want to debug. The directory structure must be namespace particular so, for example, to debug clojure.core, you need an entry in your classpath that points to a directory that contains clojure/core.clj.
All clojure source code must contain a namespace command, e.g. "(ns clojure.core)". cljdb.el uses this to help determine the proper source path.
The jdb will require the vm to be started with the following command line args:
-agentlib:jdwp=transport=dt_socket,address=8021,server=y,suspend=n
The address above, 8021, is an arbitrary port number and can be changed as needed.
If using slime-clojure, adding this to your .emacs suffice:
(setq swank-clojure-extra-vm-args '("-agentlib:jdwp=transport=dt_socket,address=8021,server=y,suspend=n"))
M-x jdb -attach 8021
This will start up the jdb and connect it to the vm. It will bring up a buffer called:
*gud-8021*
This buffer displays the jdb and you can run standard jdb commands from here.
| Keystroke | Command Executed |
|---|---|
| C-x C-a C-b | From within a file buffer, sets a breakpoint on the current line. (Currently only works on clj files. The rest of the commands below should work on both .java and .clj files.) |
| C-x C-a C-n | jdb Next command |
| C-x C-a C-s | jdb Step command |
| C-x C-a C-f | jdb Step up command, which runs to the end of the current function |
| C-x C-a C-l | refresh the display |
| C-x C-a C-r | continue |
| C-x C-a < | up stackframe |
| C-x C-a > | down stackframe |
| Command | Output |
|---|---|
| locals | list of the method args and local variables |
| print <var> | prints the contents of var, which could be a method,instance or class variable, or a method argument |
| where | print the stacktrace, and refresh the file display |
| catch | trap java exceptions |
| help | print all available jdb commands |
| clear | view/unset current breakpoints |
To confirm that it is all working, run the following tests:
At the jdb command line in the*gud-8021* buffer, type:
catch all java.lang.NullPointerException
At the repl, type:
(max-key :b {:b 1} nil)
At this point a buffer should pop up containing the clojure/src/jvm/clojure/lang/Numbers.java source, with the cursor at this line:
Class xc = x.getClass();
Note that the exception was caused by trying to dereference the null pointer x. From here, you should be able to go up and down the stack with the appropriate file being displayed in the buffer.
You should also be able to use the print command from the jdb command line to print out the values of different variables. For example, in the above stack frame, this command:
print x
should return:
x = null
while one stack frame up, it should print:
x = "1"
Continue on from this exception, so that you get back to the repl prompt.
Bring up the clojure/core.clj source file in a buffer; (it needs to be the one that is pointed to by the classpath.) Set a breakpoint in the into defn on this line:
(if items
Now from the repl run this command:
(into [1 2 3] [ 4 5 6])
When the into function comes up in the buffer, hit next a few times to step through it, using "print items" occasionally to see how the value of the collection changes.
This implementation is a text-book hack. I've made no effort to actually architect this mode for Clojure. Instead, I just ran jdb mode with some clojure files, saw where it was breaking, and patched those particular problems. That means there are probably other problems lurking, which I simply haven't come across yet. If cljdb becomes popular, I'll go back and do it right. Even so, I've found it remarkably useful as is. Hopefully you will too. Here's my current bug list:
Could not find source file.
Send any comments/suggestions to George Jahad at "george-clojure at blackbirdsystems.net" or to the main clojure mailing list: http://groups.google.com/group/clojure