summaryrefslogtreecommitdiff
path: root/src/instrumentation.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/instrumentation.cc')
-rw-r--r--src/instrumentation.cc573
1 files changed, 0 insertions, 573 deletions
diff --git a/src/instrumentation.cc b/src/instrumentation.cc
deleted file mode 100644
index 8598d6d4f8..0000000000
--- a/src/instrumentation.cc
+++ /dev/null
@@ -1,573 +0,0 @@
-/*
- * 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