Stub to capture method entry/exit.

Added stubs to allow traceview to do method tracing. Currently only
outputs to logcat, and a later change will generate the proper log file.

Change-Id: Icaafc50e2eaf042ddc4d882011f7e8121bdd8b1c
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 2001c06..0b015fe 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -183,6 +183,7 @@
 	src/sun_misc_Unsafe.cc \
 	src/thread.cc \
 	src/thread_list.cc \
+	src/trace.cc \
 	src/runtime_support.cc \
 	src/utf.cc \
 	src/utils.cc \
diff --git a/src/dalvik_system_VMDebug.cc b/src/dalvik_system_VMDebug.cc
index 65126e7..c4c43f1 100644
--- a/src/dalvik_system_VMDebug.cc
+++ b/src/dalvik_system_VMDebug.cc
@@ -17,6 +17,7 @@
 #include "class_linker.h"
 #include "debugger.h"
 #include "jni_internal.h"
+#include "trace.h"
 #include "hprof/hprof.h"
 #include "ScopedUtfChars.h"
 #include "toStringArray.h"
@@ -36,9 +37,8 @@
  */
 jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) {
   std::vector<std::string> features;
-  // TODO: we might need to uncomment these to make them work.
-  //features.push_back("method-trace-profiling");
-  //features.push_back("method-trace-profiling-streaming");
+  features.push_back("method-trace-profiling");
+  features.push_back("method-trace-profiling-streaming");
   features.push_back("hprof-heap-dump");
   features.push_back("hprof-heap-dump-streaming");
   return toStringArray(env, features);
@@ -61,8 +61,7 @@
 }
 
 void VMDebug_startMethodTracingDdmsImpl(JNIEnv* env, jclass, jint bufferSize, jint flags) {
-  UNIMPLEMENTED(WARNING);
-  //dvmMethodTraceStart("[DDMS]", -1, bufferSize, flags, true);
+  Trace::Start("[DDMS]", -1, bufferSize, flags, true);
 }
 
 void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceFilename, jobject javaFd, jint bufferSize, jint flags) {
@@ -81,8 +80,7 @@
   if (traceFilename.c_str() == NULL) {
     return;
   }
-  UNIMPLEMENTED(WARNING);
-  //dvmMethodTraceStart(traceFilename.c_str(), fd, bufferSize, flags, false);
+  Trace::Start(traceFilename.c_str(), fd, bufferSize, flags, false);
 }
 
 void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring javaTraceFilename, jint bufferSize, jint flags) {
@@ -90,18 +88,15 @@
   if (traceFilename.c_str() == NULL) {
     return;
   }
-  UNIMPLEMENTED(WARNING);
-  //dvmMethodTraceStart(traceFilename.c_str(), -1, bufferSize, flags, false);
+  Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, false);
 }
 
 jboolean VMDebug_isMethodTracingActive(JNIEnv*, jclass) {
-  UNIMPLEMENTED(WARNING);
-  return JNI_FALSE; //dvmIsMethodTraceActive();
+  return Trace::IsMethodTracingActive();
 }
 
 void VMDebug_stopMethodTracing(JNIEnv*, jclass) {
-  UNIMPLEMENTED(WARNING);
-  //dvmMethodTraceStop();
+  Trace::Stop();
 }
 
 void VMDebug_startEmulatorTracing(JNIEnv*, jclass) {
diff --git a/src/dex_cache.cc b/src/dex_cache.cc
index c43f5bf..d844909 100644
--- a/src/dex_cache.cc
+++ b/src/dex_cache.cc
@@ -9,6 +9,12 @@
 
 namespace art {
 
+void CodeAndDirectMethods::SetResolvedDirectMethodTraceEntry(uint32_t method_idx, const void* pcode) {
+  CHECK(pcode != NULL);
+  int32_t code = reinterpret_cast<int32_t>(pcode);
+  Set(CodeIndex(method_idx), code);
+}
+
 void CodeAndDirectMethods::SetResolvedDirectMethod(uint32_t method_idx, Method* method) {
   CHECK(method != NULL);
   CHECK(method->IsDirect()) << PrettyMethod(method);
diff --git a/src/dex_cache.h b/src/dex_cache.h
index 8cd9b12..9e35053 100644
--- a/src/dex_cache.h
+++ b/src/dex_cache.h
@@ -33,6 +33,8 @@
     Set(MethodIndex(method_idx), method_idx);
   }
 
+  void SetResolvedDirectMethodTraceEntry(uint32_t method_idx, const void* pcode);
+
   void SetResolvedDirectMethod(uint32_t method_idx, Method* method);
 
   static size_t LengthAsArray(size_t elements) {
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index ad34215..0d0cfb4 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -7,7 +7,6 @@
 #include <cmath>
 
 #include "common_test.h"
-#include "gtest/gtest-death-test.h"
 #include "ScopedLocalRef.h"
 
 namespace art {
diff --git a/src/object.cc b/src/object.cc
index 14e8c5c..614df3c 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -447,7 +447,8 @@
     return DexFile::kDexNoIndex;   // Special no mapping case
   }
   size_t mapping_table_length = GetMappingTableLength();
-  uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(GetCode());
+  const void* code_offset = Trace::IsMethodTracingActive() ? Trace::GetSavedCodeFromMap(this) : GetCode();
+  uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(code_offset);
   uint32_t best_offset = 0;
   uint32_t best_dex_offset = 0;
   for (size_t i = 0; i < mapping_table_length; i += 2) {
@@ -477,7 +478,11 @@
     uint32_t map_offset = mapping_table[i];
     uint32_t map_dex_offset = mapping_table[i + 1];
     if (map_dex_offset == dex_pc) {
-      return reinterpret_cast<uintptr_t>(GetCode()) + map_offset;
+      if (Trace::IsMethodTracingActive()) {
+        return reinterpret_cast<uintptr_t>(Trace::GetSavedCodeFromMap(this)) + map_offset;
+      } else {
+        return reinterpret_cast<uintptr_t>(GetCode()) + map_offset;
+      }
     }
   }
   LOG(FATAL) << "Looking up Dex PC not contained in method";
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 1d05527..3a3eaf1 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -22,6 +22,7 @@
 #include "object.h"
 #include "object_utils.h"
 #include "reflection.h"
+#include "trace.h"
 #include "ScopedLocalRef.h"
 
 namespace art {
@@ -139,6 +140,10 @@
 
 extern "C" void artThrowStackOverflowFromCode(Method* method, Thread* thread, Method** sp) {
   FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
+  // Remove extra entry pushed onto second stack during method tracing
+  if (Trace::IsMethodTracingActive()) {
+    artTraceMethodUnwindFromCode(thread);
+  }
   thread->SetStackEndForStackOverflow();  // Allow space on the stack for constructor to execute
   thread->ThrowNewExceptionF("Ljava/lang/StackOverflowError;",
       "stack size %zdkb; default stack size: %zdkb",
@@ -1163,6 +1168,32 @@
   }
 }
 
+extern "C" const void* artTraceMethodEntryFromCode(Method* method, Thread* self, uintptr_t lr) {
+  LOG(INFO) << "Tracer - entering: " << PrettyMethod(method);
+  TraceStackFrame trace_frame = TraceStackFrame(method, lr);
+  self->PushTraceStackFrame(trace_frame);
+
+  return Trace::GetSavedCodeFromMap(method);
+}
+
+extern "C" uintptr_t artTraceMethodExitFromCode() {
+  TraceStackFrame trace_frame = Thread::Current()->PopTraceStackFrame();
+  Method* method = trace_frame.method_;
+  uintptr_t lr = trace_frame.return_pc_;
+  LOG(INFO) << "Tracer - exiting: " << PrettyMethod(method);
+
+  return lr;
+}
+
+uintptr_t artTraceMethodUnwindFromCode(Thread* self) {
+  TraceStackFrame trace_frame = self->PopTraceStackFrame();
+  Method* method = trace_frame.method_;
+  uintptr_t lr = trace_frame.return_pc_;
+  LOG(INFO) << "Tracer - unwinding: " << PrettyMethod(method);
+
+  return lr;
+}
+
 /*
  * Float/double conversion requires clamping to min and max of integer form.  If
  * target doesn't support this normally, use these.
diff --git a/src/runtime_support.h b/src/runtime_support.h
index be53b03..2ebfc22 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -27,6 +27,7 @@
 void ObjectInitFromCode(Object* o);
 extern void ResolveMethodFromCode(Method* method, uint32_t method_idx);
 extern void LockObjectFromCode(Thread* thread, Object* obj);
+uint32_t artTraceMethodUnwindFromCode(Thread* self);
 extern int64_t D2L(double d);
 extern int64_t F2L(float f);
 
@@ -69,6 +70,8 @@
   extern "C" void* art_initialize_static_storage_from_code(uint32_t, void*);
   extern "C" void* art_initialize_type_from_code(uint32_t, void*);
   extern "C" void* art_initialize_type_and_verify_access_from_code(uint32_t, void*);
+  extern "C" void art_trace_entry_from_code(void*);
+  extern "C" void art_trace_exit_from_code();
   extern "C" void* art_resolve_string_from_code(void*, uint32_t);
 
   /* Conversions */
diff --git a/src/runtime_support_asm.S b/src/runtime_support_asm.S
index ad600ce..305e732 100644
--- a/src/runtime_support_asm.S
+++ b/src/runtime_support_asm.S
@@ -558,6 +558,33 @@
     bxeq    lr                     @ return on success
     DELIVER_PENDING_EXCEPTION
 
+    .global art_trace_entry_from_code
+    .extern artTraceMethodEntryFromCode
+    /*
+     * Routine that intercepts method calls
+     */
+art_trace_entry_from_code:
+    push  {r0-r3}        @ save arguments (4 words)
+    mov   r1, r9         @ pass Thread::Current
+    mov   r2, lr         @ pass LR
+    blx   artTraceMethodEntryFromCode  @ (Method*, Thread*, LR)
+    mov   r12, r0        @ r12 holds reference to code
+    pop   {r0-r3}        @ restore arguments
+    blx   r12            @ call method
+    /* intentional fallthrough */
+
+    .global art_trace_exit_from_code
+    .extern artTraceMethodExitFromCode
+    /*
+     * Routine that intercepts method returns
+     */
+art_trace_exit_from_code:
+    push  {r0-r1}        @ save return value
+    blx   artTraceMethodExitFromCode  @ ()
+    mov   lr, r0         @ restore link register
+    pop   {r0, r1}       @ restore return value
+    bx    lr             @ return
+
     .global art_shl_long
 art_shl_long:
     /*
diff --git a/src/stack.cc b/src/stack.cc
index 9944ec3..0232572 100644
--- a/src/stack.cc
+++ b/src/stack.cc
@@ -48,6 +48,11 @@
   return *reinterpret_cast<uintptr_t*>(pc_addr);
 }
 
+void Frame::SetReturnPC(uintptr_t pc) {
+  byte* pc_addr = reinterpret_cast<byte*>(sp_) + GetMethod()->GetReturnPcOffsetInBytes();
+  *reinterpret_cast<uintptr_t*>(pc_addr) = pc;
+}
+
 uint32_t Frame::GetVReg(const DexFile::CodeItem* code_item, uint32_t core_spills,
                         uint32_t fp_spills, size_t frame_size, int vreg) const {
   int offset = oatVRegOffset(code_item, core_spills, fp_spills, frame_size, vreg);
diff --git a/src/stack.h b/src/stack.h
index eee1d56..868b157 100644
--- a/src/stack.h
+++ b/src/stack.h
@@ -53,6 +53,8 @@
 
   uintptr_t GetReturnPC() const;
 
+  void SetReturnPC(uintptr_t pc);
+
   uintptr_t LoadCalleeSave(int num) const;
 
 
diff --git a/src/thread.cc b/src/thread.cc
index a9dfc23..ff92359 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -793,7 +793,8 @@
       long_jump_context_(NULL),
       throwing_OutOfMemoryError_(false),
       pre_allocated_OutOfMemoryError_(NULL),
-      debug_invoke_req_(new DebugInvokeReq) {
+      debug_invoke_req_(new DebugInvokeReq),
+      trace_stack_(new std::vector<TraceStackFrame>) {
   CHECK_EQ((sizeof(Thread) % 4), 0U) << sizeof(Thread);
 }
 
@@ -838,6 +839,7 @@
   delete long_jump_context_;
 
   delete debug_invoke_req_;
+  delete trace_stack_;
 }
 
 void Thread::HandleUncaughtExceptions() {
@@ -1319,6 +1321,17 @@
       } else if (method->IsNative()) {
         native_method_count_++;
       } else {
+        // Unwind stack during method tracing
+        if (Trace::IsMethodTracingActive()) {
+#if defined(__arm__)
+          uintptr_t trace_exit = reinterpret_cast<uintptr_t>(art_trace_exit_from_code);
+          if (ManglePc(trace_exit) == pc) {
+            pc = ManglePc(artTraceMethodUnwindFromCode(Thread::Current()));
+          }
+#else
+          UNIMPLEMENTED(WARNING);
+#endif
+        }
         dex_pc = method->ToDexPC(pc);
       }
       if (dex_pc != DexFile::kDexNoIndex) {
diff --git a/src/thread.h b/src/thread.h
index c4c619a..d24ddca 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -23,6 +23,7 @@
 #include <iosfwd>
 #include <list>
 #include <string>
+#include <vector>
 
 #include "dex_file.h"
 #include "globals.h"
@@ -34,6 +35,7 @@
 #include "offsets.h"
 #include "runtime_stats.h"
 #include "stack.h"
+#include "trace.h"
 #include "UniquePtr.h"
 
 namespace art {
@@ -466,6 +468,20 @@
     return debug_invoke_req_;
   }
 
+  bool IsTraceStackEmpty() const {
+    return trace_stack_->empty();
+  }
+
+  void PushTraceStackFrame(const TraceStackFrame& frame) {
+    trace_stack_->push_back(frame);
+  }
+
+  TraceStackFrame PopTraceStackFrame() {
+    TraceStackFrame frame = trace_stack_->back();
+    trace_stack_->pop_back();
+    return frame;
+  }
+
  private:
   Thread();
   ~Thread();
@@ -604,6 +620,10 @@
   // TLS key used to retrieve the VM thread object.
   static pthread_key_t pthread_key_self_;
 
+  // Additional stack used by method tracer to store method and return pc values.
+  // Stored as a pointer since std::vector is not PACKED.
+  std::vector<TraceStackFrame>* trace_stack_;
+
   DISALLOW_COPY_AND_ASSIGN(Thread);
 };
 
diff --git a/src/trace.cc b/src/trace.cc
new file mode 100644
index 0000000..0a41a2e
--- /dev/null
+++ b/src/trace.cc
@@ -0,0 +1,190 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "trace.h"
+
+#include "class_linker.h"
+#include "dex_cache.h"
+#include "runtime_support.h"
+#include "thread.h"
+
+namespace art {
+
+#if defined(__arm__)
+static bool InstallStubsClassVisitor(Class* klass, void* trace_stub) {
+  for (size_t i = 0; i < klass->NumDirectMethods(); i++) {
+    Method* method = klass->GetDirectMethod(i);
+    if (method->GetCode() != trace_stub) {
+      Trace::SaveAndUpdateCode(method, trace_stub);
+    }
+  }
+
+  for (size_t i = 0; i < klass->NumVirtualMethods(); i++) {
+    Method* method = klass->GetVirtualMethod(i);
+    if (method->GetCode() != trace_stub) {
+      Trace::SaveAndUpdateCode(method, trace_stub);
+    }
+  }
+
+  if (!klass->IsArrayClass() && !klass->IsPrimitive()) {
+    CodeAndDirectMethods* c_and_dm = klass->GetDexCache()->GetCodeAndDirectMethods();
+    for (size_t i = 0; i < c_and_dm->NumCodeAndDirectMethods(); i++) {
+      Method* method = c_and_dm->GetResolvedMethod(i);
+      if (method != NULL && (size_t) method != i) {
+        c_and_dm->SetResolvedDirectMethodTraceEntry(i, trace_stub);
+      }
+    }
+  }
+  return true;
+}
+
+static bool UninstallStubsClassVisitor(Class* klass, void* trace_stub) {
+  for (size_t i = 0; i < klass->NumDirectMethods(); i++) {
+    Method* method = klass->GetDirectMethod(i);
+    if (Trace::GetSavedCodeFromMap(method) != NULL) {
+      Trace::ResetSavedCode(method);
+    }
+  }
+
+  for (size_t i = 0; i < klass->NumVirtualMethods(); i++) {
+    Method* method = klass->GetVirtualMethod(i);
+    if (Trace::GetSavedCodeFromMap(method) != NULL) {
+      Trace::ResetSavedCode(method);
+    }
+  }
+
+  if (!klass->IsArrayClass() && !klass->IsPrimitive()) {
+    CodeAndDirectMethods* c_and_dm = klass->GetDexCache()->GetCodeAndDirectMethods();
+    for (size_t i = 0; i < c_and_dm->NumCodeAndDirectMethods(); i++) {
+      const void* code = c_and_dm->GetResolvedCode(i);
+      if (code == trace_stub) {
+        Method* method = klass->GetDexCache()->GetResolvedMethod(i);
+        if (Trace::GetSavedCodeFromMap(method) != NULL) {
+          Trace::ResetSavedCode(method);
+        }
+        c_and_dm->SetResolvedDirectMethod(i, method);
+      }
+    }
+  }
+  return true;
+}
+
+static void TraceRestoreStack(Thread* t, void*) {
+  uintptr_t trace_exit = reinterpret_cast<uintptr_t>(art_trace_exit_from_code);
+
+  Frame frame = t->GetTopOfStack();
+  if (frame.GetSP() != 0) {
+    for ( ; frame.GetMethod() != 0; frame.Next()) {
+      if (t->IsTraceStackEmpty()) {
+        break;
+      }
+      uintptr_t pc = frame.GetReturnPC();
+      Method* method = frame.GetMethod();
+      if (trace_exit == pc) {
+        TraceStackFrame trace_frame = t->PopTraceStackFrame();
+        frame.SetReturnPC(trace_frame.return_pc_);
+        CHECK(method == trace_frame.method_);
+      }
+    }
+  }
+}
+#endif
+
+bool Trace::method_tracing_active_ = false;
+std::map<const Method*, const void*> Trace::saved_code_map_;
+
+void Trace::AddSavedCodeToMap(const Method* method, const void* code) {
+  saved_code_map_.insert(std::make_pair(method, code));
+}
+
+void Trace::RemoveSavedCodeFromMap(const Method* method) {
+  saved_code_map_.erase(method);
+}
+
+const void* Trace::GetSavedCodeFromMap(const Method* method) {
+  return saved_code_map_.find(method)->second;
+}
+
+void Trace::SaveAndUpdateCode(Method* method, const void* new_code) {
+  CHECK(GetSavedCodeFromMap(method) == NULL);
+  AddSavedCodeToMap(method, method->GetCode());
+  method->SetCode(new_code);
+}
+
+void Trace::ResetSavedCode(Method* method) {
+  CHECK(GetSavedCodeFromMap(method) != NULL);
+  method->SetCode(GetSavedCodeFromMap(method));
+  RemoveSavedCodeFromMap(method);
+}
+
+bool Trace::IsMethodTracingActive() {
+  return method_tracing_active_;
+}
+
+void Trace::SetMethodTracingActive(bool value) {
+  method_tracing_active_ = value;
+}
+
+void Trace::Start(const char* trace_filename, int trace_fd, int buffer_size, int flags, bool direct_to_ddms) {
+  LOG(INFO) << "Starting method tracing...";
+  if (IsMethodTracingActive()) {
+    // TODO: Stop the trace, then start it up again instead of returning
+    LOG(INFO) << "Trace already in progress, stopping";
+    return;
+  }
+
+  SetMethodTracingActive(true);
+  InstallStubs();
+  LOG(INFO) << "Method tracing started";
+}
+
+void Trace::Stop() {
+  LOG(INFO) << "Stopping method tracing...";
+  if (!IsMethodTracingActive()) {
+    LOG(INFO) << "Trace stop requested, but not running";
+    return;
+  }
+
+  UninstallStubs();
+  SetMethodTracingActive(false);
+  LOG(INFO) << "Method tracing stopped";
+}
+
+void Trace::InstallStubs() {
+#if defined(__arm__)
+  {
+    ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
+    Runtime::Current()->GetThreadList()->SuspendAll(false);
+  }
+
+  void* trace_stub = reinterpret_cast<void*>(art_trace_entry_from_code);
+  Runtime::Current()->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, trace_stub);
+
+  Runtime::Current()->GetThreadList()->ResumeAll(false);
+#else
+  UNIMPLEMENTED(WARNING);
+#endif
+}
+
+void Trace::UninstallStubs() {
+#if defined(__arm__)
+  {
+    ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
+    Runtime::Current()->GetThreadList()->SuspendAll(false);
+  }
+
+  void* trace_stub = reinterpret_cast<void*>(art_trace_entry_from_code);
+  Runtime::Current()->GetClassLinker()->VisitClasses(UninstallStubsClassVisitor, trace_stub);
+
+  // Restore stacks of all threads
+  {
+    ScopedThreadListLock thread_list_lock;
+    Runtime::Current()->GetThreadList()->ForEach(TraceRestoreStack, NULL);
+  }
+
+  Runtime::Current()->GetThreadList()->ResumeAll(false);
+#else
+  UNIMPLEMENTED(WARNING);
+#endif
+}
+
+}  // namespace art
diff --git a/src/trace.h b/src/trace.h
new file mode 100644
index 0000000..4c0edda
--- /dev/null
+++ b/src/trace.h
@@ -0,0 +1,56 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_TRACE_H_
+#define ART_SRC_TRACE_H_
+
+#include <map>
+
+#include "globals.h"
+#include "macros.h"
+
+namespace art {
+
+class Method;
+
+struct TraceStackFrame {
+  TraceStackFrame(Method* method, uintptr_t return_pc)
+      : method_(method), return_pc_(return_pc) {
+  }
+
+  Method* method_;
+  uintptr_t return_pc_;
+};
+
+class Trace {
+ public:
+  static void Start(const char* trace_filename, int trace_fd, int buffer_size, int flags, bool direct_to_ddms);
+  static void Stop();
+
+  static bool IsMethodTracingActive();
+  static void SetMethodTracingActive(bool value);
+
+  static void AddSavedCodeToMap(const Method* method, const void* code);
+  static void RemoveSavedCodeFromMap(const Method* method);
+  static const void* GetSavedCodeFromMap(const Method* method);
+
+  static void SaveAndUpdateCode(Method* method, const void* new_code);
+  static void ResetSavedCode(Method* method);
+
+ private:
+  // Replaces code of each method with a pointer to a stub for method tracing.
+  static void InstallStubs();
+
+  // Restores original code for each method and fixes the return values of each thread's stack.
+  static void UninstallStubs();
+
+  static bool method_tracing_active_;
+
+  // Maps a method to its original code pointer
+  static std::map<const Method*, const void*> saved_code_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(Trace);
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_TRACE_H_