Include held locks in SIGQUIT thread dumps.
Handy if you have an ANR that's locking related. Quick tour:
at org.apache.harmony.dalvik.NativeTestTarget.emptyJniStaticSynchronizedMethod0(Native method)
- locked <0x60135aa8> (a java.lang.Class<org.apache.harmony.dalvik.NativeTestTarget>)
at java.lang.reflect.Method.invoke(Native method)
at C.whileTrue(Main.java:63)
at C.synchronizedOnClassString(Main.java:56)
- locked <0x60002a70> (a java.lang.Class<java.lang.String>)
at C.nestedSynchronizationWithTryCatch(Main.java:44)
- locked <0x61336b90> (a java.lang.String)
- locked <0x61336bd0> (a java.lang.String)
at C.nestedSynchronization(Main.java:35)
- locked <0x61336b18> (a java.lang.String)
- locked <0x61336b50> (a java.lang.String)
at C.synchronizedOnClassC(Main.java:30)
- locked <0x613366f8> (a java.lang.Class<C>)
at C.noLocks(Main.java:27)
at C.<clinit>(Main.java:24)
- locked <0x613366f8> (a java.lang.Class<C>)
at Main.main(Main.java:19)
A non-static synchronized native method works too:
at org.apache.harmony.dalvik.NativeTestTarget.emptyJniSynchronizedMethod0(Native method)
- locked <0x613371a8> (a org.apache.harmony.dalvik.NativeTestTarget)
...
Note that most stack traces don't look any different; the above is a
pathological example that exercises different kinds of locking. Testing
with system_server shows most threads don't hold any locks.
Future work (marked by TODO) is that explicit JNI MonitorEnter calls in
native code aren't shown.
Change-Id: I2747f5cddb4ef64b1935736f084a68fe8e4005e9
diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
index 178c2a9..9082172 100644
--- a/src/verifier/method_verifier.cc
+++ b/src/verifier/method_verifier.cc
@@ -328,12 +328,35 @@
class_loader_(class_loader),
class_def_idx_(class_def_idx),
code_item_(code_item),
+ interesting_dex_pc_(-1),
+ monitor_enter_dex_pcs_(NULL),
have_pending_hard_failure_(false),
have_pending_rewrite_failure_(false),
new_instance_count_(0),
monitor_enter_count_(0) {
}
+void MethodVerifier::FindLocksAtDexPc(Method* m, uint32_t dex_pc, std::vector<uint32_t>& monitor_enter_dex_pcs) {
+ MethodHelper mh(m);
+ MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(),
+ mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(),
+ m, m->GetAccessFlags());
+ verifier.interesting_dex_pc_ = dex_pc;
+ verifier.monitor_enter_dex_pcs_ = &monitor_enter_dex_pcs;
+ verifier.FindLocksAtDexPc();
+}
+
+void MethodVerifier::FindLocksAtDexPc() {
+ CHECK(monitor_enter_dex_pcs_ != NULL);
+ CHECK(code_item_ != NULL); // This only makes sense for methods with code.
+
+ // Strictly speaking, we ought to be able to get away with doing a subset of the full method
+ // verification. In practice, the phase we want relies on data structures set up by all the
+ // earlier passes, so we just run the full method verification and bail out early when we've
+ // got what we wanted.
+ Verify();
+}
+
bool MethodVerifier::Verify() {
// If there aren't any instructions, make sure that's expected, then exit successfully.
if (code_item_ == NULL) {
@@ -1276,6 +1299,16 @@
}
#endif
+ // If we're doing FindLocksAtDexPc, check whether we're at the dex pc we care about.
+ // We want the state _before_ the instruction, for the case where the dex pc we're
+ // interested in is itself a monitor-enter instruction (which is a likely place
+ // for a thread to be suspended).
+ if (monitor_enter_dex_pcs_ != NULL && work_insn_idx_ == interesting_dex_pc_) {
+ for (size_t i = 0; i < work_line_->GetMonitorEnterCount(); ++i) {
+ monitor_enter_dex_pcs_->push_back(work_line_->GetMonitorEnterDexPc(i));
+ }
+ }
+
/*
* Once we finish decoding the instruction, we need to figure out where
* we can go from here. There are three possible ways to transfer