Changed monitor to contain method and return pc values for logging.

The monitor saves method and return pc for logging, instead of the
source filename and line number. This saves it from having to do a
lookup every time a fat lock is acquired.

Change-Id: I88871abc90626b9e4dffc9677c093fd24937385c
diff --git a/src/monitor.cc b/src/monitor.cc
index 16298ae..b9a9459 100644
--- a/src/monitor.cc
+++ b/src/monitor.cc
@@ -24,6 +24,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "class_linker.h"
 #include "mutex.h"
 #include "object.h"
 #include "stl_util.h"
@@ -120,8 +121,8 @@
       obj_(obj),
       wait_set_(NULL),
       lock_("a monitor lock"),
-      owner_filename_(NULL),
-      owner_line_number_(0) {
+      locking_method_(NULL),
+      locking_pc_(0) {
 }
 
 Monitor::~Monitor() {
@@ -197,15 +198,15 @@
   uint64_t waitStart, waitEnd;
   if (!lock_.TryLock()) {
     uint32_t wait_threshold = lock_profiling_threshold_;
-    const char* current_owner_filename = NULL;
-    uint32_t current_owner_line_number = -1;
+    const Method* current_locking_method = NULL;
+    uint32_t current_locking_pc = 0;
     {
       ScopedThreadStateChange tsc(self, Thread::kBlocked);
       if (wait_threshold != 0) {
         waitStart = NanoTime() / 1000;
       }
-      current_owner_filename = owner_filename_;
-      current_owner_line_number = owner_line_number_;
+      current_locking_method = locking_method_;
+      current_locking_pc = locking_pc_;
 
       lock_.Lock();
       if (wait_threshold != 0) {
@@ -222,7 +223,11 @@
         sample_percent = 100 * wait_ms / wait_threshold;
       }
       if (sample_percent != 0 && (static_cast<uint32_t>(rand() % 100) < sample_percent)) {
-        LogContentionEvent(self, wait_ms, sample_percent, current_owner_filename, current_owner_line_number);
+        const char* current_locking_filename;
+        uint32_t current_locking_line_number;
+        TranslateLocation(current_locking_method, current_locking_pc,
+                          current_locking_filename, current_locking_line_number);
+        LogContentionEvent(self, wait_ms, sample_percent, current_locking_filename, current_locking_line_number);
       }
     }
   }
@@ -232,7 +237,8 @@
   // When debugging, save the current monitor holder for future
   // acquisition failures to use in sampled logging.
   if (lock_profiling_threshold_ != 0) {
-    self->GetCurrentLocation(owner_filename_, owner_line_number_);
+    locking_method_ = self->GetCurrentMethod();
+    locking_pc_ = self->GetCurrentReturnPc();
   }
 }
 
@@ -246,8 +252,8 @@
     // We own the monitor, so nobody else can be in here.
     if (lock_count_ == 0) {
       owner_ = NULL;
-      owner_filename_ = "unlocked";
-      owner_line_number_ = 0;
+      locking_method_ = NULL;
+      locking_pc_ = 0;
       lock_.Unlock();
     } else {
       --lock_count_;
@@ -366,10 +372,10 @@
   int prevLockCount = lock_count_;
   lock_count_ = 0;
   owner_ = NULL;
-  const char* savedFileName = owner_filename_;
-  owner_filename_ = NULL;
-  uint32_t savedLineNumber = owner_line_number_;
-  owner_line_number_ = 0;
+  const Method* savedMethod = locking_method_;
+  locking_method_ = NULL;
+  uint32_t savedPc = locking_pc_;
+  locking_pc_ = 0;
 
   /*
    * Update thread status.  If the GC wakes up, it'll ignore us, knowing
@@ -435,8 +441,8 @@
    */
   owner_ = self;
   lock_count_ = prevLockCount;
-  owner_filename_ = savedFileName;
-  owner_line_number_ = savedLineNumber;
+  locking_method_ = savedMethod;
+  locking_pc_ = savedPc;
   RemoveFromWaitSet(self);
 
   /* set self->status back to Thread::kRunnable, and self-suspend if needed */
@@ -810,6 +816,25 @@
   os << "\n";
 }
 
+void Monitor::TranslateLocation(const Method* method, uint32_t pc,
+                                const char*& source_file, uint32_t& line_number) const {
+  // If method is null, location is unknown
+  if (method == NULL) {
+    source_file = "unknown";
+    line_number = 0;
+    return;
+  }
+
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Class* c = method->GetDeclaringClass();
+  DexCache* dex_cache = c->GetDexCache();
+  const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
+  const DexFile::ClassDef* class_def = dex_file.FindClassDef(c->GetDescriptor()->ToModifiedUtf8());
+
+  source_file = dex_file.dexGetSourceFile(*class_def);
+  line_number = dex_file.GetLineNumFromPC(method, method->ToDexPC(pc));
+}
+
 MonitorList::MonitorList() : lock_("MonitorList lock") {
 }
 
diff --git a/src/monitor.h b/src/monitor.h
index be704ba..9b79f8e 100644
--- a/src/monitor.h
+++ b/src/monitor.h
@@ -55,6 +55,7 @@
 #define LW_LOCK_OWNER_SHIFT 3
 #define LW_LOCK_OWNER(x) (((x) >> LW_LOCK_OWNER_SHIFT) & LW_LOCK_OWNER_MASK)
 
+class Method;
 class Object;
 class Thread;
 
@@ -97,6 +98,10 @@
 
   void Wait(Thread* self, int64_t msec, int32_t nsec, bool interruptShouldThrow);
 
+  // Translates the provided method and pc into its declaring class' source file and line number.
+  void TranslateLocation(const Method* method, uint32_t pc,
+                         const char*& source_file, uint32_t& line_number) const;
+
   static bool (*is_sensitive_thread_hook_)();
   static bool is_verbose_;
   static uint32_t lock_profiling_threshold_;
@@ -115,10 +120,11 @@
 
   Mutex lock_;
 
-  // Who last acquired this monitor, when lock sampling is enabled.
-  // Even when enabled, owner_filename_ may be NULL.
-  const char* owner_filename_;
-  uint32_t owner_line_number_;
+  // Method and pc where the lock owner acquired the lock, used when lock
+  // sampling is enabled. locking_method_ may be null if the lock is currently
+  // unlocked, or if the lock is acquired by the system when the stack is empty.
+  const Method* locking_method_;
+  uint32_t locking_pc_;
 
   friend class MonitorList;
   friend class Object;
diff --git a/src/monitor_android.cc b/src/monitor_android.cc
index 95dd397..39dc524 100644
--- a/src/monitor_android.cc
+++ b/src/monitor_android.cc
@@ -78,7 +78,7 @@
   // Emit the source code file name, <= 37 bytes.
   const char* filename;
   uint32_t line_number;
-  self->GetCurrentLocation(filename, line_number);
+  TranslateLocation(self->GetCurrentMethod(), self->GetCurrentReturnPc(), filename, line_number);
   cp = EventLogWriteString(cp, filename, strlen(filename));
 
   // Emit the source code line number, 5 bytes.
diff --git a/src/thread.cc b/src/thread.cc
index 2bdfb3c..5dd0f2d 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1191,35 +1191,6 @@
   return result;
 }
 
