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_