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/debugger.cc b/src/debugger.cc
index 147a9b6..9dcdda5 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -1489,7 +1489,7 @@
struct CountStackDepthVisitor : public StackVisitor {
CountStackDepthVisitor(const ManagedStack* stack,
const std::vector<TraceStackFrame>* trace_stack)
- : StackVisitor(stack, trace_stack), depth(0) {}
+ : StackVisitor(stack, trace_stack, NULL), depth(0) {}
bool VisitFrame() {
if (!GetMethod()->IsRuntimeMethod()) {
@@ -1499,6 +1499,7 @@
}
size_t depth;
};
+
CountStackDepthVisitor visitor(thread->GetManagedStack(), thread->GetTraceStack());
visitor.WalkStack();
return visitor.depth;
@@ -1515,7 +1516,7 @@
public:
GetFrameVisitor(const ManagedStack* stack, const std::vector<TraceStackFrame>* trace_stack,
size_t start_frame, size_t frame_count, JDWP::ExpandBuf* buf)
- : StackVisitor(stack, trace_stack), depth_(0),
+ : StackVisitor(stack, trace_stack, NULL), depth_(0),
start_frame_(start_frame), frame_count_(frame_count), buf_(buf) {
expandBufAdd4BE(buf_, frame_count_);
}
@@ -1621,7 +1622,7 @@
struct FrameIdVisitor : public StackVisitor {
FrameIdVisitor(const ManagedStack* stack, const std::vector<TraceStackFrame>* trace_stack,
Method** m)
- : StackVisitor(stack, trace_stack), quick_frame_to_find(m) , frame_id(0) {}
+ : StackVisitor(stack, trace_stack, NULL), quick_frame_to_find(m) , frame_id(0) {}
virtual bool VisitFrame() {
if (quick_frame_to_find != GetCurrentQuickFrame()) {
@@ -1779,11 +1780,11 @@
void Dbg::SetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag,
uint64_t value, size_t width) {
struct SetLocalVisitor : public StackVisitor {
- SetLocalVisitor(const ManagedStack* stack, const std::vector<TraceStackFrame>* trace_stack,
+ SetLocalVisitor(const ManagedStack* stack, const std::vector<TraceStackFrame>* trace_stack, Context* context,
JDWP::FrameId frame_id, int slot, JDWP::JdwpTag tag, uint64_t value,
size_t width)
- : StackVisitor(stack, trace_stack), frame_id_(frame_id), slot_(slot), tag_(tag),
- value_(value), width_(width) {}
+ : StackVisitor(stack, trace_stack, context),
+ frame_id_(frame_id), slot_(slot), tag_(tag), value_(value), width_(width) {}
bool VisitFrame() {
if (GetFrameId() != frame_id_) {
@@ -1841,8 +1842,9 @@
const size_t width_;
};
Thread* thread = DecodeThread(threadId);
- SetLocalVisitor visitor(thread->GetManagedStack(), thread->GetTraceStack(), frameId, slot, tag,
- value, width);
+ UniquePtr<Context> context(Context::Create());
+ SetLocalVisitor visitor(thread->GetManagedStack(), thread->GetTraceStack(), context.get(),
+ frameId, slot, tag, value, width);
visitor.WalkStack();
}
@@ -2060,7 +2062,7 @@
struct SingleStepStackVisitor : public StackVisitor {
SingleStepStackVisitor(const ManagedStack* stack,
const std::vector<TraceStackFrame>* trace_stack)
- : StackVisitor(stack, trace_stack) {
+ : StackVisitor(stack, trace_stack, NULL) {
MutexLock mu(gBreakpointsLock); // Keep GCC happy.
gSingleStepControl.method = NULL;
gSingleStepControl.stack_depth = 0;
@@ -2955,7 +2957,7 @@
struct AllocRecordStackVisitor : public StackVisitor {
AllocRecordStackVisitor(const ManagedStack* stack,
const std::vector<TraceStackFrame>* trace_stack, AllocRecord* record)
- : StackVisitor(stack, trace_stack), record(record), depth(0) {}
+ : StackVisitor(stack, trace_stack, NULL), record(record), depth(0) {}
bool VisitFrame() {
if (depth >= kMaxAllocRecordStackDepth) {
diff --git a/src/monitor.cc b/src/monitor.cc
index de08b88..e5e867a 100644
--- a/src/monitor.cc
+++ b/src/monitor.cc
@@ -24,7 +24,10 @@
#include <time.h>
#include <unistd.h>
+#include <vector>
+
#include "class_linker.h"
+#include "dex_instruction.h"
#include "mutex.h"
#include "object.h"
#include "object_utils.h"
@@ -33,6 +36,7 @@
#include "stl_util.h"
#include "thread.h"
#include "thread_list.h"
+#include "verifier/method_verifier.h"
#include "well_known_classes.h"
namespace art {
@@ -867,11 +871,10 @@
// We're not waiting on anything.
return;
}
- os << "<" << object << ">";
// - waiting on <0x613f83d8> (a java.lang.ThreadLock) held by thread 5
// - waiting on <0x6008c468> (a java.lang.Class<java.lang.ref.ReferenceQueue>)
- os << " (a " << PrettyTypeOf(object) << ")";
+ os << "<" << object << "> (a " << PrettyTypeOf(object) << ")";
if (lock_owner != ThreadList::kInvalidId) {
os << " held by thread " << lock_owner;
@@ -880,6 +883,69 @@
os << "\n";
}
+static void DumpLockedObject(std::ostream& os, Object* o) {
+ os << " - locked <" << o << "> (a " << PrettyTypeOf(o) << ")\n";
+}
+
+void Monitor::DescribeLocks(std::ostream& os, StackVisitor* stack_visitor) {
+ Method* m = stack_visitor->GetMethod();
+ CHECK(m != NULL);
+
+ // Native methods are an easy special case.
+ // TODO: use the JNI implementation's table of explicit MonitorEnter calls and dump those too.
+ if (m->IsNative()) {
+ if (m->IsSynchronized()) {
+ Object* jni_this = stack_visitor->GetCurrentSirt()->GetReference(0);
+ DumpLockedObject(os, jni_this);
+ }
+ return;
+ }
+
+ // <clinit> is another special case. The runtime holds the class lock while calling <clinit>.
+ MethodHelper mh(m);
+ if (mh.IsClassInitializer()) {
+ DumpLockedObject(os, m->GetDeclaringClass());
+ // Fall through because there might be synchronization in the user code too.
+ }
+
+ // Is there any reason to believe there's any synchronization in this method?
+ const DexFile::CodeItem* code_item = mh.GetCodeItem();
+ CHECK(code_item != NULL);
+ if (code_item->tries_size_ == 0) {
+ return; // No "tries" implies no synchronization, so no held locks to report.
+ }
+
+ // Ask the verifier for the dex pcs of all the monitor-enter instructions corresponding to
+ // the locks held in this stack frame.
+ std::vector<uint32_t> monitor_enter_dex_pcs;
+ verifier::MethodVerifier::FindLocksAtDexPc(m, stack_visitor->GetDexPc(), monitor_enter_dex_pcs);
+ if (monitor_enter_dex_pcs.empty()) {
+ return;
+ }
+
+ // Verification is an iterative process, so it can visit the same monitor-enter instruction
+ // repeatedly with increasingly accurate type information. Our callers don't want to see
+ // duplicates.
+ STLSortAndRemoveDuplicates(&monitor_enter_dex_pcs);
+
+ for (size_t i = 0; i < monitor_enter_dex_pcs.size(); ++i) {
+ // The verifier works in terms of the dex pcs of the monitor-enter instructions.
+ // We want the registers used by those instructions (so we can read the values out of them).
+ uint32_t dex_pc = monitor_enter_dex_pcs[i];
+ uint16_t monitor_enter_instruction = code_item->insns_[dex_pc];
+
+ // Quick sanity check.
+ if ((monitor_enter_instruction & 0xff) != Instruction::MONITOR_ENTER) {
+ LOG(FATAL) << "expected monitor-enter @" << dex_pc << "; was "
+ << reinterpret_cast<void*>(monitor_enter_instruction);
+ }
+
+ uint16_t monitor_register = ((monitor_enter_instruction >> 8) & 0xff);
+ Object* o = reinterpret_cast<Object*>(stack_visitor->GetVReg(m, monitor_register));
+ DumpLockedObject(os, o);
+ }
+}
+
void Monitor::TranslateLocation(const Method* method, uint32_t dex_pc,
const char*& source_file, uint32_t& line_number) const {
// If method is null, location is unknown
diff --git a/src/monitor.h b/src/monitor.h
index 300e5a5..d72ff73 100644
--- a/src/monitor.h
+++ b/src/monitor.h
@@ -58,6 +58,7 @@
class Method;
class Object;
class Thread;
+class StackVisitor;
class Monitor {
public:
@@ -76,6 +77,7 @@
static void Wait(Thread* self, Object* obj, int64_t ms, int32_t ns, bool interruptShouldThrow);
static void DescribeWait(std::ostream& os, const Thread* thread);
+ static void DescribeLocks(std::ostream& os, StackVisitor* stack_visitor);
Object* GetObject();
diff --git a/src/native/dalvik_system_VMStack.cc b/src/native/dalvik_system_VMStack.cc
index 933a5d5..12fa8db 100644
--- a/src/native/dalvik_system_VMStack.cc
+++ b/src/native/dalvik_system_VMStack.cc
@@ -56,8 +56,9 @@
ClosestUserClassLoaderVisitor(const ManagedStack* stack,
const std::vector<TraceStackFrame>* trace_stack,
Object* bootstrap, Object* system)
- : StackVisitor(stack, trace_stack), bootstrap(bootstrap), system(system),
- class_loader(NULL) {}
+ : StackVisitor(stack, trace_stack, NULL),
+ bootstrap(bootstrap), system(system), class_loader(NULL) {}
+
bool VisitFrame() {
DCHECK(class_loader == NULL);
Class* c = GetMethod()->GetDeclaringClass();
@@ -68,6 +69,7 @@
}
return true;
}
+
Object* bootstrap;
Object* system;
Object* class_loader;
diff --git a/src/nth_caller_visitor.h b/src/nth_caller_visitor.h
index db4d28c..0f29ae7 100644
--- a/src/nth_caller_visitor.h
+++ b/src/nth_caller_visitor.h
@@ -24,8 +24,9 @@
// Walks up the stack 'n' callers, when used with Thread::WalkStack.
struct NthCallerVisitor : public StackVisitor {
- NthCallerVisitor(const ManagedStack* stack, const std::vector<TraceStackFrame>* trace_stack,
- size_t n) : StackVisitor(stack, trace_stack), n(n), count(0), caller(NULL) {}
+ NthCallerVisitor(const ManagedStack* stack, const std::vector<TraceStackFrame>* trace_stack, size_t n)
+ : StackVisitor(stack, trace_stack, NULL), n(n), count(0), caller(NULL) {}
+
bool VisitFrame() {
DCHECK(caller == NULL);
if (count++ == n) {
@@ -34,6 +35,7 @@
}
return true;
}
+
size_t n;
size_t count;
Method* caller;
diff --git a/src/stack.cc b/src/stack.cc
index e7c632c..c419530 100644
--- a/src/stack.cc
+++ b/src/stack.cc
@@ -27,7 +27,7 @@
class StackGetter {
public:
StackGetter(const ScopedJniThreadState& ts, Thread* thread)
- : ts_(ts), thread_(thread), trace_(NULL) {
+ : ts_(ts), thread_(thread), trace_(NULL) {
}
static void Callback(void* arg) {
@@ -106,6 +106,7 @@
}
uint32_t StackVisitor::GetVReg(Method* m, int vreg) const {
+ DCHECK(context_ != NULL); // You can't reliably read registers without a context.
DCHECK(m == GetMethod());
uint32_t core_spills = m->GetCoreSpillMask();
const VmapTable vmap_table(m->GetVmapTableRaw());
@@ -135,6 +136,7 @@
}
void StackVisitor::SetVReg(Method* m, int vreg, uint32_t new_value) {
+ DCHECK(context_ != NULL); // You can't reliably write registers without a context.
DCHECK(m == GetMethod());
const VmapTable vmap_table(m->GetVmapTableRaw());
uint32_t vmap_offset;
@@ -174,12 +176,13 @@
struct NumFramesVisitor : public StackVisitor {
explicit NumFramesVisitor(const ManagedStack* stack,
const std::vector<TraceStackFrame>* trace_stack)
- : StackVisitor(stack, trace_stack), frames(0) {}
+ : StackVisitor(stack, trace_stack, NULL), frames(0) {}
virtual bool VisitFrame() {
frames++;
return true;
}
+
size_t frames;
};
diff --git a/src/stack.h b/src/stack.h
index ff0bcd0..254451d 100644
--- a/src/stack.h
+++ b/src/stack.h
@@ -31,6 +31,7 @@
class Method;
class Object;
class ShadowFrame;
+class StackIndirectReferenceTable;
class ScopedJniThreadState;
class Thread;
@@ -215,7 +216,7 @@
class StackVisitor {
protected:
StackVisitor(const ManagedStack* stack, const std::vector<TraceStackFrame>* trace_stack,
- Context* context = NULL)
+ Context* context)
: stack_start_(stack), trace_stack_(trace_stack), cur_shadow_frame_(NULL),
cur_quick_frame_(NULL), cur_quick_frame_pc_(0), num_frames_(0), cur_depth_(0),
context_(context) {}
@@ -256,12 +257,12 @@
uint32_t GetDexPc() const;
- // Gets the height of the stack in the managed stack frames, including transitions.
+ // Returns the height of the stack in the managed stack frames, including transitions.
size_t GetFrameHeight() {
return GetNumFrames() - cur_depth_;
}
- // Get a frame ID where 0 is a special value.
+ // Returns a frame ID for JDWP use, starting from 1.
size_t GetFrameId() {
return GetFrameHeight() + 1;
}
@@ -359,6 +360,12 @@
return cur_shadow_frame_;
}
+ StackIndirectReferenceTable* GetCurrentSirt() const {
+ Method** sp = GetCurrentQuickFrame();
+ ++sp; // Skip Method*; SIRT comes next;
+ return reinterpret_cast<StackIndirectReferenceTable*>(sp);
+ }
+
private:
size_t ComputeNumFrames() const;
diff --git a/src/stl_util.h b/src/stl_util.h
index c0fe6b1..1282cc4 100644
--- a/src/stl_util.h
+++ b/src/stl_util.h
@@ -17,10 +17,18 @@
#ifndef ART_SRC_STL_UTIL_H_
#define ART_SRC_STL_UTIL_H_
+#include <algorithm>
#include <iostream>
namespace art {
+// Sort and remove duplicates of an STL vector or deque.
+template<class T>
+void STLSortAndRemoveDuplicates(T* v) {
+ std::sort(v->begin(), v->end());
+ v->erase(std::unique(v->begin(), v->end()), v->end());
+}
+
// STLDeleteContainerPointers()
// For a range within a container of pointers, calls delete
// (non-array version) on these pointers.
diff --git a/src/thread.cc b/src/thread.cc
index 9f88f5a..8fe0160 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -519,9 +519,10 @@
}
struct StackDumpVisitor : public StackVisitor {
- StackDumpVisitor(std::ostream& os, const Thread* thread)
- : StackVisitor(thread->GetManagedStack(), thread->GetTraceStack()), last_method(NULL),
- last_line_number(0), repetition_count(0), os(os), thread(thread), frame_count(0) {
+ StackDumpVisitor(std::ostream& os, const Thread* thread, Context* context, bool can_allocate)
+ : StackVisitor(thread->GetManagedStack(), thread->GetTraceStack(), context),
+ os(os), thread(thread), can_allocate(can_allocate),
+ last_method(NULL), last_line_number(0), repetition_count(0), frame_count(0) {
}
virtual ~StackDumpVisitor() {
@@ -565,19 +566,24 @@
<< ":" << line_number << ")";
}
os << "\n";
+ if (frame_count == 0) {
+ Monitor::DescribeWait(os, thread);
+ }
+ if (can_allocate) {
+ Monitor::DescribeLocks(os, this);
+ }
}
- if (frame_count++ == 0) {
- Monitor::DescribeWait(os, thread);
- }
+ ++frame_count;
return true;
}
+ std::ostream& os;
+ const Thread* thread;
+ bool can_allocate;
MethodHelper mh;
Method* last_method;
int last_line_number;
int repetition_count;
- std::ostream& os;
- const Thread* thread;
int frame_count;
};
@@ -587,7 +593,8 @@
DumpKernelStack(os, GetTid(), " kernel: ", false);
DumpNativeStack(os, GetTid(), " native: ", false);
}
- StackDumpVisitor dumper(os, this);
+ UniquePtr<Context> context(Context::Create());
+ StackDumpVisitor dumper(os, this, context.get(), !throwing_OutOfMemoryError_);
dumper.WalkStack();
}
@@ -999,7 +1006,8 @@
public:
CountStackDepthVisitor(const ManagedStack* stack,
const std::vector<TraceStackFrame>* trace_stack)
- : StackVisitor(stack, trace_stack), depth_(0), skip_depth_(0), skipping_(true) {}
+ : StackVisitor(stack, trace_stack, NULL),
+ depth_(0), skip_depth_(0), skipping_(true) {}
bool VisitFrame() {
// We want to skip frames up to and including the exception's constructor.
@@ -1039,8 +1047,8 @@
explicit BuildInternalStackTraceVisitor(const ManagedStack* stack,
const std::vector<TraceStackFrame>* trace_stack,
int skip_depth)
- : StackVisitor(stack, trace_stack), skip_depth_(skip_depth), count_(0), dex_pc_trace_(NULL),
- method_trace_(NULL) {}
+ : StackVisitor(stack, trace_stack, NULL),
+ skip_depth_(skip_depth), count_(0), dex_pc_trace_(NULL), method_trace_(NULL) {}
bool Init(int depth, const ScopedJniThreadState& ts) {
// Allocate method trace with an extra slot that will hold the PC trace
@@ -1551,7 +1559,7 @@
struct CurrentMethodVisitor : public StackVisitor {
CurrentMethodVisitor(const ManagedStack* stack,
const std::vector<TraceStackFrame>* trace_stack)
- : StackVisitor(stack, trace_stack), method_(NULL), dex_pc_(0), frame_id_(0) {}
+ : StackVisitor(stack, trace_stack, NULL), method_(NULL), dex_pc_(0), frame_id_(0) {}
virtual bool VisitFrame() {
Method* m = GetMethod();
diff --git a/src/trace.cc b/src/trace.cc
index 6b5d668..cd594cf 100644
--- a/src/trace.cc
+++ b/src/trace.cc
@@ -197,7 +197,7 @@
static void TraceRestoreStack(Thread* self, void*) {
struct RestoreStackVisitor : public StackVisitor {
RestoreStackVisitor(Thread* self)
- : StackVisitor(self->GetManagedStack(), self->GetTraceStack()), self_(self) {}
+ : StackVisitor(self->GetManagedStack(), self->GetTraceStack(), NULL), self_(self) {}
virtual bool VisitFrame() {
if (self_->IsTraceStackEmpty()) {
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
diff --git a/src/verifier/method_verifier.h b/src/verifier/method_verifier.h
index 64a723e..8eef71a 100644
--- a/src/verifier/method_verifier.h
+++ b/src/verifier/method_verifier.h
@@ -202,6 +202,10 @@
static const std::vector<uint8_t>* GetGcMap(Compiler::MethodReference ref);
+ // Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding
+ // to the locks held at 'dex_pc' in 'm'.
+ static void FindLocksAtDexPc(Method* m, uint32_t dex_pc, std::vector<uint32_t>& monitor_enter_dex_pcs);
+
static void Init();
static void Shutdown();
@@ -242,6 +246,8 @@
// has an irrecoverable corruption.
bool Verify();
+ void FindLocksAtDexPc();
+
/*
* Compute the width of the instruction at each address in the instruction stream, and store it in
* insn_flags_. Addresses that are in the middle of an instruction, or that are part of switch
@@ -617,6 +623,12 @@
const DexFile::CodeItem* code_item_; // The code item containing the code for the method.
UniquePtr<InsnFlags[]> insn_flags_; // Instruction widths and flags, one entry per code unit.
+ // The dex PC of a FindLocksAtDexPc request, -1 otherwise.
+ uint32_t interesting_dex_pc_;
+ // The container into which FindLocksAtDexPc should write the registers containing held locks,
+ // NULL if we're not doing FindLocksAtDexPc.
+ std::vector<uint32_t>* monitor_enter_dex_pcs_;
+
// The types of any error that occurs.
std::vector<VerifyError> failures_;
// Error messages associated with failures.
diff --git a/src/verifier/register_line.h b/src/verifier/register_line.h
index e501e13..e406678 100644
--- a/src/verifier/register_line.h
+++ b/src/verifier/register_line.h
@@ -230,6 +230,14 @@
// Write a bit at each register location that holds a reference
void WriteReferenceBitMap(std::vector<uint8_t>& data, size_t max_bytes);
+ size_t GetMonitorEnterCount() {
+ return monitors_.size();
+ }
+
+ uint32_t GetMonitorEnterDexPc(size_t i) {
+ return monitors_[i];
+ }
+
private:
void CopyRegToLockDepth(size_t dst, size_t src) {
SafeMap<uint32_t, uint32_t>::iterator it = reg_to_lock_depths_.find(src);