-void Thread::GetCurrentLocation(const char*& source_file, uint32_t& line_number) const {
-  Frame f = top_of_managed_stack_;
-  Method* m = f.GetMethod();
-
-  // Check if the stack is empty
-  if (m == NULL) {
-    source_file = "UNKNOWN";
-    line_number = 0;
-    return;
-  }
-
-  // TODO: can this ever happen?
-  if (m->IsCalleeSaveMethod()) {
-    f.Next();
-    m = f.GetMethod();
-  }
-
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  Class* c = m->GetDeclaringClass();
-  DexCache* dex_cache = c->GetDexCache();
-  const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
-  const DexFile::ClassDef* class_def = dex_file.FindClassDef(c->GetDescriptor()->ToModifiedUtf8());
-
-  source_file = dex_file.dexGetSourceFile(*class_def);
-
-  uint32_t pc = ManglePc(f.GetReturnPC());
-  line_number = dex_file.GetLineNumFromPC(m, m->ToDexPC(pc));
-}
-
 void Thread::ThrowNewExceptionF(const char* exception_class_descriptor, const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
@@ -1399,7 +1370,7 @@
   // here via a "FromCode" function, in which case there's a synthetic
   // callee-save method at the top of the stack. These shouldn't be user-visible,
   // so if we find one, skip it and return the compiled method underneath.
-  if (m->IsCalleeSaveMethod()) {
+  if (m != NULL && m->IsCalleeSaveMethod()) {
     Frame f = top_of_managed_stack_;
     f.Next();
     m = f.GetMethod();
@@ -1407,6 +1378,13 @@
   return m;
 }
 
+uint32_t Thread::GetCurrentReturnPc() const {
+  if (top_of_managed_stack_.GetMethod() == NULL) {
+    return 0;
+  }
+  return ManglePc(top_of_managed_stack_.GetReturnPC());
+}
+
 bool Thread::HoldsLock(Object* object) {
   if (object == NULL) {
     return false;
diff --git a/src/thread.h b/src/thread.h
index 15ded24..7b8c8b9 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -229,9 +229,6 @@
   // Returns the java.lang.Thread's name, or NULL.
   String* GetName() const;
 
-  // Returns the current method's declaring class' source file and the current line number.
-  void GetCurrentLocation(const char*& source_file, uint32_t& line_number) const;
-
   Object* GetPeer() const {
     return peer_;
   }
@@ -244,6 +241,8 @@
   // This is used by the JNI implementation for logging and diagnostic purposes.
   const Method* GetCurrentMethod() const;
 
+  uint32_t GetCurrentReturnPc() const;
+
   bool IsExceptionPending() const {
     return exception_ != NULL;
   }