| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "instrumentation.h" |
| |
| #include <sys/uio.h> |
| |
| #include "base/unix_file/fd_file.h" |
| #include "class_linker.h" |
| #include "debugger.h" |
| #include "dex_cache.h" |
| #if !defined(ART_USE_LLVM_COMPILER) |
| #include "oat/runtime/oat_support_entrypoints.h" |
| #endif |
| #include "object_utils.h" |
| #include "os.h" |
| #include "scoped_thread_state_change.h" |
| #include "thread.h" |
| #include "thread_list.h" |
| #include "trace.h" |
| |
| namespace art { |
| |
| static bool InstallStubsClassVisitor(Class* klass, void*) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); |
| for (size_t i = 0; i < klass->NumDirectMethods(); i++) { |
| AbstractMethod* method = klass->GetDirectMethod(i); |
| if (instrumentation->GetSavedCodeFromMap(method) == NULL) { |
| instrumentation->SaveAndUpdateCode(method); |
| } |
| } |
| |
| for (size_t i = 0; i < klass->NumVirtualMethods(); i++) { |
| AbstractMethod* method = klass->GetVirtualMethod(i); |
| if (instrumentation->GetSavedCodeFromMap(method) == NULL) { |
| instrumentation->SaveAndUpdateCode(method); |
| } |
| } |
| return true; |
| } |
| |
| static bool UninstallStubsClassVisitor(Class* klass, void*) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); |
| for (size_t i = 0; i < klass->NumDirectMethods(); i++) { |
| AbstractMethod* method = klass->GetDirectMethod(i); |
| if (instrumentation->GetSavedCodeFromMap(method) != NULL) { |
| instrumentation->ResetSavedCode(method); |
| } |
| } |
| |
| for (size_t i = 0; i < klass->NumVirtualMethods(); i++) { |
| AbstractMethod* method = klass->GetVirtualMethod(i); |
| if (instrumentation->GetSavedCodeFromMap(method) != NULL) { |
| instrumentation->ResetSavedCode(method); |
| } |
| } |
| return true; |
| } |
| |
| void InstrumentationInstallStack(Thread* self, void* arg) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| struct InstallStackVisitor : public StackVisitor { |
| InstallStackVisitor(Thread* self, uintptr_t instrumentation_exit_pc) |
| : StackVisitor(self->GetManagedStack(), self->GetInstrumentationStack(), NULL), |
| self_(self), instrumentation_exit_pc_(instrumentation_exit_pc) {} |
| |
| virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| if (GetCurrentQuickFrame() == NULL) { |
| return true; // Ignore shadow frames. |
| } |
| AbstractMethod* m = GetMethod(); |
| if (m == NULL) { |
| return true; // Ignore upcalls. |
| } |
| if (m->GetDexMethodIndex() == DexFile::kDexNoIndex16) { |
| return true; // Ignore unresolved methods since they will be instrumented after resolution. |
| } |
| uintptr_t pc = GetReturnPc(); |
| InstrumentationStackFrame instrumentation_frame(m, pc, GetFrameId()); |
| self_->PushBackInstrumentationStackFrame(instrumentation_frame); |
| SetReturnPc(instrumentation_exit_pc_); |
| return true; // Continue. |
| } |
| Thread* const self_; |
| const uintptr_t instrumentation_exit_pc_; |
| }; |
| uintptr_t instrumentation_exit_pc = GetInstrumentationExitPc(); |
| InstallStackVisitor visitor(self, instrumentation_exit_pc); |
| visitor.WalkStack(true); |
| Trace* trace = reinterpret_cast<Trace*>(arg); |
| if (trace != NULL) { |
| std::deque<InstrumentationStackFrame>::const_reverse_iterator it = |
| self->GetInstrumentationStack()->rbegin(); |
| std::deque<InstrumentationStackFrame>::const_reverse_iterator end = |
| self->GetInstrumentationStack()->rend(); |
| for (; it != end; ++it) { |
| trace->LogMethodTraceEvent(self, (*it).method_, Trace::kMethodTraceEnter); |
| } |
| } |
| } |
| |
| static void InstrumentationRestoreStack(Thread* self, void*) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| struct RestoreStackVisitor : public StackVisitor { |
| RestoreStackVisitor(Thread* self, uintptr_t instrumentation_exit_pc) |
| : StackVisitor(self->GetManagedStack(), self->GetInstrumentationStack(), NULL), |
| self_(self), instrumentation_exit_pc_(instrumentation_exit_pc) {} |
| |
| virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| if (self_->IsInstrumentationStackEmpty()) { |
| return false; // Stop. |
| } |
| AbstractMethod* m = GetMethod(); |
| if (m == NULL) { |
| return true; // Ignore upcalls. |
| } |
| uintptr_t pc = GetReturnPc(); |
| if (pc == instrumentation_exit_pc_) { |
| InstrumentationStackFrame instrumentation_frame = self_->PopInstrumentationStackFrame(); |
| SetReturnPc(instrumentation_frame.return_pc_); |
| CHECK(m == instrumentation_frame.method_); |
| CHECK_EQ(GetFrameId(), instrumentation_frame.frame_id_); |
| Runtime* runtime = Runtime::Current(); |
| if (runtime->IsMethodTracingActive()) { |
| Trace* trace = runtime->GetInstrumentation()->GetTrace(); |
| trace->LogMethodTraceEvent(self_, m, Trace::kMethodTraceExit); |
| } |
| } |
| return true; // Continue. |
| } |
| Thread* const self_; |
| const uintptr_t instrumentation_exit_pc_; |
| }; |
| uintptr_t instrumentation_exit_pc = GetInstrumentationExitPc(); |
| RestoreStackVisitor visitor(self, instrumentation_exit_pc); |
| visitor.WalkStack(true); |
| } |
| |
| Instrumentation::~Instrumentation() { |
| delete trace_; |
| } |
| |
| void Instrumentation::InstallStubs() { |
| Thread* self = Thread::Current(); |
| Locks::thread_list_lock_->AssertNotHeld(self); |
| Runtime::Current()->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, NULL); |
| MutexLock mu(self, *Locks::thread_list_lock_); |
| Runtime::Current()->GetThreadList()->ForEach(InstrumentationInstallStack, GetTrace()); |
| } |
| |
| void Instrumentation::UninstallStubs() { |
| Thread* self = Thread::Current(); |
| Locks::thread_list_lock_->AssertNotHeld(self); |
| Runtime::Current()->GetClassLinker()->VisitClasses(UninstallStubsClassVisitor, NULL); |
| MutexLock mu(self, *Locks::thread_list_lock_); |
| Runtime::Current()->GetThreadList()->ForEach(InstrumentationRestoreStack, NULL); |
| } |
| |
| void Instrumentation::AddSavedCodeToMap(const AbstractMethod* method, const void* code) { |
| saved_code_map_.Put(method, code); |
| } |
| |
| void Instrumentation::RemoveSavedCodeFromMap(const AbstractMethod* method) { |
| saved_code_map_.erase(method); |
| } |
| |
| const void* Instrumentation::GetSavedCodeFromMap(const AbstractMethod* method) { |
| typedef SafeMap<const AbstractMethod*, const void*>::const_iterator It; // TODO: C++0x auto |
| It it = saved_code_map_.find(method); |
| if (it == saved_code_map_.end()) { |
| return NULL; |
| } else { |
| return it->second; |
| } |
| } |
| |
| void Instrumentation::SaveAndUpdateCode(AbstractMethod* method) { |
| #if defined(ART_USE_LLVM_COMPILER) |
| UNUSED(method); |
| UNIMPLEMENTED(FATAL); |
| #else |
| void* instrumentation_stub = GetInstrumentationEntryPoint(); |
| CHECK(GetSavedCodeFromMap(method) == NULL); |
| AddSavedCodeToMap(method, method->GetCode()); |
| method->SetCode(instrumentation_stub); |
| #endif |
| } |
| |
| void Instrumentation::ResetSavedCode(AbstractMethod* method) { |
| CHECK(GetSavedCodeFromMap(method) != NULL); |
| method->SetCode(GetSavedCodeFromMap(method)); |
| RemoveSavedCodeFromMap(method); |
| } |
| |
| Trace* Instrumentation::GetTrace() const { |
| return trace_; |
| } |
| |
| void Instrumentation::SetTrace(Trace* trace) { |
| trace_ = trace; |
| } |
| |
| void Instrumentation::RemoveTrace() { |
| delete trace_; |
| trace_ = NULL; |
| } |
| |
| uint32_t InstrumentationMethodUnwindFromCode(Thread* self) { |
| Trace* trace = Runtime::Current()->GetInstrumentation()->GetTrace(); |
| InstrumentationStackFrame instrumentation_frame = self->PopInstrumentationStackFrame(); |
| AbstractMethod* method = instrumentation_frame.method_; |
| uint32_t lr = instrumentation_frame.return_pc_; |
| |
| trace->LogMethodTraceEvent(self, method, Trace::kMethodTraceUnwind); |
| |
| return lr; |
| } |
| |
| } // namespace art |