diff options
author | 2012-06-26 17:34:00 -0700 | |
---|---|---|
committer | 2012-06-27 12:18:25 -0700 | |
commit | 08fc03ae5dded4adc9b45b7014a4b9dfedbe95a6 (patch) | |
tree | 3e767d037fd3e47e792b88d982ad38bce26d39e9 | |
parent | f94404ead1f2e4a1b5a2ff8bbdb65c5e4d4a77ea (diff) |
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
-rw-r--r-- | src/debugger.cc | 22 | ||||
-rw-r--r-- | src/monitor.cc | 70 | ||||
-rw-r--r-- | src/monitor.h | 2 | ||||
-rw-r--r-- | src/native/dalvik_system_VMStack.cc | 6 | ||||
-rw-r--r-- | src/nth_caller_visitor.h | 6 | ||||
-rw-r--r-- | src/stack.cc | 7 | ||||
-rw-r--r-- | src/stack.h | 13 | ||||
-rw-r--r-- | src/stl_util.h | 8 | ||||
-rw-r--r-- | src/thread.cc | 34 | ||||
-rw-r--r-- | src/trace.cc | 2 | ||||
-rw-r--r-- | src/verifier/method_verifier.cc | 33 | ||||
-rw-r--r-- | src/verifier/method_verifier.h | 12 | ||||
-rw-r--r-- | src/verifier/register_line.h | 8 | ||||
-rw-r--r-- | test/ReferenceMap/stack_walk_refmap_jni.cc | 2 | ||||
-rw-r--r-- | test/StackWalk/stack_walk_jni.cc | 4 | ||||
-rwxr-xr-x | tools/cpplint.py | 10 |
16 files changed, 199 insertions, 40 deletions
diff --git a/src/debugger.cc b/src/debugger.cc index 147a9b65db..9dcdda5dde 100644 --- a/src/debugger.cc +++ b/src/debugger.cc @@ -1489,7 +1489,7 @@ static int GetStackDepth(Thread* thread) { 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 @@ static int GetStackDepth(Thread* thread) { } size_t depth; }; + CountStackDepthVisitor visitor(thread->GetManagedStack(), thread->GetTraceStack()); visitor.WalkStack(); return visitor.depth; @@ -1515,7 +1516,7 @@ JDWP::JdwpError Dbg::GetThreadFrames(JDWP::ObjectId thread_id, size_t start_fram 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 @@ static Object* GetThis(Method** quickFrame) { 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::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot 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 @@ void Dbg::SetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot 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 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId threadId, JDWP::JdwpStepSize s 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 @@ void Dbg::SetAllocTrackingEnabled(bool enabled) { 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 de08b88447..e5e867a38c 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 @@ void Monitor::DescribeWait(std::ostream& os, const Thread* thread) { // 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 @@ void Monitor::DescribeWait(std::ostream& os, const Thread* thread) { 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 300e5a520d..d72ff73d4f 100644 --- a/src/monitor.h +++ b/src/monitor.h @@ -58,6 +58,7 @@ namespace art { class Method; class Object; class Thread; +class StackVisitor; class Monitor { public: @@ -76,6 +77,7 @@ class Monitor { 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 933a5d5b10..12fa8db904 100644 --- a/src/native/dalvik_system_VMStack.cc +++ b/src/native/dalvik_system_VMStack.cc @@ -56,8 +56,9 @@ static jobject VMStack_getClosestUserClassLoader(JNIEnv* env, jclass, jobject ja 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 @@ static jobject VMStack_getClosestUserClassLoader(JNIEnv* env, jclass, jobject ja } return true; } + Object* bootstrap; Object* system; Object* class_loader; diff --git a/src/nth_caller_visitor.h b/src/nth_caller_visitor.h index db4d28cc10..0f29ae720f 100644 --- a/src/nth_caller_visitor.h +++ b/src/nth_caller_visitor.h @@ -24,8 +24,9 @@ namespace art { // 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 @@ struct NthCallerVisitor : public StackVisitor { } return true; } + size_t n; size_t count; Method* caller; diff --git a/src/stack.cc b/src/stack.cc index e7c632c439..c419530e37 100644 --- a/src/stack.cc +++ b/src/stack.cc @@ -27,7 +27,7 @@ namespace art { 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::GetDexPc() const { } 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 @@ uint32_t StackVisitor::GetVReg(Method* m, int vreg) const { } 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 @@ size_t StackVisitor::ComputeNumFrames() const { 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 ff0bcd0356..254451d71e 100644 --- a/src/stack.h +++ b/src/stack.h @@ -31,6 +31,7 @@ namespace art { class Method; class Object; class ShadowFrame; +class StackIndirectReferenceTable; class ScopedJniThreadState; class Thread; @@ -215,7 +216,7 @@ class PACKED ManagedStack { 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 @@ class StackVisitor { 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 @@ class StackVisitor { 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 c0fe6b1330..1282cc4230 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 9f88f5a3e6..8fe016030a 100644 --- a/src/thread.cc +++ b/src/thread.cc @@ -519,9 +519,10 @@ void Thread::DumpState(std::ostream& os) const { } 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 @@ struct StackDumpVisitor : public StackVisitor { << ":" << 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 @@ void Thread::DumpStack(std::ostream& os) const { 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 @@ class CountStackDepthVisitor : public StackVisitor { 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 @@ class BuildInternalStackTraceVisitor : public StackVisitor { 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 @@ Method* Thread::GetCurrentMethod(uint32_t* dex_pc, size_t* frame_id) const { 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 6b5d668f2a..cd594cf65b 100644 --- a/src/trace.cc +++ b/src/trace.cc @@ -197,7 +197,7 @@ static bool UninstallStubsClassVisitor(Class* klass, void*) { 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 178c2a92dd..908217218d 100644 --- a/src/verifier/method_verifier.cc +++ b/src/verifier/method_verifier.cc @@ -328,12 +328,35 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, DexCache* dex_cache, 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } #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 64a723ef2e..8eef71aa37 100644 --- a/src/verifier/method_verifier.h +++ b/src/verifier/method_verifier.h @@ -202,6 +202,10 @@ class MethodVerifier { 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 @@ class MethodVerifier { // 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 @@ class MethodVerifier { 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 e501e1347e..e4066783bd 100644 --- a/src/verifier/register_line.h +++ b/src/verifier/register_line.h @@ -230,6 +230,14 @@ class RegisterLine { // 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); diff --git a/test/ReferenceMap/stack_walk_refmap_jni.cc b/test/ReferenceMap/stack_walk_refmap_jni.cc index d7910af222..ddda260cd5 100644 --- a/test/ReferenceMap/stack_walk_refmap_jni.cc +++ b/test/ReferenceMap/stack_walk_refmap_jni.cc @@ -43,7 +43,7 @@ namespace art { struct ReferenceMap2Visitor : public StackVisitor { explicit ReferenceMap2Visitor(const ManagedStack* stack, const std::vector<TraceStackFrame>* trace_stack) : - StackVisitor(stack, trace_stack) { + StackVisitor(stack, trace_stack, NULL) { } bool VisitFrame() { diff --git a/test/StackWalk/stack_walk_jni.cc b/test/StackWalk/stack_walk_jni.cc index 37731981ff..9382b8ffab 100644 --- a/test/StackWalk/stack_walk_jni.cc +++ b/test/StackWalk/stack_walk_jni.cc @@ -40,8 +40,8 @@ static int gJava_StackWalk_refmap_calls = 0; struct TestReferenceMapVisitor : public StackVisitor { explicit TestReferenceMapVisitor(const ManagedStack* stack, - const std::vector<TraceStackFrame>* trace_stack) : - StackVisitor(stack, trace_stack) { + const std::vector<TraceStackFrame>* trace_stack) + : StackVisitor(stack, trace_stack, NULL) { } bool VisitFrame() { diff --git a/tools/cpplint.py b/tools/cpplint.py index 526b9556dc..ff92d70f06 100755 --- a/tools/cpplint.py +++ b/tools/cpplint.py @@ -2568,7 +2568,7 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, # probably a member operator declaration or default constructor. match = Search( r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there - r'(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line) + r'(int|float|double|bool|char|u?int(8|16|32|64)_t)\([^)]', line) # TODO(enh): upstream change to handle all stdint types. if match: # gMock methods are defined using some variant of MOCK_METHODx(name, type) # where type may be float(), int(string), etc. Without context they are @@ -2585,7 +2585,7 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], 'static_cast', - r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) + r'\((int|float|double|bool|char|u?int(8|16|32|64))\)', error) # TODO(enh): upstream change to handle all stdint types. # This doesn't catch all cases. Consider (const char * const)"hello". # @@ -3300,6 +3300,7 @@ def ParseArguments(args): """ try: (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', + 'stdout', # TODO(enh): added --stdout 'counting=', 'filter=']) except getopt.GetoptError: @@ -3307,12 +3308,15 @@ def ParseArguments(args): verbosity = _VerboseLevel() output_format = _OutputFormat() + output_stream = sys.stderr # TODO(enh): added --stdout filters = '' counting_style = '' for (opt, val) in opts: if opt == '--help': PrintUsage(None) + elif opt == '--stdout': # TODO(enh): added --stdout + output_stream = sys.stdout # TODO(enh): added --stdout elif opt == '--output': if not val in ('emacs', 'vs7'): PrintUsage('The only allowed output formats are emacs and vs7.') @@ -3336,6 +3340,8 @@ def ParseArguments(args): _SetFilters(filters) _SetCountingStyle(counting_style) + sys.stderr = output_stream # TODO(enh): added --stdout + return filenames |