Detailed walkthrough
Getting a full Thread Dump on Android
The Android operating system does a thread dump whenever it receives SIGQUIT
signal. However you need an unlocked device to send that signal. Fear not, there’s a workaround to get the exact same thread dump with a little more effort on a production device with a debuggable application.
Background
There are a few sources on the Internet on how to make a thread dump in Android, but most of them don’t work or are lacking the intrinsic locking information, here’s an example:
If you have a developer / rooted device, you can ask the Dalvik VM to dump thread stacks by sending a
SIGQUIT
to the app process you’re interested in. How to make Java Thread Dump in Android?
If you try to send a signal on a non-rooted/non-dev device you’ll get:
me@laptop$ adb shell "ps | grep twister"
USER PID PPID VSIZE RSS WCHAN PC NAME
u0_a504 10904 278 914108 23164 ffffffff 00000000 S net.twisterrob.app
me@laptop$ adb shell kill -s SIGQUIT 10904
/system/bin/sh: kill: 10904: Operation not permitted
An answer to How to stop an android application from adb without force-stop or root? suggests to use run-as
:
me@laptop$ adb shell run-as net.twisterrob.app kill -3 10904
run-as: Package 'net.twisterrob.app' is unknown
while the OP states that force-stop
and am kill
is ruled out, we could use them here:
me@laptop$ adb shell am force-stop net.twisterrob.app
me@laptop$ adb shell am kill --user all net.twisterrob.app
…the first really kills the app, and the second does nothing, in any case there’s no thread dump.
Solution
A solution for the above issues follows, based on the fact that you have control over your own Android process from the app’s code.
Step 0: Fake a UI lock-down
To have something to validate that we have the right information I suggest to lock on a known object. So later we’ll see who/where and when locked. I put the following into my main Activity’s onCreate
:
// we want to see what "this" is in a thread dump
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Step 1: Always available debugger break entry point
If you want a thread dump you probably have a deadlock in your code in which case it might not be trivial to know when and where to stop the code execution. You’ll need to start a background thread in your app so that you can stop the execution any time and be able to mess around in immediate mode too.
There’s a Pause Program action available in IntelliJ IDEA’s Debug view:
but you can’t execute any code in that mode:
Target VM is not paused by breakpoint request. Evaluation of methods is not possible in this mode
So to have a live breakable point, put the following snippet somewhere in your app:
new Thread("Debug-Breaker") {
@Override public void run() {
while (true) {
try {
Thread.sleep(1); // breakpoint here
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
Anywhere is ok that you know will surely execute before you need the thread dump. Even an button’s event handler will work. I choose to put mine in:
public class App extends Application {
@Override public void onCreate() {
Don’t forget to register it in your manifest: <application android:name=".App"
.
Compile and run your app. If you did step 0 as I did, your app will be plain black/white and do nothing at this point, because wait()
blocked the UI thread.
Step 2: Create thread dump
Go to your IDE and do Run > Attach debugger to Android process
and select your app’s process and then go to the place where you have Thread.sleep(1);
and put a breakpoint on it:
The breakpoint should be hit immediately:
Now there’s a context you can execute code in, go to Run > Evaluate Expression... and enter:
android.os.Process.sendSignal(android.os.Process.myPid(), android.os.Process.SIGNAL_QUIT)
In the Android LogCat view you’ll see the following information printed:
04-13 16:36:21.971 10904-10954/net.twisterrob.app I/Process﹕ Sending signal. PID: 10904 SIG: 3
04-13 16:36:21.971 10904-10909/net.twisterrob.app I/dalvikvm﹕ threadid=3: reacting to signal 3
04-13 16:36:22.061 10904-10909/net.twisterrob.app I/dalvikvm﹕ Wrote stack traces to '/data/anr/traces.txt'
The app may be still running at this point, but it doesn’t matter any more. Feel free to detach debugger and/or stop it on the phone.
Step 3: Acquire thread dump
Now it should be a simple pull
from your device like this, but hey it’s not that simple…
me@laptop$ adb pull /data/anr/traces.txt
failed to copy '/data/anr/traces.txt' to './traces.txt': Permission denied
for some reason I wasn’t able to copy the file, but I can read it… so it’s simple after all:
me@laptop$ adb shell "cat /data/anr/traces.txt" > traces.txt
Step 4: Read thread dump
Now if you open traces.txt on you machine and scroll from the bottom you should find your app:
----- pid 10904 at 2015-04-13 17:16:40 -----
Cmd line: net.twisterrob.app
... lots of stack traces of threads ...
----- end 10904 -----
Here’s the UI thread’s state, remember in Step 0 I synchronized on this
, see line #6 (starting with “waiting on”):
"main" prio=5 tid=1 WAIT
| group="main" sCount=1 dsCount=0 obj=0x418ccea0 self=0x417c7388
| sysTid=13183 nice=-11 sched=0/0 cgrp=apps handle=1074684244
| state=S schedstat=( 0 0 0 ) utm=7 stm=5 core=3
at java.lang.Object.wait(Native Method)
- waiting on <0x42aeb7a8> (a net.twisterrob.app.MainActivity)
at java.lang.Object.wait(Object.java:364)
at net.twisterrob.app.MainActivity.onCreate(MainActivity.java:45)
at android.app.Activity.performCreate(Activity.java:5426)
...
Summary
The above steps should give you access to thread dumps for any application you develop from your real device. The full thread dumps contain locking information like the waiting on line above which helps to diagnose deadlocks and synchronization issues. If you just want to see where your app is at the current time I suggest you use the Threads view of the SDK’s DDMS monitor
, or Thread.getAllStackTraces()
programmatically or while debugging.