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/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