http://codeseekah.com/2012/02/16/command-line-android-development-debugging/
I personally have a distaste towards IDEs, preferring lightweight solutions, with maybe less convenience. I addition to saving resources and having direct control over what happens and what doesn’t, I find that by doing things the low level way you begin to better understand how things work.
Sure, Eclipse will let you debug in 2 clicks, but what do you learn besides that you application has a bug? There should always be time to learn a thing or two about the underlying technologies. What if one day, you have to SSH into a server and debug a Java application right there and then? If you’ve never seen anything beyond Eclipse in your life you’re in for some hair pulling. So let’s learn some low level stuff.
The Dalvik VM adheres to the Java Debug Wire Protocol, although it does not support all of the protocol features. This means that any JDWP-compliant debugger should be able to attach itself to an Android application. The Eclipse debugger uses this protocol. But there’s a Java debugger which ships with the JDK and would usually be already installed.
It’s called jdb – the Java Debugger, a command line debugger, just like gdb.
Debugging Android Applications with jdb
In order to attach jdb to an Android application, which is running inside the Dalvik VM, we have to use adb, the Android debug bridge. adb bridges the gap between an application and a development/debugging environment. The Dalvik VM creates a JDWP thread for every application to allow debuggers to attach to it on certain ports/process IDs.
In order to find out the JDWP ID of a debuggable application, issue the adb jdwp command. This will return a list of currently active JDWP processes. The very last number corresponds to the JDWP the last debuggable application launched.
To attach jdb to the remote VM we have to have adb forward the remote JDWP port/process ID to a local port. This is done with the forward command, like so: adb forward tcp:7777 jdwp:JDWP_PORT. adb will open a local TCP socket that you can connect to, and will forward all data sent to the local TCP socket to the JDWP process running on the device/emulator.
Next, attach jdb like so: jdb -sourcepath /your/project/src -attach localhost:7777. The sourcepath is the path to your project’s src directory, if you’re launching jdb from you project directory simply state -sourcepath src or -sourcepath ./src. Press return and you should be attached to your application.
As an example of the full procedure, I’ve built (don’t forget to add android:debuggable="true" to your application’s AndroidManifest.xml file) and launched the WordPress Android application:
soulseekah@soulseekah:~/code/wordpress-android/2.0.5$ adb jdwp
5384
6385
7051 # <- last launched
soulseekah@soulseekah:~/code/wordpress-android/2.0.5$ adb forward tcp:7777 jdwp:7051
soulseekah@soulseekah:~/code/wordpress-android/2.0.5$ jdb -sourcepath src -attach localhost:7777
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
Initializing jdb ...
>
Now what? The application is running, there’s nothing to debug. By default jdb will not set any breakpoints. It will break on Exceptions only. In order to add breakpoints you issue stop in and stop at commands, the former will break on entering a method, while the latter will break on a specific line in a class:
> stop in org.wordpress.android.AddAccount.onCreate
Set breakpoint org.wordpress.android.AddAccount.onCreate
>
Breakpoint hit: "thread=<1> main", org.wordpress.android.AddAccount.onCreate(), line=67 bci=2
67 super.onCreate(savedInstanceState);
<1> main[1] list
63 private int blogCtr = 0;
64 public ArrayList<CharSequence> aBlogNames = new ArrayList<CharSequence>();
65 @Override
66 protected void onCreate(Bundle savedInstanceState) {
67 => super.onCreate(savedInstanceState);
68 setContentView(R.layout.add_account);
69
70 this.setTitle("WordPress - " + getResources().getText(R.string.add_account));
71
72 if (WordPress.wpDB == null)
<1> main[1]
I’ve set the breakpoint on the onCreate method of the AddAccount Activity class. The breakpoint was hit when I tapped “Add self-hosted WordPress blog”. The list command gives us the source listing. We can advance by issuing step (execute current line, step into), next (step over), step up (step until current method returns). Remember you can issue help anytime for a quick look at the available commands.
I order to automate breakpoint position settings, especially if you’d like to set a breakpoint in the onCreate method of the main Activity, you’ll have to use either .jdbrc or jdb.ini files. You can create these in the directory you’re launching jdb from. These files can contain any valid jdb commands. These will be executed upon attaching.
In order to freeze the application when launching, use the android.os.Debug.waitForDebugger() method. The application will freeze and wait for a debugger to attach itself before continuing.
ddms
The Dalvik Debug Monitor Server allows you do quite a lot of neat stuff, like take screenshots from Android device. The ddms will also forward JDWP ports to local TCP ports automatically.
logcat
logcat is another very important command line tool. It shows you debug output from all applications. Highly useful to view tracedumps before positioning breakpoints. Although jdb should break on caught Exceptions.
Conclusion
If you’re racing against time, jdb won’t help you get things done quickly. But know that it’s there when you want to get more intimate with your application and the Dalvik VM. There are lot of visual JDWP-compliant debuggers out there, which can be used if you don’t want to install a whole IDE just for quick debugging, like me. One JDWP-compliant debugger I like and use when I’m racing against time is JSwat.
If you have any debugging tips and tricks you’d like to share with the rest of us I wholeheartedly encourage you to do so, using the comments below.