summaryrefslogtreecommitdiffstats
path: root/runtime/instrumentation.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/instrumentation.cc')
-rw-r--r--runtime/instrumentation.cc573
1 files changed, 573 insertions, 0 deletions
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
new file mode 100644
index 0000000..8598d6d
--- /dev/null
+++ b/runtime/instrumentation.cc
@@ -0,0 +1,573 @@
+/*
+ * 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 "atomic_integer.h"
+#include "base/unix_file/fd_file.h"
+#include "class_linker.h"
+#include "debugger.h"
+#include "dex_file-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/dex_cache.h"
+#include "mirror/abstract_method-inl.h"
+#include "mirror/object_array-inl.h"
+#include "mirror/object-inl.h"
+#include "nth_caller_visitor.h"
+#if !defined(ART_USE_PORTABLE_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"
+
+namespace art {
+namespace instrumentation {
+
+static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
+ return instrumentation->InstallStubsForClass(klass);
+}
+
+bool Instrumentation::InstallStubsForClass(mirror::Class* klass) {
+ bool uninstall = !entry_exit_stubs_installed_ && !interpreter_stubs_installed_;
+ ClassLinker* class_linker = NULL;
+ if (uninstall) {
+ class_linker = Runtime::Current()->GetClassLinker();
+ }
+ bool is_initialized = klass->IsInitialized();
+ for (size_t i = 0; i < klass->NumDirectMethods(); i++) {
+ mirror::AbstractMethod* method = klass->GetDirectMethod(i);
+ if (!method->IsAbstract()) {
+ const void* new_code;
+ if (uninstall) {
+ if (is_initialized || !method->IsStatic() || method->IsConstructor()) {
+ new_code = class_linker->GetOatCodeFor(method);
+ } else {
+ new_code = GetResolutionTrampoline(class_linker);
+ }
+ } else { // !uninstall
+ if (!interpreter_stubs_installed_ || method->IsNative()) {
+ new_code = GetInstrumentationEntryPoint();
+ } else {
+ new_code = GetInterpreterEntryPoint();
+ }
+ }
+ method->SetEntryPointFromCompiledCode(new_code);
+ }
+ }
+ for (size_t i = 0; i < klass->NumVirtualMethods(); i++) {
+ mirror::AbstractMethod* method = klass->GetVirtualMethod(i);
+ if (!method->IsAbstract()) {
+ const void* new_code;
+ if (uninstall) {
+ new_code = class_linker->GetOatCodeFor(method);
+ } else { // !uninstall
+ if (!interpreter_stubs_installed_ || method->IsNative()) {
+ new_code = GetInstrumentationEntryPoint();
+ } else {
+ new_code = GetInterpreterEntryPoint();
+ }
+ }
+ method->SetEntryPointFromCompiledCode(new_code);
+ }
+ }
+ return true;
+}
+
+// Places the instrumentation exit pc as the return PC for every quick frame. This also allows
+// deoptimization of quick frames to interpreter frames.
+static void InstrumentationInstallStack(Thread* thread, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ struct InstallStackVisitor : public StackVisitor {
+ InstallStackVisitor(Thread* thread, Context* context, uintptr_t instrumentation_exit_pc)
+ : StackVisitor(thread, context), instrumentation_stack_(thread->GetInstrumentationStack()),
+ instrumentation_exit_pc_(instrumentation_exit_pc), last_return_pc_(0) {}
+
+ virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::AbstractMethod* m = GetMethod();
+ if (GetCurrentQuickFrame() == NULL) {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << " Ignoring a shadow frame. Frame " << GetFrameId()
+ << " Method=" << PrettyMethod(m);
+ }
+ return true; // Ignore shadow frames.
+ }
+ if (m == NULL) {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << " Skipping upcall. Frame " << GetFrameId();
+ }
+ last_return_pc_ = 0;
+ return true; // Ignore upcalls.
+ }
+ if (m->IsRuntimeMethod()) {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << " Skipping runtime method. Frame " << GetFrameId();
+ }
+ last_return_pc_ = GetReturnPc();
+ return true; // Ignore unresolved methods since they will be instrumented after resolution.
+ }
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << " Installing exit stub in " << DescribeLocation();
+ }
+ uintptr_t return_pc = GetReturnPc();
+ CHECK_NE(return_pc, instrumentation_exit_pc_);
+ CHECK_NE(return_pc, 0U);
+ InstrumentationStackFrame instrumentation_frame(GetThisObject(), m, return_pc, GetFrameId(),
+ false);
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Pushing frame " << instrumentation_frame.Dump();
+ }
+ instrumentation_stack_->push_back(instrumentation_frame);
+ dex_pcs_.push_back(m->ToDexPc(last_return_pc_));
+ SetReturnPc(instrumentation_exit_pc_);
+ last_return_pc_ = return_pc;
+ return true; // Continue.
+ }
+ std::deque<InstrumentationStackFrame>* const instrumentation_stack_;
+ std::vector<uint32_t> dex_pcs_;
+ const uintptr_t instrumentation_exit_pc_;
+ uintptr_t last_return_pc_;
+ };
+ if (kVerboseInstrumentation) {
+ std::string thread_name;
+ thread->GetThreadName(thread_name);
+ LOG(INFO) << "Installing exit stubs in " << thread_name;
+ }
+ UniquePtr<Context> context(Context::Create());
+ uintptr_t instrumentation_exit_pc = GetInstrumentationExitPc();
+ InstallStackVisitor visitor(thread, context.get(), instrumentation_exit_pc);
+ visitor.WalkStack(true);
+
+ // Create method enter events for all methods current on the thread's stack.
+ Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
+ typedef std::deque<InstrumentationStackFrame>::const_reverse_iterator It;
+ for (It it = thread->GetInstrumentationStack()->rbegin(),
+ end = thread->GetInstrumentationStack()->rend(); it != end; ++it) {
+ mirror::Object* this_object = (*it).this_object_;
+ mirror::AbstractMethod* method = (*it).method_;
+ uint32_t dex_pc = visitor.dex_pcs_.back();
+ visitor.dex_pcs_.pop_back();
+ instrumentation->MethodEnterEvent(thread, this_object, method, dex_pc);
+ }
+ thread->VerifyStack();
+}
+
+// Removes the instrumentation exit pc as the return PC for every quick frame.
+static void InstrumentationRestoreStack(Thread* thread, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ struct RestoreStackVisitor : public StackVisitor {
+ RestoreStackVisitor(Thread* thread, uintptr_t instrumentation_exit_pc,
+ Instrumentation* instrumentation)
+ : StackVisitor(thread, NULL), thread_(thread),
+ instrumentation_exit_pc_(instrumentation_exit_pc),
+ instrumentation_(instrumentation),
+ instrumentation_stack_(thread->GetInstrumentationStack()),
+ frames_removed_(0) {}
+
+ virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (instrumentation_stack_->size() == 0) {
+ return false; // Stop.
+ }
+ mirror::AbstractMethod* m = GetMethod();
+ if (GetCurrentQuickFrame() == NULL) {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << " Ignoring a shadow frame. Frame " << GetFrameId() << " Method=" << PrettyMethod(m);
+ }
+ return true; // Ignore shadow frames.
+ }
+ if (m == NULL) {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << " Skipping upcall. Frame " << GetFrameId();
+ }
+ return true; // Ignore upcalls.
+ }
+ typedef std::deque<instrumentation::InstrumentationStackFrame>::const_iterator It; // TODO: C++0x auto
+ bool removed_stub = false;
+ // TODO: make this search more efficient?
+ for (It it = instrumentation_stack_->begin(), end = instrumentation_stack_->end(); it != end;
+ ++it) {
+ InstrumentationStackFrame instrumentation_frame = *it;
+ if (instrumentation_frame.frame_id_ == GetFrameId()) {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << " Removing exit stub in " << DescribeLocation();
+ }
+ if (instrumentation_frame.interpreter_entry_) {
+ CHECK(m == Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs));
+ } else {
+ CHECK(m == instrumentation_frame.method_) << PrettyMethod(m);
+ }
+ SetReturnPc(instrumentation_frame.return_pc_);
+ // Create the method exit events. As the methods didn't really exit the result is 0.
+ instrumentation_->MethodExitEvent(thread_, instrumentation_frame.this_object_, m,
+ GetDexPc(), JValue());
+ frames_removed_++;
+ removed_stub = true;
+ break;
+ }
+ }
+ if (!removed_stub) {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << " No exit stub in " << DescribeLocation();
+ }
+ }
+ return true; // Continue.
+ }
+ Thread* const thread_;
+ const uintptr_t instrumentation_exit_pc_;
+ Instrumentation* const instrumentation_;
+ std::deque<instrumentation::InstrumentationStackFrame>* const instrumentation_stack_;
+ size_t frames_removed_;
+ };
+ if (kVerboseInstrumentation) {
+ std::string thread_name;
+ thread->GetThreadName(thread_name);
+ LOG(INFO) << "Removing exit stubs in " << thread_name;
+ }
+ std::deque<instrumentation::InstrumentationStackFrame>* stack = thread->GetInstrumentationStack();
+ if (stack->size() > 0) {
+ Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
+ uintptr_t instrumentation_exit_pc = GetInstrumentationExitPc();
+ RestoreStackVisitor visitor(thread, instrumentation_exit_pc, instrumentation);
+ visitor.WalkStack(true);
+ CHECK_EQ(visitor.frames_removed_, stack->size());
+ while (stack->size() > 0) {
+ stack->pop_front();
+ }
+ }
+}
+
+void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t events) {
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+ bool require_entry_exit_stubs = false;
+ bool require_interpreter = false;
+ if ((events & kMethodEntered) != 0) {
+ method_entry_listeners_.push_back(listener);
+ require_entry_exit_stubs = true;
+ have_method_entry_listeners_ = true;
+ }
+ if ((events & kMethodExited) != 0) {
+ method_exit_listeners_.push_back(listener);
+ require_entry_exit_stubs = true;
+ have_method_exit_listeners_ = true;
+ }
+ if ((events & kMethodUnwind) != 0) {
+ method_unwind_listeners_.push_back(listener);
+ have_method_unwind_listeners_ = true;
+ }
+ if ((events & kDexPcMoved) != 0) {
+ dex_pc_listeners_.push_back(listener);
+ require_interpreter = true;
+ have_dex_pc_listeners_ = true;
+ }
+ if ((events & kExceptionCaught) != 0) {
+ exception_caught_listeners_.push_back(listener);
+ have_exception_caught_listeners_ = true;
+ }
+ ConfigureStubs(require_entry_exit_stubs, require_interpreter);
+}
+
+void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t events) {
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+ bool require_entry_exit_stubs = false;
+ bool require_interpreter = false;
+
+ if ((events & kMethodEntered) != 0) {
+ bool contains = std::find(method_entry_listeners_.begin(), method_entry_listeners_.end(),
+ listener) != method_entry_listeners_.end();
+ if (contains) {
+ method_entry_listeners_.remove(listener);
+ }
+ have_method_entry_listeners_ = method_entry_listeners_.size() > 0;
+ require_entry_exit_stubs |= have_method_entry_listeners_;
+ }
+ if ((events & kMethodExited) != 0) {
+ bool contains = std::find(method_exit_listeners_.begin(), method_exit_listeners_.end(),
+ listener) != method_exit_listeners_.end();
+ if (contains) {
+ method_exit_listeners_.remove(listener);
+ }
+ have_method_exit_listeners_ = method_exit_listeners_.size() > 0;
+ require_entry_exit_stubs |= have_method_exit_listeners_;
+ }
+ if ((events & kMethodUnwind) != 0) {
+ method_unwind_listeners_.remove(listener);
+ }
+ if ((events & kDexPcMoved) != 0) {
+ bool contains = std::find(dex_pc_listeners_.begin(), dex_pc_listeners_.end(),
+ listener) != dex_pc_listeners_.end();
+ if (contains) {
+ dex_pc_listeners_.remove(listener);
+ }
+ have_dex_pc_listeners_ = dex_pc_listeners_.size() > 0;
+ require_interpreter |= have_dex_pc_listeners_;
+ }
+ if ((events & kExceptionCaught) != 0) {
+ exception_caught_listeners_.remove(listener);
+ have_exception_caught_listeners_ = exception_caught_listeners_.size() > 0;
+ }
+ ConfigureStubs(require_entry_exit_stubs, require_interpreter);
+}
+
+void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter) {
+ interpret_only_ = require_interpreter || forced_interpret_only_;
+ // Compute what level of instrumentation is required and compare to current.
+ int desired_level, current_level;
+ if (require_interpreter) {
+ desired_level = 2;
+ } else if (require_entry_exit_stubs) {
+ desired_level = 1;
+ } else {
+ desired_level = 0;
+ }
+ if (interpreter_stubs_installed_) {
+ current_level = 2;
+ } else if (entry_exit_stubs_installed_) {
+ current_level = 1;
+ } else {
+ current_level = 0;
+ }
+ if (desired_level == current_level) {
+ // We're already set.
+ return;
+ }
+ Thread* self = Thread::Current();
+ Runtime* runtime = Runtime::Current();
+ Locks::thread_list_lock_->AssertNotHeld(self);
+ if (desired_level > 0) {
+ if (require_interpreter) {
+ interpreter_stubs_installed_ = true;
+ } else {
+ CHECK(require_entry_exit_stubs);
+ entry_exit_stubs_installed_ = true;
+ }
+ runtime->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, this);
+ instrumentation_stubs_installed_ = true;
+ MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ runtime->GetThreadList()->ForEach(InstrumentationInstallStack, this);
+ } else {
+ interpreter_stubs_installed_ = false;
+ entry_exit_stubs_installed_ = false;
+ runtime->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, this);
+ instrumentation_stubs_installed_ = false;
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ Runtime::Current()->GetThreadList()->ForEach(InstrumentationRestoreStack, this);
+ }
+}
+
+void Instrumentation::UpdateMethodsCode(mirror::AbstractMethod* method, const void* code) const {
+ if (LIKELY(!instrumentation_stubs_installed_)) {
+ method->SetEntryPointFromCompiledCode(code);
+ }
+}
+
+const void* Instrumentation::GetQuickCodeFor(const mirror::AbstractMethod* method) const {
+ Runtime* runtime = Runtime::Current();
+ if (LIKELY(!instrumentation_stubs_installed_)) {
+ const void* code = method->GetEntryPointFromCompiledCode();
+ DCHECK(code != NULL);
+ if (LIKELY(code != GetResolutionTrampoline(runtime->GetClassLinker()) &&
+ code != GetInterpreterEntryPoint())) {
+ return code;
+ }
+ }
+ return runtime->GetClassLinker()->GetOatCodeFor(method);
+}
+
+void Instrumentation::MethodEnterEventImpl(Thread* thread, mirror::Object* this_object,
+ const mirror::AbstractMethod* method,
+ uint32_t dex_pc) const {
+ typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto
+ for (It it = method_entry_listeners_.begin(), end = method_entry_listeners_.end(); it != end;
+ ++it) {
+ (*it)->MethodEntered(thread, this_object, method, dex_pc);
+ }
+}
+
+void Instrumentation::MethodExitEventImpl(Thread* thread, mirror::Object* this_object,
+ const mirror::AbstractMethod* method,
+ uint32_t dex_pc, const JValue& return_value) const {
+ typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto
+ for (It it = method_exit_listeners_.begin(), end = method_exit_listeners_.end(); it != end;
+ ++it) {
+ (*it)->MethodExited(thread, this_object, method, dex_pc, return_value);
+ }
+}
+
+void Instrumentation::MethodUnwindEvent(Thread* thread, mirror::Object* this_object,
+ const mirror::AbstractMethod* method,
+ uint32_t dex_pc) const {
+ if (have_method_unwind_listeners_) {
+ typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto
+ for (It it = method_unwind_listeners_.begin(), end = method_unwind_listeners_.end(); it != end;
+ ++it) {
+ (*it)->MethodUnwind(thread, method, dex_pc);
+ }
+ }
+}
+
+void Instrumentation::DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object,
+ const mirror::AbstractMethod* method,
+ uint32_t dex_pc) const {
+ // TODO: STL copy-on-write collection? The copy below is due to the debug listener having an
+ // action where it can remove itself as a listener and break the iterator. The copy only works
+ // around the problem and in general we may have to move to something like reference counting to
+ // ensure listeners are deleted correctly.
+ std::list<InstrumentationListener*> copy(dex_pc_listeners_);
+ typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto
+ for (It it = copy.begin(), end = copy.end(); it != end; ++it) {
+ (*it)->DexPcMoved(thread, this_object, method, dex_pc);
+ }
+}
+
+void Instrumentation::ExceptionCaughtEvent(Thread* thread, const ThrowLocation& throw_location,
+ mirror::AbstractMethod* catch_method,
+ uint32_t catch_dex_pc,
+ mirror::Throwable* exception_object) {
+ if (have_exception_caught_listeners_) {
+ DCHECK_EQ(thread->GetException(NULL), exception_object);
+ thread->ClearException();
+ typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto
+ for (It it = exception_caught_listeners_.begin(), end = exception_caught_listeners_.end();
+ it != end; ++it) {
+ (*it)->ExceptionCaught(thread, throw_location, catch_method, catch_dex_pc, exception_object);
+ }
+ thread->SetException(throw_location, exception_object);
+ }
+}
+
+static void CheckStackDepth(Thread* self, const InstrumentationStackFrame& instrumentation_frame,
+ int delta)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ size_t frame_id = StackVisitor::ComputeNumFrames(self) + delta;
+ if (frame_id != instrumentation_frame.frame_id_) {
+ LOG(ERROR) << "Expected frame_id=" << frame_id << " but found "
+ << instrumentation_frame.frame_id_;
+ StackVisitor::DescribeStack(self);
+ CHECK_EQ(frame_id, instrumentation_frame.frame_id_);
+ }
+}
+
+void Instrumentation::PushInstrumentationStackFrame(Thread* self, mirror::Object* this_object,
+ mirror::AbstractMethod* method,
+ uintptr_t lr, bool interpreter_entry) {
+ // We have a callee-save frame meaning this value is guaranteed to never be 0.
+ size_t frame_id = StackVisitor::ComputeNumFrames(self);
+ std::deque<instrumentation::InstrumentationStackFrame>* stack = self->GetInstrumentationStack();
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Entering " << PrettyMethod(method) << " from PC " << (void*)lr;
+ }
+ instrumentation::InstrumentationStackFrame instrumentation_frame(this_object, method, lr,
+ frame_id, interpreter_entry);
+ stack->push_front(instrumentation_frame);
+
+ MethodEnterEvent(self, this_object, method, 0);
+}
+
+uint64_t Instrumentation::PopInstrumentationStackFrame(Thread* self, uintptr_t* return_pc,
+ uint64_t gpr_result, uint64_t fpr_result) {
+ // Do the pop.
+ std::deque<instrumentation::InstrumentationStackFrame>* stack = self->GetInstrumentationStack();
+ CHECK_GT(stack->size(), 0U);
+ InstrumentationStackFrame instrumentation_frame = stack->front();
+ stack->pop_front();
+
+ // Set return PC and check the sanity of the stack.
+ *return_pc = instrumentation_frame.return_pc_;
+ CheckStackDepth(self, instrumentation_frame, 0);
+
+ mirror::AbstractMethod* method = instrumentation_frame.method_;
+ char return_shorty = MethodHelper(method).GetShorty()[0];
+ JValue return_value;
+ if (return_shorty == 'V') {
+ return_value.SetJ(0);
+ } else if (return_shorty == 'F' || return_shorty == 'D') {
+ return_value.SetJ(fpr_result);
+ } else {
+ return_value.SetJ(gpr_result);
+ }
+ // TODO: improve the dex pc information here, requires knowledge of current PC as opposed to
+ // return_pc.
+ uint32_t dex_pc = DexFile::kDexNoIndex;
+ mirror::Object* this_object = instrumentation_frame.this_object_;
+ MethodExitEvent(self, this_object, instrumentation_frame.method_, dex_pc, return_value);
+
+ bool deoptimize = false;
+ if (interpreter_stubs_installed_) {
+ // Deoptimize unless we're returning to an upcall.
+ NthCallerVisitor visitor(self, 1, true);
+ visitor.WalkStack(true);
+ deoptimize = visitor.caller != NULL;
+ if (deoptimize && kVerboseInstrumentation) {
+ LOG(INFO) << "Deoptimizing into " << PrettyMethod(visitor.caller);
+ }
+ }
+ if (deoptimize) {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Deoptimizing from " << PrettyMethod(method)
+ << " result is " << std::hex << return_value.GetJ();
+ }
+ self->SetDeoptimizationReturnValue(return_value);
+ return static_cast<uint64_t>(GetDeoptimizationEntryPoint()) |
+ (static_cast<uint64_t>(*return_pc) << 32);
+ } else {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Returning from " << PrettyMethod(method) << " to PC " << (void*)(*return_pc);
+ }
+ return *return_pc;
+ }
+}
+
+void Instrumentation::PopMethodForUnwind(Thread* self, bool is_deoptimization) const {
+ // Do the pop.
+ std::deque<instrumentation::InstrumentationStackFrame>* stack = self->GetInstrumentationStack();
+ CHECK_GT(stack->size(), 0U);
+ InstrumentationStackFrame instrumentation_frame = stack->front();
+ // TODO: bring back CheckStackDepth(self, instrumentation_frame, 2);
+ stack->pop_front();
+
+ mirror::AbstractMethod* method = instrumentation_frame.method_;
+ if (is_deoptimization) {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Popping for deoptimization " << PrettyMethod(method);
+ }
+ } else {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Popping for unwind " << PrettyMethod(method);
+ }
+
+ // Notify listeners of method unwind.
+ // TODO: improve the dex pc information here, requires knowledge of current PC as opposed to
+ // return_pc.
+ uint32_t dex_pc = DexFile::kDexNoIndex;
+ MethodUnwindEvent(self, instrumentation_frame.this_object_, method, dex_pc);
+ }
+}
+
+std::string InstrumentationStackFrame::Dump() const {
+ std::ostringstream os;
+ os << "Frame " << frame_id_ << " " << PrettyMethod(method_) << ":"
+ << reinterpret_cast<void*>(return_pc_) << " this=" << reinterpret_cast<void*>(this_object_);
+ return os.str();
+}
+
+} // namespace instrumentation
+} // namespace art