|
Technote 1154Debugging Java Code With MacsBugBy Jens Alfke |
CONTENTSMacsBug In a Nutshell | M acsBug, the low-level debugger for the Mac OS, seems unlikely to be useful for debugging a very high-level language like Java. Au contraire! The MRJ plug-in |
MacsBug In a NutshellBefore you can start using MacsBug to debug Java, you need to install MacsBug and learn the basics of how to use it. If youve already been developing Mac software, this is a non-issue since youre almost certainly already familiar with MacsBug, and you can skip to the next section. However, there are a lot of people developing or testing Java on the Macintosh who are not otherwise Mac developers and dont know an A-trap from Take the A Train. This section is for them, so it presumes a lot less Mac technical knowledge than most technotes do. Whats MacsBug?MacsBug is Apples assembly-level 680x0 and PowerPC debugger for Mac OS. It can be used to debug code running in most execution environments, from applications to drivers, and everything in between. Its often used as a bug-reporting tool by many third-party developers, as well as Mac OS system software developers. MacsBug knows nothing about source code, only assembly-language instructions, and its support for high-level data structures is primitive. But its great for examining the exact machine state. Unlike debuggers on most other platforms, MacsBug is always resident once installed, and can take over instantly when a crash occurs or when you press a hot-key, even if the machine is otherwise frozen. Download & InstallationMacsBug is available from Apples developer Web site. As befits a tweaky developer tool, it spends most of its time in one prerelease state or another, and the latest and greatest version is nearly always marked as alpha or beta. Nonetheless, its still usually best to install the latest prerelease; theyve proven to be pretty stable. (Note in particular that as of this writing, only prerelease versions are compatible with OS 8.5.) There are a lot of files in the download, but the only one you need is MacsBug itself, which youll find in the into System folder folder. Drag it into your System folder (not the Extensions folder!) and restart. You should see the message Debugger Installed appear below Welcome To Mac OS in the Mac OS splash screen as the system begins to boot. A Very Brief OverviewThe MacsBug manual is also available online, but its large and intimidating (not to mention slightly obsolete) and actually overkill for the Java-only developer. Heres a very brief introduction: MacsBug loads itself into memory very early in the boot process and hides out invisibly until its needed. (It does consume a bit of RAM.) Three different circumstances will cause MacsBug to appear:
When MacsBug appears, it completely takes over the screen and the CPU. No other application or OS software can run while MacsBug is visible. This explains why MacsBugs user interface is so completely non-Mac-like -- it cant use any of the Toolbox. MacsBug is a command-line environment like DOS or a Unix shell. It shows one fixed-size window in the middle of the screen, with garbage pixels outside the window. Theres an input line at the bottom, a few lines of machine code disassembly above it, and a large scrolling output area above that. On the left side is a register and stack display. You type commands into the input line at the bottom and press Return to run them. You cant use the mouse to select text, but most standard editing keystrokes work (arrow keys, delete and forward-delete, option- and Command- arrow, etc.) You can also press Cmd-V to copy the previous command into the input line. Some essential commandsThe most vital MacsBug commands are:
|
The 'mrj' dcmdWeve written a plug-in (called a dcmd) for MacsBug that adds a new command, The To use the
|
Heres information about the most commonly used commands. (Many of the other commands are for querying internal data structures and are only of use for debugging MRJ itself.) If you invoke the Java log ( |
mrj il
methodname
)Disassembles the bytecodes of a method. The method has to be named in the usual internal format:
/
s.
,.
For example:
il java/lang/Thread.join.()V disassembly from $659ed84 java.lang.Thread.join(Thread.java:873) [0] 2A aload_0 [1] 9 lconst_0 [2] E2 invokevirtual_quick_w Method: "java/lang/Thread.join(J)V" [5] B1 return |
The debug build of MRJ enables extra commands in the MRJ The debug build also has a limited form of deadlock-checking built into the thread scheduler: in the case of a classic two-thread deadlock it will automatically drop into MacsBug with a user break warning about a deadlock. You should immediately use Another handy feature of the debug build is that it will display a cursor shaped like a little bulldozer while it garbage-collects. This can help you tell whether long pauses in your app are actually caused by garbage collection (as can the Count class instances ( |
Warning: |
This command waits for the main MRJ thread to get control, does its work,
then writes the results to System.out
. Heres the beginning of some typical output:
mrj graphrefs $6b11f18 recursively searching for references to $6b11f18 References to: $6b11f18 instance field: $6b11aa8 java/lang/ Thread.target(Ljava/lang/Runnable;) instance field: $6b126c8 com/apple/mrj/console/Console$ConsoleArea.this$0(Lcom/ apple/mrj/console/Console;) instance field: $6b13628 com/apple/mrj/ console/Console$1.this$0(Lcom/apple/mrj/console/Console;) java thread var ref $6b11f18 at $x (tid $68ef584, ) References to: $6b11aa8 array element: $6b11ea8 [1] c thread found $6b11aa8 at $680f2fc (tid $6adda7c, ConsoleThread) c thread found $6b11aa8 at $680f34c (tid $6adda7c, ConsoleThread) c thread found $6b11aa8 at $680f370 (tid $6adda7c, ConsoleThread) c thread found $6b11aa8 at $680f3e8 (tid $6adda7c, ConsoleThread) java thread var ref $6b11aa8 at $x (tid $68ef558, ) |
Traces are separated by blank lines. Each trace starts with The first trace is for the object you requested. Subsequent traces are for objects found in previous traces. The result, if you follow from one trace to another, lets you find out exactly what chains of references are keeping an object from being garbage collected. This output is pretty hard to read and cries out for a nice tool to interpret it. For now all we can suggest is pasting the output into a good programmers editor and using the Find command to find matching hex values. |
Inspecting ObjectsYou can examine the fields of Java objects and the elements of arrays if you know the object/arrays handle. This is a 32-bit object ID. There are three ways to find a handle:
Display Object ( |
[email protected]/5D0A668 SuperName: java/lang/Object # ClassName: java/lang/ThreadGroup parent.(Ljava/lang/ThreadGroup;) = $05d540a0 name.(Ljava/lang/String;) = $05d590d0 maxPriority.(I) = 10 destroyed.(Z) = false daemon.(Z) = false vmAllowSuspension.(Z) = false nthreads.(I) = 3 threads.([Ljava/lang/Thread;) = $05d59090 ngroups.(I) = 1 groups.([Ljava/lang/ThreadGroup;) = $05d5a948 # ClassName: java/lang/Object |
The first line shows the objects class-name and handle. (The second number after the /
is not useful.) The second line shows the name of the superclass.
After that, follow blocks for the objects class and each superclass. Each block starts with the classname and then shows all variables declared in that class and their values for that object. (Static variables have [static]
at the end.)
Each variable entry shows its name, then its type in parentheses. The type follows the typical encoding scheme used by the JVM: Single letters for primitive types (I
for int, Z
for boolean, etc.) and for object types, L
followed by the classname followed by ;
.
If a variables type is an object, the value shown is the objects handle, so you can use a further mrj do
command to inspect that object.
mrj da
handle
)Displays the contents of the array with the given handle. The first line shows the type of the array elements and the length of the array; then, each element is listed on a separate line.
To list only a portion of an array, you can provide two extra parameters that specify the first item to display and the item after the last one to display; for example:
mrj da 05d5a948 1 3 java.lang.ThreadGroup[4] 1: $00000000 -> NULL 2: $00000000 -> NULL |
mrj ds
handle
)A convenient way to display the contents of a String object. (You could use do
and da
to find and dump the char[]
array in the String, but its awkward.)
This command doesnt do very well with non-ASCII characters, since that would require higher level translation services that arent available from within MacsBug.
mrj fc
name
)Locates a loaded class with the given name. You need to specify the complete name including packages, and package names need to be separated with /
instead of .
; for historical reasons, thats the way classnames are represented internally.
If the class is found, the command will return the handle of its Class
object. That object usually isnt very useful, but this command can still be handy to determine whether or not a particular class has been loaded yet.
mrj dcm
classname
)Displays all the methods of a given class (including inherited ones.) The classname has to be fully specified, with /
s, as described above for mrj fc
. The output format is similar to that of mrj do
:
Theres a section for the class and each superclass. Each section shows the classname, then each method introduced by that class. Each method is shown on a line containing:
I
for int
, V
for void, Z
for boolean
, etc.) and for object types, L
followed by the classname followed by ;
.static
and synchronized
.mrj dom
handle
)Similar to mrj dcm
, but dumps the methods of the class of the object whose handle is given.
Interpreting Stack Crawls and Thread DumpsThe
The thread headerThe first four lines display information about the thread: The threads name In quotes on the first line. This is the String parameter passed to the threads constructor. If you dont provide one, you get a default name like Thread-7. Giving your threads meaningful names is quite useful when debugging.
The stack crawl itselfAfter the thread header comes the Java stack crawl. This is mostly identical to the kind of stack crawl youre used to seeing when an exception is dumped to the console. The stack frames are listed in reverse chronological order, so the current method is at the top. Theres an additional hex number at the beginning of each line, which is the object handle of the After the object handle comes the name of the method. After that in parentheses is the name of the source file and the exact line number.
The Monitor Cache DumpMonitors are the primitives used to implement synchronization on objects. Monitors are not objects, but an object is assigned a monitor when a thread synchronizes against it. (There are also special internal monitors that are not associated with objects.) As described above, the header of a stack crawl tells whether the thread is blocked on a monitor and, if so, which one. The thing you probably want to know next is what object that monitor corresponds to. Usually (not always) you can determine this by looking in the Monitor Cache Dump section in the
The first line shows the objects class. The hex number between the The second line shows which thread owns (is currently holding) that monitor, or If one or more other threads are blocked on that monitor, they will be listed after a line reading Its worth pointing out two Java objects that often play a prominent role in synchronization problems. The Registered Monitor DumpThe last section of the thread dump shows the list of registered monitors. These are monitors that are not associated with objects but which are known to the JVM. These are used internally by things like the JIT, the class loader, and the finalizer thread. Normally you dont need to pay attention to these, but very rarely you may encounter a deadlock that involves one or them (for instance, we once had a nasty bug that could cause the JIT and the class loader to deadlock). If you encounter any problems involving these, its almost certainly a bug in MRJ, which you should report at once, including a |
Further References
|