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);
diff --git a/test/ReferenceMap/stack_walk_refmap_jni.cc b/test/ReferenceMap/stack_walk_refmap_jni.cc
index d7910af..ddda260 100644
--- a/test/ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/ReferenceMap/stack_walk_refmap_jni.cc
@@ -43,7 +43,7 @@
 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 3773198..9382b8f 100644
--- a/test/StackWalk/stack_walk_jni.cc
+++ b/test/StackWalk/stack_walk_jni.cc
@@ -40,8 +40,8 @@
 
 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 526b955..ff92d70 100755
--- a/tools/cpplint.py
+++ b/tools/cpplint.py
@@ -2568,7 +2568,7 @@
   # 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 @@
 
   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 @@
   """
   try:
     (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=',
+                                                 'stdout', # TODO(enh): added --stdout
                                                  'counting=',
                                                  'filter='])
   except getopt.GetoptError:
@@ -3307,12 +3308,15 @@
 
   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 @@
   _SetFilters(filters)
   _SetCountingStyle(counting_style)
 
+  sys.stderr = output_stream # TODO(enh): added --stdout
+
   return filenames