Java Debugging and Profiling on your BUG

From BUG Wiki

Jump to: navigation, search

Contents

Setup

The BUG uses phoneME [1] for its Java Virtual Machine. The version that comes pre-installed on your BUG's root filesystem is optimized for running applications as fast as possible.

To do debugging or profling you must instead use a special phoneME build with JVMTI [2] support. This version is not provided in your root file system by default, so you must obtain the package and install it on your BUG yourself.

Obtaining the debug version of phoneME

There are plans to make a package repository available, but in the meantime you will need to:

  1. Set yourself to be able to build the root file system; see Kernel_and_Rootfs_Build_System.
  2. Build the debug/profile version of phoneME with bitbake phoneme-advanced-personal-debug
  3. Locate the resulting package. It should be at .../com.buglabs.build.oe/build/tmp/deploy/ipk/arm6/phoneme-advanced-personal_mr2-r1_arm6.ipk (it's okay if the numbers on the end are a bit different but don't grab the -dbg or -dev variants as they don't have what you need).
  4. Copy the package over to your BUG: scp phoneme-advanced-personal_mr2-r1_arm6.ipk root@10.10.10.10:
  5. SSH to your BUG and install the package: ipkg install phoneme-advanced-personal_mr2-r1_arm6.ipk
  6. TEMPORARY: Create a symbolic link to get profiling to work:
 ln -s /opt/phoneme-advanced-personal-debug/lib/jvm.hprof.txt /opt/phoneme-advanced-personal-debug/jvm.hprof.txt

(This is just to work around a bug in phoneME's JVMTI profiling support, that should be fixed very soon.)

At this point you should have a new directory tree at /opt/phoneme-advanced-personal-debug/ (the default one that was already on your BUG is at /opt/phoneme-advanced-personal/.

If you have any problems building or installing the package, hit up the Bug Labs team on the forums [3] or on the IRC channel (#buglabs on irc.freenode.net).

Congratulations. If you made it this far, you are ready to start debugging or profiling Java code on your BUG!

Debugging

Remote debugging is supported with the debug version of phoneME that you built and installed above. This means that you can use Eclipse, or jdb or any other JVMTI-capable debugging client on your host machine to drive your debugging session.

BUG applications all reside within the Concierge [4] OSGi framework. This is what allows them to access services, interact with the BUG modules, and so on. The framework is installed on your BUG at /usr/share/java. It is normally started by the /etc/init.d/concierge. If you look at that script you will see that it really just runs /usr/share/java/start.sh.

If you look at start.sh you will see a section near the top that begins with CVM_DIR=/opt/phoneme-advanced-personal. Below that there is an if..elif..fi statement with two sections. The first is executed if WANT_DEBUG is defined; the second if WANT_PROFILE is defined. In the WANT_DEBUG section you can see that it overrides CVM_DIR to point to the debug version of phoneME that you (hopefully) installed above. It also defines CVM_PARAMS so that on startup the virtual machine will act as a server and listen on port 5000 for a debugger client before executing any code. You can of course modify this line as you wish (but remember to make a copy of the original start.sh before you start hacking on it, just to be safe).

R1.3 Note

If you have R1.3 or earlier then your start.sh won't have the lines described above. You may still be able to build the debug package but you'll have to modify start.sh yourself. Here's what the section described above looks like in full:

CVM_DIR=/opt/phoneme-advanced-personal
CVM_PARAMS=

if [ -n "$WANT_DEBUG" ]; then
  echo 'Running debug CVM'
  CVM_DIR=/opt/phoneme-advanced-personal-debug
  # example to stop before running any code and wait for debugger client to connect to us on port 5000
  CVM_PARAMS='-Xdebug  -Xrunjdwp:transport=dt_socket,server=y,address=5000 '
elif [ -n "$WANT_PROFILE" ]; then
  CVM_DIR=/opt/phoneme-advanced-personal-debug
  # this is an example profile run; change it to '-agentlib:jvmtihprof=help' to see all the options
  CVM_PARAMS="-agentlib:jvmtihprof=heap=all,cpu=samples,file=/tmp/profile.txt -Xbootclasspath/a:$CVM_DIR/lib/java_crw_demo.jar"
fi

Then the line where the VM is run references those variables:

$CVM_DIR/bin/cvm $CVM_PARAMS ...

Starting the VM in debug mode

You can of course use this as an example and call cvm directly against any Java code that you wish. But as an example let's look at how we would debug Java code running within the Concierge framework.

To debug the Concierge framework using the default debugger parameters already provided in start.sh:

  1. Shut down the existing framework with /etc/init.d/concierge stop. If that doesn't work, get out your kill sledgehammer and dispatch the cvm process (CVM is the phoneME virtual machine).
  2. From the /usr/share/java directory execute: WANT_DEBUG=1 start.sh

You should see something like this:

root@bug:/usr/share/java# WANT_DEBUG=1 start.sh
Running debug CVM
normal startup
Listening for transport dt_socket at address: 5000

At this point the virtual machine is waiting for you to connect your debugging client.

Connecting from jdb

To connect with jdb you would execute the following on your host machine (which must already have a JRE):

$ jdb -attach 10.10.10.10:5000

Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
Initializing jdb ...
> > 
VM Started: No frames on the current call stack

main[1] step
> 
Step completed: "thread=main", ch.ethz.iks.concierge.framework.Framework.<clinit>(), line=97 bci=0

main[1] 

You can see that I executed the step command to get to the first line of the main function in the framework. Of course, you will need to be in the directory where your source is to do much from here.

Connecting from Eclipse

To connect with Eclipse, choose Run/Open Debug Dialog from the menu and create a new Remote Java Application configuration. On the Connect tab, set the host and port to that of your BUG (10.10.10.10 unless you're not using the USB ethernet connection, and port 5000 unless you changed it on the CVM_PARAMS line in start.sh). Set the project to the one that you wish to debug and the Connection Type to Standard (Socket Attach). You can probably leave the Source and Common tab settings at their defaults.

Press the Debug button to start your debugging session. You may have to choose the Debug perspective if you aren't already in it.

Here's an example where I am going to debug the Camera Modlet project (part of the BUG's OSGi innards):

And here's that debug session at a breakpoint:

Note that you must have the same version of the code that you are debugging on your host machine as is on your BUG. If you wish to modify code that's lower level than a BUG application you can obtain the source from the Bug Labs Subversion repository [5] and replace jar files in /usr/share/java with ones built on your host machine, as long as you are aware of the dependencies between those jars; that topic is beyond the scope of this article however.

Profiling

Remote profiling is not supported in the build of phoneME we are using (on ARM anyway), so you will need to do all your profiling directly on your BUG.

As discussed in the #Debugging section, there is an if..elif..fi statement in the start.sh script>; the elif part of that statement is executed if WANT_PROFILE is defined. You can see that it overrides CVM_DIR to use the debug version, and sets up CVM_PARAMS with an example set of profiling parameters.

Starting the VM in profiling mode

Again you can choose to profile whatever Java code you wish but as an example let's run Concierge through the profiler:

  1. Start it up in profiling mode with: WANT_PROFILE=1 start.sh.
  2. Exercise whatever it is that you wish to profile.
  3. Shut down Concierge by connecting to the Concierge console (by TELNETing to port 8090 on your BUG) and issuing the exit command:
 telnet 10.10.10.10 8090
Trying 10.10.10.10...
Connected to 10.10.10.10.
Escape character is '^]'.
(: exit

(: Connection closed by foreign host.

You should now have a file at /tmp/profile.txt (unless you modified the CVM_PARAMS line) with all your profiling information in it.

Here's the bottom of that file (it's very very long) from an example run on my BUG (where I didn't let it do much):

CPU SAMPLES BEGIN (total = 44305) Thu Jan  1 02:13:29 1970
rank   self  accum   count trace method
   1 29.48% 29.48%   13060 300129 java.net.PlainSocketImpl.socketAccept
   2  9.90% 39.38%    4388 300237 java.lang.Object.wait
   3  9.85% 49.23%    4364 300216 com.buglabs.bug.jni.input.InputDevice.readEvents
   4  9.69% 58.92%    4292 301354 java.lang.Object.wait
   5  9.59% 68.51%    4248 301846 java.lang.Object.wait
   6  9.55% 78.05%    4229 301827 java.io.FileInputStream.open
   7  9.48% 87.53%    4198 301841 java.net.PlainDatagramSocketImpl.receive
   8  9.46% 96.99%    4192 301861 java.lang.Object.wait
   9  1.59% 98.58%     704 301871 java.net.SocketInputStream.socketRead0
  10  0.46% 99.03%     202 301842 java.net.PlainDatagramSocketImpl.receive
  11  0.12% 99.15%      51 301856 java.lang.Object.wait
  12  0.12% 99.26%      51 301857 java.net.PlainDatagramSocketImpl.receive
  13  0.03% 99.29%      13 300250 java.lang.ClassLoader.loadBootstrapClass0
  14  0.02% 99.32%      11 300093 java.io.FileOutputStream.writeBytes
  15  0.02% 99.34%       8 300098 java.lang.ClassLoader.defineClass0
  16  0.02% 99.35%       7 300008 sun.misc.Launcher$AppClassLoader.findContainer
  17  0.02% 99.37%       7 300082 java.util.zip.Inflater.init
  18  0.01% 99.38%       5 300003 sun.io.CharToByteUTF8.convert
  19  0.01% 99.39%       5 300079 sun.io.CharToByteUTF8.convert
  20  0.01% 99.40%       5 301812 java.lang.UNIXProcess.forkAndExec
  21  0.01% 99.41%       5 301870 java.lang.Thread.sleep0