|
Technote 1154Debugging Java Code With MacsBug |
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 'dcmd' for MacsBug adds a number
of commands that can help you debug everything from deadlocks to memory leaks. |
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 you've already been developing Mac software, this is a non-issue since you're 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 don't 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. What's MacsBug?MacsBug is Apple's 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. It's 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 it's 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 Apple's 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, it's still usually best to install the latest prerelease; they've proven to be pretty stable. There are a lot of files in the download, but the only one you need is MacsBug itself, which you'll 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 it's large and intimidating (not to mention slightly obsolete) and actually overkill for the Java-only developer. Here's a very brief introduction: MacsBug loads itself into memory very early in the boot process and hides out invisibly until it's 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 MacsBug's user interface is so completely non-Mac-like -- it can't 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. There's 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 can't 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' dcmdWe've written a plug-in (called a "dcmd") for MacsBug that adds a new
command, The To use the
|
Here's 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. Count class instances (
|
Here's 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 "References to:"
followed by the handle of the object it's tracing, and then lists all references
to that object, such as instance variables in other objects, static variables, and
local variables of current stack frames. 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 saving using log in Macsbug, then invoking a good programmer's editor and using the Find command to find matching hex values. |
Interpreting Stack Crawls and Thread DumpsThe
The thread headerThe first four lines display information about the thread: "The thread's name" In quotes on the first line. This is the String parameter passed to the thread's constructor. If you don't 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 you're 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. There's 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 object's 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 " It's 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 don't 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, it's almost certainly a bug in MRJ, which you should
report at once, including a |
Switching ContextsIf only a single instance of MRJ is running, the To target a particular instance of MRJ, you need to know its CFM context ID.
You can find this by using the MacsBug " Switch contexts (
|
Further References
|