Command Line Android Development: Debugging
Continuing into Command Line Android Development (last week I did a piece on Command Line Android Development: Basics), today I’d like to go over some of the techniques that allow for debugging applications from the command line.
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.
Further reading
- Using jdb with adb (no ADT) – provides a simple shell script that automates parsing of JDWP ports and forwards them to TCP ports, launches Dalvik VM through adb shell
- Android Development without Eclipse – great overview on how to deal with the fact
- Tips on Android Development Using Emacs – it can even organize missing imports
This artical is very good. Debugging under command will be more efficient than eclipse. I like it. Thanks.
Thanks for stopping by and commenting. I appreciate the fact that there are many people out there who prefer good old command line debugging.
I too agree Eclipse is a crutch and hides too much from us! Great article! … JSwat is another nice debugger that attaches to the Dalvik VM.
excellent! thanks for sharing,
IDEs are worthless. Thanks.
After having Eclipse take a crap on my project for the third time in as many weeks I’ve decided I’ve had enough. The first time through I was unsuccessful getting a command line compilation completed. But now, debugging has become my last hurdle. Thank you. I feel the same way about IDE’s and appreciate this.
@physicali I had the same issue with Eclipse. Crashed a few times, then failed to restore my work, then I lost hours and hours of work which I had to do over again, from the scratch. But debugging is not an issue for me now at least. Thanks for the great info.
Eclipse ADT is completely broken. Google “deprecated” ADT in favor Android Studio. For me it was easier to learn to debug on command line (rather then master new IDE). This article helped me a lot. Thank you, very much!
[…] For more explanations and tutorials, take a look to here and here. […]
does not work for me 🙁
[aik@aik MyWatchApp]$ adb jdwp
14097
^C
[aik@aik MyWatchApp]$ adb forward tcp:7777 jdwp:14097
[aik@aik MyWatchApp]$ jdb -sourcepath src -attach localhost:7777
java.io.IOException: handshake failed – connection prematurally closed
at com.sun.tools.jdi.SocketTransportService.handshake(SocketTransportService.java:136)
at com.sun.tools.jdi.SocketTransportService.attach(SocketTransportService.java:232)
at com.sun.tools.jdi.GenericAttachingConnector.attach(GenericAttachingConnector.java:116)
at com.sun.tools.jdi.SocketAttachingConnector.attach(SocketAttachingConnector.java:90)
at com.sun.tools.example.debug.tty.VMConnection.attachTarget(VMConnection.java:519)
at com.sun.tools.example.debug.tty.VMConnection.open(VMConnection.java:328)
at com.sun.tools.example.debug.tty.Env.init(Env.java:63)
at com.sun.tools.example.debug.tty.TTY.main(TTY.java:1066)
Fatal error:
Unable to attach to target VM.
Check iptables settings and netstat -lp to make sure adb is listening on port 7777. You can also try to strace jdb to see what’s the exact issue.
I had the same error and fixed it by closing DDMS/Monitor. They cannot be run simultaneously. See this issue on the Android project:
https://code.google.com/p/android/issues/detail?id=76024#c5
[…] https://codeseekah.com/2012/02/16/command-line-android-development-debugging/ […]
[…] https://codeseekah.com/2012/02/16/command-line-android-development-debugging/ […]
what if we dont have src of apk?
You could try smali and baksmali to debug APKs, but it’s gonna be tough. I’ve done this and it’s pretty much reading like x86 assembly (cause you’ll be looking at bytecode), doable but a pain.
Hi, I totally agree with the author’s point of view:
“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.” That’s really true!
My experience:
I ‘ve started to use debugging in smali (Android Studio). I haven’t known a way the JDWP works, so I ‘ve had serious problems to run it. It has happened to me, because I haven’t known the basics – how the JDWP and other stuff works! So I have started from basics.
If I started from basics I would save many hours by searching the problem in Android studio. The problem was inside me!
Thank you for the article!
Have a nice day!
Its 2017, This article has helped me connect android studio to my emulator and debug, when DDMS is not working. Thanks
[…] https://codeseekah.com/2012/02/16/command-line-android-development-debugging/ […]