diff options
| -rw-r--r-- | openjdkjvmti/ti_stack.cc | 90 | ||||
| -rw-r--r-- | runtime/debugger.cc | 528 | ||||
| -rw-r--r-- | runtime/gc/allocation_record.cc | 47 | ||||
| -rw-r--r-- | runtime/instrumentation.cc | 121 | ||||
| -rw-r--r-- | runtime/jit/jit_code_cache.cc | 48 | ||||
| -rw-r--r-- | runtime/monitor.cc | 71 | ||||
| -rw-r--r-- | runtime/quick_exception_handler.cc | 159 | ||||
| -rw-r--r-- | runtime/stack.h | 30 | ||||
| -rw-r--r-- | runtime/thread.cc | 52 | ||||
| -rw-r--r-- | runtime/trace.cc | 42 | ||||
| -rw-r--r-- | test/461-get-reference-vreg/get_reference_vreg_jni.cc | 86 | ||||
| -rw-r--r-- | test/543-env-long-ref/env_long_ref.cc | 54 | ||||
| -rw-r--r-- | test/570-checker-osr/osr.cc | 167 | ||||
| -rw-r--r-- | test/common/stack_inspect.cc | 105 |
14 files changed, 921 insertions, 679 deletions
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index 4a3eac8a15..5de4a81f5e 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -673,24 +673,34 @@ jvmtiError StackUtil::GetThreadListStackTraces(jvmtiEnv* env, return ERR(NONE); } +// Walks up the stack counting Java frames. This is not StackVisitor::ComputeNumFrames, as +// runtime methods and transitions must not be counted. +struct GetFrameCountVisitor : public art::StackVisitor { + explicit GetFrameCountVisitor(art::Thread* thread) + : art::StackVisitor(thread, nullptr, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames), + count(0) {} + + bool VisitFrame() override REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::ArtMethod* m = GetMethod(); + const bool do_count = !(m == nullptr || m->IsRuntimeMethod()); + if (do_count) { + count++; + } + return true; + } + + size_t count; +}; + struct GetFrameCountClosure : public art::Closure { public: GetFrameCountClosure() : count(0) {} void Run(art::Thread* self) override REQUIRES_SHARED(art::Locks::mutator_lock_) { - // This is not StackVisitor::ComputeNumFrames, as runtime methods and transitions must not be - // counted. - art::StackVisitor::WalkStack( - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(art::Locks::mutator_lock_) { - art::ArtMethod* m = stack_visitor->GetMethod(); - if (m != nullptr && !m->IsRuntimeMethod()) { - count++; - } - return true; - }, - self, - /* context= */ nullptr, - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); + GetFrameCountVisitor visitor(self); + visitor.WalkStack(false); + + count = visitor.count; } size_t count; @@ -733,30 +743,46 @@ jvmtiError StackUtil::GetFrameCount(jvmtiEnv* env ATTRIBUTE_UNUSED, return ERR(NONE); } +// Walks up the stack 'n' callers, when used with Thread::WalkStack. +struct GetLocationVisitor : public art::StackVisitor { + GetLocationVisitor(art::Thread* thread, size_t n_in) + : art::StackVisitor(thread, nullptr, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames), + n(n_in), + count(0), + caller(nullptr), + caller_dex_pc(0) {} + + bool VisitFrame() override REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::ArtMethod* m = GetMethod(); + const bool do_count = !(m == nullptr || m->IsRuntimeMethod()); + if (do_count) { + DCHECK(caller == nullptr); + if (count == n) { + caller = m; + caller_dex_pc = GetDexPc(false); + return false; + } + count++; + } + return true; + } + + const size_t n; + size_t count; + art::ArtMethod* caller; + uint32_t caller_dex_pc; +}; + struct GetLocationClosure : public art::Closure { public: explicit GetLocationClosure(size_t n_in) : n(n_in), method(nullptr), dex_pc(0) {} void Run(art::Thread* self) override REQUIRES_SHARED(art::Locks::mutator_lock_) { - // Walks up the stack 'n' callers. - size_t count = 0u; - art::StackVisitor::WalkStack( - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(art::Locks::mutator_lock_) { - art::ArtMethod* m = stack_visitor->GetMethod(); - if (m != nullptr && !m->IsRuntimeMethod()) { - DCHECK(method == nullptr); - if (count == n) { - method = m; - dex_pc = stack_visitor->GetDexPc(/*abort_on_failure=*/false); - return false; - } - count++; - } - return true; - }, - self, - /* context= */ nullptr, - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); + GetLocationVisitor visitor(self, n); + visitor.WalkStack(false); + + method = visitor.caller; + dex_pc = visitor.caller_dex_pc; } const size_t n; diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 39980f000a..3ad7fc92a2 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -2362,18 +2362,25 @@ void Dbg::GetThreads(mirror::Object* thread_group, std::vector<JDWP::ObjectId>* } static int GetStackDepth(Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_) { - size_t depth = 0u; - StackVisitor::WalkStack( - [&depth](const StackVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - if (!visitor->GetMethod()->IsRuntimeMethod()) { - ++depth; - } - return true; - }, - thread, - /* context= */ nullptr, - StackVisitor::StackWalkKind::kIncludeInlinedFrames); - return depth; + struct CountStackDepthVisitor : public StackVisitor { + explicit CountStackDepthVisitor(Thread* thread_in) + : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + depth(0) {} + + // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses + // annotalysis. + bool VisitFrame() override NO_THREAD_SAFETY_ANALYSIS { + if (!GetMethod()->IsRuntimeMethod()) { + ++depth; + } + return true; + } + size_t depth; + }; + + CountStackDepthVisitor visitor(thread); + visitor.WalkStack(); + return visitor.depth; } JDWP::JdwpError Dbg::GetThreadFrameCount(JDWP::ObjectId thread_id, size_t* result) { @@ -2391,10 +2398,47 @@ JDWP::JdwpError Dbg::GetThreadFrameCount(JDWP::ObjectId thread_id, size_t* resul return JDWP::ERR_NONE; } -JDWP::JdwpError Dbg::GetThreadFrames(JDWP::ObjectId thread_id, - const size_t start_frame, - const size_t frame_count, - JDWP::ExpandBuf* buf) { +JDWP::JdwpError Dbg::GetThreadFrames(JDWP::ObjectId thread_id, size_t start_frame, + size_t frame_count, JDWP::ExpandBuf* buf) { + class GetFrameVisitor : public StackVisitor { + public: + GetFrameVisitor(Thread* thread, size_t start_frame_in, size_t frame_count_in, + JDWP::ExpandBuf* buf_in) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + depth_(0), + start_frame_(start_frame_in), + frame_count_(frame_count_in), + buf_(buf_in) { + expandBufAdd4BE(buf_, frame_count_); + } + + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + if (GetMethod()->IsRuntimeMethod()) { + return true; // The debugger can't do anything useful with a frame that has no Method*. + } + if (depth_ >= start_frame_ + frame_count_) { + return false; + } + if (depth_ >= start_frame_) { + JDWP::FrameId frame_id(GetFrameId()); + JDWP::JdwpLocation location; + SetJdwpLocation(&location, GetMethod(), GetDexPc()); + VLOG(jdwp) << StringPrintf(" Frame %3zd: id=%3" PRIu64 " ", depth_, frame_id) << location; + expandBufAdd8BE(buf_, frame_id); + expandBufAddLocation(buf_, location); + } + ++depth_; + return true; + } + + private: + size_t depth_; + const size_t start_frame_; + const size_t frame_count_; + JDWP::ExpandBuf* buf_; + }; + ScopedObjectAccessUnchecked soa(Thread::Current()); JDWP::JdwpError error; Thread* thread = DecodeThread(soa, thread_id, &error); @@ -2404,32 +2448,8 @@ JDWP::JdwpError Dbg::GetThreadFrames(JDWP::ObjectId thread_id, if (!IsSuspendedForDebugger(soa, thread)) { return JDWP::ERR_THREAD_NOT_SUSPENDED; } - - size_t depth = 0u; - StackVisitor::WalkStack( - [&](StackVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - if (visitor->GetMethod()->IsRuntimeMethod()) { - return true; // The debugger can't do anything useful with a frame that has no Method*. - } - if (depth >= start_frame + frame_count) { - return false; - } - if (depth >= start_frame) { - JDWP::FrameId frame_id(visitor->GetFrameId()); - JDWP::JdwpLocation location; - SetJdwpLocation(&location, visitor->GetMethod(), visitor->GetDexPc()); - VLOG(jdwp) - << StringPrintf(" Frame %3zd: id=%3" PRIu64 " ", depth, frame_id) << location; - expandBufAdd8BE(buf, frame_id); - expandBufAddLocation(buf, location); - } - ++depth; - return true; - }, - thread, - /* context= */ nullptr, - StackVisitor::StackWalkKind::kIncludeInlinedFrames); - + GetFrameVisitor visitor(thread, start_frame, frame_count, buf); + visitor.WalkStack(); return JDWP::ERR_NONE; } @@ -2510,6 +2530,28 @@ void Dbg::SuspendSelf() { Runtime::Current()->GetThreadList()->SuspendSelfForDebugger(); } +struct GetThisVisitor : public StackVisitor { + GetThisVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id_in) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + this_object(nullptr), + frame_id(frame_id_in) {} + + // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses + // annotalysis. + bool VisitFrame() override NO_THREAD_SAFETY_ANALYSIS { + if (frame_id != GetFrameId()) { + return true; // continue + } else { + this_object = GetThisObject(); + return false; + } + } + + mirror::Object* this_object; + JDWP::FrameId frame_id; +}; + JDWP::JdwpError Dbg::GetThisObject(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, JDWP::ObjectId* result) { ScopedObjectAccessUnchecked soa(Thread::Current()); @@ -2522,50 +2564,48 @@ JDWP::JdwpError Dbg::GetThisObject(JDWP::ObjectId thread_id, JDWP::FrameId frame return JDWP::ERR_THREAD_NOT_SUSPENDED; } std::unique_ptr<Context> context(Context::Create()); - mirror::Object* this_object = nullptr; - StackVisitor::WalkStack( - [&](art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - if (frame_id != stack_visitor->GetFrameId()) { - return true; // continue - } else { - this_object = stack_visitor->GetThisObject(); - return false; - } - }, - thread, - context.get(), - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); - *result = gRegistry->Add(this_object); + GetThisVisitor visitor(thread, context.get(), frame_id); + visitor.WalkStack(); + *result = gRegistry->Add(visitor.this_object); return JDWP::ERR_NONE; } -template <typename FrameHandler> -static JDWP::JdwpError FindAndHandleNonNativeFrame(Thread* thread, - JDWP::FrameId frame_id, - const FrameHandler& handler) - REQUIRES_SHARED(Locks::mutator_lock_) { - JDWP::JdwpError result = JDWP::ERR_INVALID_FRAMEID; - std::unique_ptr<Context> context(Context::Create()); - StackVisitor::WalkStack( - [&](art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - if (stack_visitor->GetFrameId() != frame_id) { - return true; // Not our frame, carry on. - } - ArtMethod* m = stack_visitor->GetMethod(); - if (m->IsNative()) { - // We can't read/write local value from/into native method. - result = JDWP::ERR_OPAQUE_FRAME; - } else { - // We found our frame. - result = handler(stack_visitor); - } - return false; - }, - thread, - context.get(), - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); - return result; -} +// Walks the stack until we find the frame with the given FrameId. +class FindFrameVisitor final : public StackVisitor { + public: + FindFrameVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + frame_id_(frame_id), + error_(JDWP::ERR_INVALID_FRAMEID) {} + + // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses + // annotalysis. + bool VisitFrame() override NO_THREAD_SAFETY_ANALYSIS { + if (GetFrameId() != frame_id_) { + return true; // Not our frame, carry on. + } + ArtMethod* m = GetMethod(); + if (m->IsNative()) { + // We can't read/write local value from/into native method. + error_ = JDWP::ERR_OPAQUE_FRAME; + } else { + // We found our frame. + error_ = JDWP::ERR_NONE; + } + return false; + } + + JDWP::JdwpError GetError() const { + return error_; + } + + private: + const JDWP::FrameId frame_id_; + JDWP::JdwpError error_; + + DISALLOW_COPY_AND_ASSIGN(FindFrameVisitor); +}; JDWP::JdwpError Dbg::GetLocalValues(JDWP::Request* request, JDWP::ExpandBuf* pReply) { JDWP::ObjectId thread_id = request->ReadThreadId(); @@ -2580,29 +2620,31 @@ JDWP::JdwpError Dbg::GetLocalValues(JDWP::Request* request, JDWP::ExpandBuf* pRe if (!IsSuspendedForDebugger(soa, thread)) { return JDWP::ERR_THREAD_NOT_SUSPENDED; } + // Find the frame with the given frame_id. + std::unique_ptr<Context> context(Context::Create()); + FindFrameVisitor visitor(thread, context.get(), frame_id); + visitor.WalkStack(); + if (visitor.GetError() != JDWP::ERR_NONE) { + return visitor.GetError(); + } - return FindAndHandleNonNativeFrame( - thread, - frame_id, - [&](art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - // Read the values from visitor's context. - int32_t slot_count = request->ReadSigned32("slot count"); - expandBufAdd4BE(pReply, slot_count); /* "int values" */ - for (int32_t i = 0; i < slot_count; ++i) { - uint32_t slot = request->ReadUnsigned32("slot"); - JDWP::JdwpTag reqSigByte = request->ReadTag(); + // Read the values from visitor's context. + int32_t slot_count = request->ReadSigned32("slot count"); + expandBufAdd4BE(pReply, slot_count); /* "int values" */ + for (int32_t i = 0; i < slot_count; ++i) { + uint32_t slot = request->ReadUnsigned32("slot"); + JDWP::JdwpTag reqSigByte = request->ReadTag(); - VLOG(jdwp) << " --> slot " << slot << " " << reqSigByte; + VLOG(jdwp) << " --> slot " << slot << " " << reqSigByte; - size_t width = Dbg::GetTagWidth(reqSigByte); - uint8_t* ptr = expandBufAddSpace(pReply, width + 1); - error = Dbg::GetLocalValue(*stack_visitor, soa, slot, reqSigByte, ptr, width); - if (error != JDWP::ERR_NONE) { - return error; - } - } - return JDWP::ERR_NONE; - }); + size_t width = Dbg::GetTagWidth(reqSigByte); + uint8_t* ptr = expandBufAddSpace(pReply, width + 1); + error = Dbg::GetLocalValue(visitor, soa, slot, reqSigByte, ptr, width); + if (error != JDWP::ERR_NONE) { + return error; + } + } + return JDWP::ERR_NONE; } constexpr JDWP::JdwpError kStackFrameLocalAccessError = JDWP::ERR_ABSENT_INFORMATION; @@ -2749,27 +2791,29 @@ JDWP::JdwpError Dbg::SetLocalValues(JDWP::Request* request) { if (!IsSuspendedForDebugger(soa, thread)) { return JDWP::ERR_THREAD_NOT_SUSPENDED; } + // Find the frame with the given frame_id. + std::unique_ptr<Context> context(Context::Create()); + FindFrameVisitor visitor(thread, context.get(), frame_id); + visitor.WalkStack(); + if (visitor.GetError() != JDWP::ERR_NONE) { + return visitor.GetError(); + } - return FindAndHandleNonNativeFrame( - thread, - frame_id, - [&](art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - // Writes the values into visitor's context. - int32_t slot_count = request->ReadSigned32("slot count"); - for (int32_t i = 0; i < slot_count; ++i) { - uint32_t slot = request->ReadUnsigned32("slot"); - JDWP::JdwpTag sigByte = request->ReadTag(); - size_t width = Dbg::GetTagWidth(sigByte); - uint64_t value = request->ReadValue(width); - - VLOG(jdwp) << " --> slot " << slot << " " << sigByte << " " << value; - error = Dbg::SetLocalValue(thread, *stack_visitor, slot, sigByte, value, width); - if (error != JDWP::ERR_NONE) { - return error; - } - } - return JDWP::ERR_NONE; - }); + // Writes the values into visitor's context. + int32_t slot_count = request->ReadSigned32("slot count"); + for (int32_t i = 0; i < slot_count; ++i) { + uint32_t slot = request->ReadUnsigned32("slot"); + JDWP::JdwpTag sigByte = request->ReadTag(); + size_t width = Dbg::GetTagWidth(sigByte); + uint64_t value = request->ReadValue(width); + + VLOG(jdwp) << " --> slot " << slot << " " << sigByte << " " << value; + error = Dbg::SetLocalValue(thread, visitor, slot, sigByte, value, width); + if (error != JDWP::ERR_NONE) { + return error; + } + } + return JDWP::ERR_NONE; } template<typename T> @@ -2941,71 +2985,107 @@ void Dbg::PostFieldModificationEvent(ArtMethod* m, int dex_pc, gJdwpState->PostFieldEvent(&location, f, this_object, field_value, true); } +/** + * Finds the location where this exception will be caught. We search until we reach the top + * frame, in which case this exception is considered uncaught. + */ +class CatchLocationFinder : public StackVisitor { + public: + CatchLocationFinder(Thread* self, const Handle<mirror::Throwable>& exception, Context* context) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(self, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + exception_(exception), + handle_scope_(self), + this_at_throw_(handle_scope_.NewHandle<mirror::Object>(nullptr)), + catch_method_(nullptr), + throw_method_(nullptr), + catch_dex_pc_(dex::kDexNoIndex), + throw_dex_pc_(dex::kDexNoIndex) { + } + + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* method = GetMethod(); + DCHECK(method != nullptr); + if (method->IsRuntimeMethod()) { + // Ignore callee save method. + DCHECK(method->IsCalleeSaveMethod()); + return true; + } + + uint32_t dex_pc = GetDexPc(); + if (throw_method_ == nullptr) { + // First Java method found. It is either the method that threw the exception, + // or the Java native method that is reporting an exception thrown by + // native code. + this_at_throw_.Assign(GetThisObject()); + throw_method_ = method; + throw_dex_pc_ = dex_pc; + } + + if (dex_pc != dex::kDexNoIndex) { + StackHandleScope<1> hs(GetThread()); + uint32_t found_dex_pc; + Handle<mirror::Class> exception_class(hs.NewHandle(exception_->GetClass())); + bool unused_clear_exception; + found_dex_pc = method->FindCatchBlock(exception_class, dex_pc, &unused_clear_exception); + if (found_dex_pc != dex::kDexNoIndex) { + catch_method_ = method; + catch_dex_pc_ = found_dex_pc; + return false; // End stack walk. + } + } + return true; // Continue stack walk. + } + + ArtMethod* GetCatchMethod() REQUIRES_SHARED(Locks::mutator_lock_) { + return catch_method_; + } + + ArtMethod* GetThrowMethod() REQUIRES_SHARED(Locks::mutator_lock_) { + return throw_method_; + } + + mirror::Object* GetThisAtThrow() REQUIRES_SHARED(Locks::mutator_lock_) { + return this_at_throw_.Get(); + } + + uint32_t GetCatchDexPc() const { + return catch_dex_pc_; + } + + uint32_t GetThrowDexPc() const { + return throw_dex_pc_; + } + + private: + const Handle<mirror::Throwable>& exception_; + StackHandleScope<1> handle_scope_; + MutableHandle<mirror::Object> this_at_throw_; + ArtMethod* catch_method_; + ArtMethod* throw_method_; + uint32_t catch_dex_pc_; + uint32_t throw_dex_pc_; + + DISALLOW_COPY_AND_ASSIGN(CatchLocationFinder); +}; + void Dbg::PostException(mirror::Throwable* exception_object) { if (!IsDebuggerActive()) { return; } Thread* const self = Thread::Current(); - StackHandleScope<2> handle_scope(self); + StackHandleScope<1> handle_scope(self); Handle<mirror::Throwable> h_exception(handle_scope.NewHandle(exception_object)); - MutableHandle<mirror::Object> this_at_throw = handle_scope.NewHandle<mirror::Object>(nullptr); std::unique_ptr<Context> context(Context::Create()); - - ArtMethod* catch_method = nullptr; - ArtMethod* throw_method = nullptr; - uint32_t catch_dex_pc = dex::kDexNoIndex; - uint32_t throw_dex_pc = dex::kDexNoIndex; - StackVisitor::WalkStack( - /** - * Finds the location where this exception will be caught. We search until we reach the top - * frame, in which case this exception is considered uncaught. - */ - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* method = stack_visitor->GetMethod(); - DCHECK(method != nullptr); - if (method->IsRuntimeMethod()) { - // Ignore callee save method. - DCHECK(method->IsCalleeSaveMethod()); - return true; - } - - uint32_t dex_pc = stack_visitor->GetDexPc(); - if (throw_method == nullptr) { - // First Java method found. It is either the method that threw the exception, - // or the Java native method that is reporting an exception thrown by - // native code. - this_at_throw.Assign(stack_visitor->GetThisObject()); - throw_method = method; - throw_dex_pc = dex_pc; - } - - if (dex_pc != dex::kDexNoIndex) { - StackHandleScope<1> hs(stack_visitor->GetThread()); - uint32_t found_dex_pc; - Handle<mirror::Class> exception_class(hs.NewHandle(h_exception->GetClass())); - bool unused_clear_exception; - found_dex_pc = method->FindCatchBlock(exception_class, dex_pc, &unused_clear_exception); - if (found_dex_pc != dex::kDexNoIndex) { - catch_method = method; - catch_dex_pc = found_dex_pc; - return false; // End stack walk. - } - } - return true; // Continue stack walk. - }, - self, - context.get(), - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); - + CatchLocationFinder clf(self, h_exception, context.get()); + clf.WalkStack(/* include_transitions= */ false); JDWP::EventLocation exception_throw_location; - SetEventLocation(&exception_throw_location, throw_method, throw_dex_pc); + SetEventLocation(&exception_throw_location, clf.GetThrowMethod(), clf.GetThrowDexPc()); JDWP::EventLocation exception_catch_location; - SetEventLocation(&exception_catch_location, catch_method, catch_dex_pc); + SetEventLocation(&exception_catch_location, clf.GetCatchMethod(), clf.GetCatchDexPc()); - gJdwpState->PostException(&exception_throw_location, - h_exception.Get(), - &exception_catch_location, - this_at_throw.Get()); + gJdwpState->PostException(&exception_throw_location, h_exception.Get(), &exception_catch_location, + clf.GetThisAtThrow()); } void Dbg::PostClassPrepare(mirror::Class* c) { @@ -3569,6 +3649,56 @@ bool Dbg::IsForcedInterpreterNeededForUpcallImpl(Thread* thread, ArtMethod* m) { return instrumentation->IsDeoptimized(m); } +class NeedsDeoptimizationVisitor : public StackVisitor { + public: + explicit NeedsDeoptimizationVisitor(Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(self, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + needs_deoptimization_(false) {} + + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + // The visitor is meant to be used when handling exception from compiled code only. + CHECK(!IsShadowFrame()) << "We only expect to visit compiled frame: " + << ArtMethod::PrettyMethod(GetMethod()); + ArtMethod* method = GetMethod(); + if (method == nullptr) { + // We reach an upcall and don't need to deoptimize this part of the stack (ManagedFragment) + // so we can stop the visit. + DCHECK(!needs_deoptimization_); + return false; + } + if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) { + // We found a compiled frame in the stack but instrumentation is set to interpret + // everything: we need to deoptimize. + needs_deoptimization_ = true; + return false; + } + if (Runtime::Current()->GetInstrumentation()->IsDeoptimized(method)) { + // We found a deoptimized method in the stack. + needs_deoptimization_ = true; + return false; + } + ShadowFrame* frame = GetThread()->FindDebuggerShadowFrame(GetFrameId()); + if (frame != nullptr) { + // The debugger allocated a ShadowFrame to update a variable in the stack: we need to + // deoptimize the stack to execute (and deallocate) this frame. + needs_deoptimization_ = true; + return false; + } + return true; + } + + bool NeedsDeoptimization() const { + return needs_deoptimization_; + } + + private: + // Do we need to deoptimize the stack? + bool needs_deoptimization_; + + DISALLOW_COPY_AND_ASSIGN(NeedsDeoptimizationVisitor); +}; + // Do we need to deoptimize the stack to handle an exception? bool Dbg::IsForcedInterpreterNeededForExceptionImpl(Thread* thread) { const SingleStepControl* const ssc = thread->GetSingleStepControl(); @@ -3578,45 +3708,9 @@ bool Dbg::IsForcedInterpreterNeededForExceptionImpl(Thread* thread) { } // Deoptimization is required if at least one method in the stack needs it. However we // skip frames that will be unwound (thus not executed). - bool needs_deoptimization = false; - StackVisitor::WalkStack( - [&](art::StackVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - // The visitor is meant to be used when handling exception from compiled code only. - CHECK(!visitor->IsShadowFrame()) << "We only expect to visit compiled frame: " - << ArtMethod::PrettyMethod(visitor->GetMethod()); - ArtMethod* method = visitor->GetMethod(); - if (method == nullptr) { - // We reach an upcall and don't need to deoptimize this part of the stack (ManagedFragment) - // so we can stop the visit. - DCHECK(!needs_deoptimization); - return false; - } - if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) { - // We found a compiled frame in the stack but instrumentation is set to interpret - // everything: we need to deoptimize. - needs_deoptimization = true; - return false; - } - if (Runtime::Current()->GetInstrumentation()->IsDeoptimized(method)) { - // We found a deoptimized method in the stack. - needs_deoptimization = true; - return false; - } - ShadowFrame* frame = visitor->GetThread()->FindDebuggerShadowFrame(visitor->GetFrameId()); - if (frame != nullptr) { - // The debugger allocated a ShadowFrame to update a variable in the stack: we need to - // deoptimize the stack to execute (and deallocate) this frame. - needs_deoptimization = true; - return false; - } - return true; - }, - thread, - /* context= */ nullptr, - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames, - /* check_suspended */ true, - /* include_transitions */ true); - return needs_deoptimization; + NeedsDeoptimizationVisitor visitor(thread); + visitor.WalkStack(true); // includes upcall. + return visitor.NeedsDeoptimization(); } // Scoped utility class to suspend a thread so that we may do tasks such as walk its stack. Doesn't diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc index 80e3394f86..e11fa5c212 100644 --- a/runtime/gc/allocation_record.cc +++ b/runtime/gc/allocation_record.cc @@ -184,6 +184,34 @@ void AllocRecordObjectMap::BroadcastForNewAllocationRecords() { new_record_condition_.Broadcast(Thread::Current()); } +class AllocRecordStackVisitor : public StackVisitor { + public: + AllocRecordStackVisitor(Thread* thread, size_t max_depth, AllocRecordStackTrace* trace_out) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + max_depth_(max_depth), + trace_(trace_out) {} + + // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses + // annotalysis. + bool VisitFrame() override NO_THREAD_SAFETY_ANALYSIS { + if (trace_->GetDepth() >= max_depth_) { + return false; + } + ArtMethod* m = GetMethod(); + // m may be null if we have inlined methods of unresolved classes. b/27858645 + if (m != nullptr && !m->IsRuntimeMethod()) { + m = m->GetInterfaceMethodIfProxy(kRuntimePointerSize); + trace_->AddStackElement(AllocRecordStackTraceElement(m, GetDexPc())); + } + return true; + } + + private: + const size_t max_depth_; + AllocRecordStackTrace* const trace_; +}; + void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) { Thread* self = Thread::Current(); Heap* heap = Runtime::Current()->GetHeap(); @@ -240,26 +268,11 @@ void AllocRecordObjectMap::RecordAllocation(Thread* self, // Get stack trace outside of lock in case there are allocations during the stack walk. // b/27858645. AllocRecordStackTrace trace; + AllocRecordStackVisitor visitor(self, max_stack_depth_, /*out*/ &trace); { StackHandleScope<1> hs(self); auto obj_wrapper = hs.NewHandleWrapper(obj); - - StackVisitor::WalkStack( - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - if (trace.GetDepth() >= max_stack_depth_) { - return false; - } - ArtMethod* m = stack_visitor->GetMethod(); - // m may be null if we have inlined methods of unresolved classes. b/27858645 - if (m != nullptr && !m->IsRuntimeMethod()) { - m = m->GetInterfaceMethodIfProxy(kRuntimePointerSize); - trace.AddStackElement(AllocRecordStackTraceElement(m, stack_visitor->GetDexPc())); - } - return true; - }, - self, - /* context= */ nullptr, - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); + visitor.WalkStack(); } MutexLock mu(self, *Locks::alloc_tracker_lock_); diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 12f1522386..cbcaaef260 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -18,8 +18,6 @@ #include <sstream> -#include <android-base/logging.h> - #include "arch/context.h" #include "art_field-inl.h" #include "art_method-inl.h" @@ -1357,66 +1355,65 @@ DeoptimizationMethodType Instrumentation::GetDeoptimizationMethodType(ArtMethod* } // Try to get the shorty of a runtime method if it's an invocation stub. -static char GetRuntimeMethodShorty(Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_) { - char shorty = 'V'; - StackVisitor::WalkStack( - [&shorty](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* m = stack_visitor->GetMethod(); - if (m == nullptr || m->IsRuntimeMethod()) { - return true; - } - // The first Java method. - if (m->IsNative()) { - // Use JNI method's shorty for the jni stub. - shorty = m->GetShorty()[0]; - } else if (m->IsProxyMethod()) { - // Proxy method just invokes its proxied method via - // art_quick_proxy_invoke_handler. - shorty = m->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty()[0]; - } else { - const Instruction& instr = m->DexInstructions().InstructionAt(stack_visitor->GetDexPc()); - if (instr.IsInvoke()) { - auto get_method_index_fn = [](ArtMethod* caller, - const Instruction& inst, - uint32_t dex_pc) - REQUIRES_SHARED(Locks::mutator_lock_) { - switch (inst.Opcode()) { - case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: - case Instruction::INVOKE_VIRTUAL_QUICK: { - uint16_t method_idx = caller->GetIndexFromQuickening(dex_pc); - CHECK_NE(method_idx, DexFile::kDexNoIndex16); - return method_idx; - } - default: { - return static_cast<uint16_t>(inst.VRegB()); - } - } - }; - - uint16_t method_index = get_method_index_fn(m, instr, stack_visitor->GetDexPc()); - const DexFile* dex_file = m->GetDexFile(); - if (interpreter::IsStringInit(dex_file, method_index)) { - // Invoking string init constructor is turned into invoking - // StringFactory.newStringFromChars() which returns a string. - shorty = 'L'; - } else { - shorty = dex_file->GetMethodShorty(method_index)[0]; - } +struct RuntimeMethodShortyVisitor : public StackVisitor { + explicit RuntimeMethodShortyVisitor(Thread* thread) + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + shorty('V') {} + + static uint16_t GetMethodIndexOfInvoke(ArtMethod* caller, + const Instruction& inst, + uint32_t dex_pc) + REQUIRES_SHARED(Locks::mutator_lock_) { + switch (inst.Opcode()) { + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: + case Instruction::INVOKE_VIRTUAL_QUICK: { + uint16_t method_idx = caller->GetIndexFromQuickening(dex_pc); + CHECK_NE(method_idx, DexFile::kDexNoIndex16); + return method_idx; + } + default: { + return inst.VRegB(); + } + } + } - } else { - // It could be that a non-invoke opcode invokes a stub, which in turn - // invokes Java code. In such cases, we should never expect a return - // value from the stub. - } + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + if (m == nullptr || m->IsRuntimeMethod()) { + return true; + } + // The first Java method. + if (m->IsNative()) { + // Use JNI method's shorty for the jni stub. + shorty = m->GetShorty()[0]; + } else if (m->IsProxyMethod()) { + // Proxy method just invokes its proxied method via + // art_quick_proxy_invoke_handler. + shorty = m->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty()[0]; + } else { + const Instruction& instr = m->DexInstructions().InstructionAt(GetDexPc()); + if (instr.IsInvoke()) { + uint16_t method_index = GetMethodIndexOfInvoke(m, instr, GetDexPc()); + const DexFile* dex_file = m->GetDexFile(); + if (interpreter::IsStringInit(dex_file, method_index)) { + // Invoking string init constructor is turned into invoking + // StringFactory.newStringFromChars() which returns a string. + shorty = 'L'; + } else { + shorty = dex_file->GetMethodShorty(method_index)[0]; } - // Stop stack walking since we've seen a Java frame. - return false; - }, - thread, - /* context= */ nullptr, - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); - return shorty; -} + } else { + // It could be that a non-invoke opcode invokes a stub, which in turn + // invokes Java code. In such cases, we should never expect a return + // value from the stub. + } + } + // Stop stack walking since we've seen a Java frame. + return false; + } + + char shorty; +}; TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self, uintptr_t* return_pc, @@ -1450,7 +1447,9 @@ TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self, // for clinit, we need to pass return results to the caller. // We need the correct shorty to decide whether we need to pass the return // result for deoptimization below. - return_shorty = GetRuntimeMethodShorty(self); + RuntimeMethodShortyVisitor visitor(self); + visitor.WalkStack(); + return_shorty = visitor.shorty; } else { // Some runtime methods such as allocations, unresolved field getters, etc. // have return value. We don't need to set return_value since MethodExitEvent() diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 7729838601..1701ca8a78 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -18,8 +18,7 @@ #include <sstream> -#include <android-base/logging.h> -#include <android-base/unique_fd.h> +#include "android-base/unique_fd.h" #include "arch/context.h" #include "art_method-inl.h" @@ -1273,6 +1272,31 @@ size_t JitCodeCache::ReserveData(Thread* self, } } +class MarkCodeVisitor final : public StackVisitor { + public: + MarkCodeVisitor(Thread* thread_in, JitCodeCache* code_cache_in, CodeCacheBitmap* bitmap) + : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kSkipInlinedFrames), + code_cache_(code_cache_in), + bitmap_(bitmap) {} + + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); + if (method_header == nullptr) { + return true; + } + const void* code = method_header->GetCode(); + if (code_cache_->ContainsPc(code)) { + // Use the atomic set version, as multiple threads are executing this code. + bitmap_->AtomicTestAndSet(FromCodeToAllocation(code)); + } + return true; + } + + private: + JitCodeCache* const code_cache_; + CodeCacheBitmap* const bitmap_; +}; + class MarkCodeClosure final : public Closure { public: MarkCodeClosure(JitCodeCache* code_cache, CodeCacheBitmap* bitmap, Barrier* barrier) @@ -1281,24 +1305,8 @@ class MarkCodeClosure final : public Closure { void Run(Thread* thread) override REQUIRES_SHARED(Locks::mutator_lock_) { ScopedTrace trace(__PRETTY_FUNCTION__); DCHECK(thread == Thread::Current() || thread->IsSuspended()); - StackVisitor::WalkStack( - [&](const art::StackVisitor* stack_visitor) { - const OatQuickMethodHeader* method_header = - stack_visitor->GetCurrentOatQuickMethodHeader(); - if (method_header == nullptr) { - return true; - } - const void* code = method_header->GetCode(); - if (code_cache_->ContainsPc(code)) { - // Use the atomic set version, as multiple threads are executing this code. - bitmap_->AtomicTestAndSet(FromCodeToAllocation(code)); - } - return true; - }, - thread, - /* context= */ nullptr, - art::StackVisitor::StackWalkKind::kSkipInlinedFrames); - + MarkCodeVisitor visitor(thread, code_cache_, bitmap_); + visitor.WalkStack(); if (kIsDebugBuild) { // The stack walking code queries the side instrumentation stack if it // sees an instrumentation exit pc, so the JIT code of methods in that stack diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 647928391b..df2a8e29cb 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -277,6 +277,43 @@ void Monitor::SetObject(mirror::Object* object) { obj_ = GcRoot<mirror::Object>(object); } +// Note: Adapted from CurrentMethodVisitor in thread.cc. We must not resolve here. + +struct NthCallerWithDexPcVisitor final : public StackVisitor { + explicit NthCallerWithDexPcVisitor(Thread* thread, size_t frame) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + method_(nullptr), + dex_pc_(0), + current_frame_number_(0), + wanted_frame_number_(frame) {} + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + if (m == nullptr || m->IsRuntimeMethod()) { + // Runtime method, upcall, or resolution issue. Skip. + return true; + } + + // Is this the requested frame? + if (current_frame_number_ == wanted_frame_number_) { + method_ = m; + dex_pc_ = GetDexPc(/* abort_on_failure=*/ false); + return false; + } + + // Look for more. + current_frame_number_++; + return true; + } + + ArtMethod* method_; + uint32_t dex_pc_; + + private: + size_t current_frame_number_; + const size_t wanted_frame_number_; +}; + // This function is inlined and just helps to not have the VLOG and ATRACE check at all the // potential tracing points. void Monitor::AtraceMonitorLock(Thread* self, mirror::Object* obj, bool is_wait) { @@ -289,41 +326,13 @@ void Monitor::AtraceMonitorLockImpl(Thread* self, mirror::Object* obj, bool is_w // Wait() requires a deeper call stack to be useful. Otherwise you'll see "Waiting at // Object.java". Assume that we'll wait a nontrivial amount, so it's OK to do a longer // stack walk than if !is_wait. - const size_t wanted_frame_number = is_wait ? 1U : 0U; - - ArtMethod* method = nullptr; - uint32_t dex_pc = 0u; - - size_t current_frame_number = 0u; - StackVisitor::WalkStack( - // Note: Adapted from CurrentMethodVisitor in thread.cc. We must not resolve here. - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* m = stack_visitor->GetMethod(); - if (m == nullptr || m->IsRuntimeMethod()) { - // Runtime method, upcall, or resolution issue. Skip. - return true; - } - - // Is this the requested frame? - if (current_frame_number == wanted_frame_number) { - method = m; - dex_pc = stack_visitor->GetDexPc(false /* abort_on_error*/); - return false; - } - - // Look for more. - current_frame_number++; - return true; - }, - self, - /* context= */ nullptr, - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); - + NthCallerWithDexPcVisitor visitor(self, is_wait ? 1U : 0U); + visitor.WalkStack(false); const char* prefix = is_wait ? "Waiting on " : "Locking "; const char* filename; int32_t line_number; - TranslateLocation(method, dex_pc, &filename, &line_number); + TranslateLocation(visitor.method_, visitor.dex_pc_, &filename, &line_number); // It would be nice to have a stable "ID" for the object here. However, the only stable thing // would be the identity hashcode. But we cannot use IdentityHashcode here: For one, there are diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index d4e3d54a99..afdfefaffa 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -154,36 +154,46 @@ class CatchBlockStackVisitor final : public StackVisitor { DISALLOW_COPY_AND_ASSIGN(CatchBlockStackVisitor); }; -static size_t GetInstrumentationFramesToPop(Thread* self, size_t frame_depth) - REQUIRES_SHARED(Locks::mutator_lock_) { - CHECK_NE(frame_depth, kInvalidFrameDepth); - size_t instrumentation_frames_to_pop = 0; - StackVisitor::WalkStack( - [&](art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - size_t current_frame_depth = stack_visitor->GetFrameDepth(); - if (current_frame_depth < frame_depth) { - CHECK(stack_visitor->GetMethod() != nullptr); - if (UNLIKELY(reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == - stack_visitor->GetReturnPc())) { - if (!stack_visitor->IsInInlinedFrame()) { - // We do not count inlined frames, because we do not instrument them. The reason we - // include them in the stack walking is the check against `frame_depth_`, which is - // given to us by a visitor that visits inlined frames. - ++instrumentation_frames_to_pop; - } - } - return true; +// Counts instrumentation stack frame prior to catch handler or upcall. +class InstrumentationStackVisitor : public StackVisitor { + public: + InstrumentationStackVisitor(Thread* self, size_t frame_depth) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(self, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + frame_depth_(frame_depth), + instrumentation_frames_to_pop_(0) { + CHECK_NE(frame_depth_, kInvalidFrameDepth); + } + + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + size_t current_frame_depth = GetFrameDepth(); + if (current_frame_depth < frame_depth_) { + CHECK(GetMethod() != nullptr); + if (UNLIKELY(reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == GetReturnPc())) { + if (!IsInInlinedFrame()) { + // We do not count inlined frames, because we do not instrument them. The reason we + // include them in the stack walking is the check against `frame_depth_`, which is + // given to us by a visitor that visits inlined frames. + ++instrumentation_frames_to_pop_; } - // We reached the frame of the catch handler or the upcall. - return false; - }, - self, - /* context= */ nullptr, - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames, - /* check_suspended */ true, - /* include_transitions */ true); - return instrumentation_frames_to_pop; -} + } + return true; + } else { + // We reached the frame of the catch handler or the upcall. + return false; + } + } + + size_t GetInstrumentationFramesToPop() const { + return instrumentation_frames_to_pop_; + } + + private: + const size_t frame_depth_; + size_t instrumentation_frames_to_pop_; + + DISALLOW_COPY_AND_ASSIGN(InstrumentationStackVisitor); +}; // Finds the appropriate exception catch after calling all method exit instrumentation functions. // Note that this might change the exception being thrown. @@ -219,8 +229,9 @@ void QuickExceptionHandler::FindCatch(ObjPtr<mirror::Throwable> exception) { // Figure out how many of those frames have instrumentation we need to remove (Should be the // exact same as number of new_pop_count if there aren't inlined frames). - size_t instrumentation_frames_to_pop = - GetInstrumentationFramesToPop(self_, handler_frame_depth_); + InstrumentationStackVisitor instrumentation_visitor(self_, handler_frame_depth_); + instrumentation_visitor.WalkStack(true); + size_t instrumentation_frames_to_pop = instrumentation_visitor.GetInstrumentationFramesToPop(); if (kDebugExceptionDelivery) { if (*handler_quick_frame_ == nullptr) { @@ -636,8 +647,10 @@ uintptr_t QuickExceptionHandler::UpdateInstrumentationStack() { DCHECK(is_deoptimization_) << "Non-deoptimization handlers should use FindCatch"; uintptr_t return_pc = 0; if (method_tracing_active_) { - size_t instrumentation_frames_to_pop = - GetInstrumentationFramesToPop(self_, handler_frame_depth_); + InstrumentationStackVisitor visitor(self_, handler_frame_depth_); + visitor.WalkStack(true); + + size_t instrumentation_frames_to_pop = visitor.GetInstrumentationFramesToPop(); instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); return_pc = instrumentation->PopFramesForDeoptimization(self_, instrumentation_frames_to_pop); } @@ -658,41 +671,53 @@ void QuickExceptionHandler::DoLongJump(bool smash_caller_saves) { UNREACHABLE(); } +// Prints out methods with their type of frame. +class DumpFramesWithTypeStackVisitor final : public StackVisitor { + public: + explicit DumpFramesWithTypeStackVisitor(Thread* self, bool show_details = false) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(self, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + show_details_(show_details) {} + + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* method = GetMethod(); + if (show_details_) { + LOG(INFO) << "|> pc = " << std::hex << GetCurrentQuickFramePc(); + LOG(INFO) << "|> addr = " << std::hex << reinterpret_cast<uintptr_t>(GetCurrentQuickFrame()); + if (GetCurrentQuickFrame() != nullptr && method != nullptr) { + LOG(INFO) << "|> ret = " << std::hex << GetReturnPc(); + } + } + if (method == nullptr) { + // Transition, do go on, we want to unwind over bridges, all the way. + if (show_details_) { + LOG(INFO) << "N <transition>"; + } + return true; + } else if (method->IsRuntimeMethod()) { + if (show_details_) { + LOG(INFO) << "R " << method->PrettyMethod(true); + } + return true; + } else { + bool is_shadow = GetCurrentShadowFrame() != nullptr; + LOG(INFO) << (is_shadow ? "S" : "Q") + << ((!is_shadow && IsInInlinedFrame()) ? "i" : " ") + << " " + << method->PrettyMethod(true); + return true; // Go on. + } + } + + private: + bool show_details_; + + DISALLOW_COPY_AND_ASSIGN(DumpFramesWithTypeStackVisitor); +}; + void QuickExceptionHandler::DumpFramesWithType(Thread* self, bool details) { - StackVisitor::WalkStack( - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* method = stack_visitor->GetMethod(); - if (details) { - LOG(INFO) << "|> pc = " << std::hex << stack_visitor->GetCurrentQuickFramePc(); - LOG(INFO) << "|> addr = " << std::hex - << reinterpret_cast<uintptr_t>(stack_visitor->GetCurrentQuickFrame()); - if (stack_visitor->GetCurrentQuickFrame() != nullptr && method != nullptr) { - LOG(INFO) << "|> ret = " << std::hex << stack_visitor->GetReturnPc(); - } - } - if (method == nullptr) { - // Transition, do go on, we want to unwind over bridges, all the way. - if (details) { - LOG(INFO) << "N <transition>"; - } - return true; - } else if (method->IsRuntimeMethod()) { - if (details) { - LOG(INFO) << "R " << method->PrettyMethod(true); - } - return true; - } else { - bool is_shadow = stack_visitor->GetCurrentShadowFrame() != nullptr; - LOG(INFO) << (is_shadow ? "S" : "Q") - << ((!is_shadow && stack_visitor->IsInInlinedFrame()) ? "i" : " ") - << " " - << method->PrettyMethod(true); - return true; // Go on. - } - }, - self, - /* context= */ nullptr, - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); + DumpFramesWithTypeStackVisitor visitor(self, details); + visitor.WalkStack(true); } } // namespace art diff --git a/runtime/stack.h b/runtime/stack.h index 9d30115bb1..02578d25b7 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -143,36 +143,6 @@ class StackVisitor { template <CountTransitions kCount = CountTransitions::kYes> void WalkStack(bool include_transitions = false) REQUIRES_SHARED(Locks::mutator_lock_); - // Convenience helper function to walk the stack with a lambda as a visitor. - template <CountTransitions kCountTransitions = CountTransitions::kYes, - typename T> - ALWAYS_INLINE static void WalkStack(const T& fn, - Thread* thread, - Context* context, - StackWalkKind walk_kind, - bool check_suspended = true, - bool include_transitions = false) - REQUIRES_SHARED(Locks::mutator_lock_) { - class LambdaStackVisitor : public StackVisitor { - public: - LambdaStackVisitor(const T& fn, - Thread* thread, - Context* context, - StackWalkKind walk_kind, - bool check_suspended = true) - : StackVisitor(thread, context, walk_kind, check_suspended), fn_(fn) {} - - bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { - return fn_(this); - } - - private: - T fn_; - }; - LambdaStackVisitor visitor(fn, thread, context, walk_kind, check_suspended); - visitor.template WalkStack<kCountTransitions>(include_transitions); - } - Thread* GetThread() const { return thread_; } diff --git a/runtime/thread.cc b/runtime/thread.cc index 33cd9bbb67..e9fed76d6f 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -3607,34 +3607,42 @@ Context* Thread::GetLongJumpContext() { return result; } -ArtMethod* Thread::GetCurrentMethod(uint32_t* dex_pc_out, +ArtMethod* Thread::GetCurrentMethod(uint32_t* dex_pc, bool check_suspended, bool abort_on_error) const { // Note: this visitor may return with a method set, but dex_pc_ being DexFile:kDexNoIndex. This is // so we don't abort in a special situation (thinlocked monitor) when dumping the Java // stack. - ArtMethod* method = nullptr; - uint32_t dex_pc = dex::kDexNoIndex; - StackVisitor::WalkStack( - [&](const StackVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* m = visitor->GetMethod(); - if (m->IsRuntimeMethod()) { - // Continue if this is a runtime method. - return true; - } - method = m; - dex_pc = visitor->GetDexPc(abort_on_error); - return false; - }, - const_cast<Thread*>(this), - /* context= */ nullptr, - StackVisitor::StackWalkKind::kIncludeInlinedFrames, - check_suspended); - - if (dex_pc_out != nullptr) { - *dex_pc_out = dex_pc; + struct CurrentMethodVisitor final : public StackVisitor { + CurrentMethodVisitor(Thread* thread, bool check_suspended, bool abort_on_error) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(thread, + /* context= */nullptr, + StackVisitor::StackWalkKind::kIncludeInlinedFrames, + check_suspended), + method_(nullptr), + dex_pc_(0), + abort_on_error_(abort_on_error) {} + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + if (m->IsRuntimeMethod()) { + // Continue if this is a runtime method. + return true; + } + method_ = m; + dex_pc_ = GetDexPc(abort_on_error_); + return false; + } + ArtMethod* method_; + uint32_t dex_pc_; + const bool abort_on_error_; + }; + CurrentMethodVisitor visitor(const_cast<Thread*>(this), check_suspended, abort_on_error); + visitor.WalkStack(false); + if (dex_pc != nullptr) { + *dex_pc = visitor.dex_pc_; } - return method; + return visitor.method_; } bool Thread::HoldsLock(ObjPtr<mirror::Object> object) const { diff --git a/runtime/trace.cc b/runtime/trace.cc index f6c36cf989..ad58c2ea99 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -58,6 +58,32 @@ static constexpr uint8_t kOpNewMethod = 1U; static constexpr uint8_t kOpNewThread = 2U; static constexpr uint8_t kOpTraceSummary = 3U; +class BuildStackTraceVisitor : public StackVisitor { + public: + explicit BuildStackTraceVisitor(Thread* thread) + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + method_trace_(Trace::AllocStackTrace()) {} + + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + // Ignore runtime frames (in particular callee save). + if (!m->IsRuntimeMethod()) { + method_trace_->push_back(m); + } + return true; + } + + // Returns a stack trace where the topmost frame corresponds with the first element of the vector. + std::vector<ArtMethod*>* GetStackTrace() const { + return method_trace_; + } + + private: + std::vector<ArtMethod*>* const method_trace_; + + DISALLOW_COPY_AND_ASSIGN(BuildStackTraceVisitor); +}; + static const char kTraceTokenChar = '*'; static const uint16_t kTraceHeaderLength = 32; static const uint32_t kTraceMagicValue = 0x574f4c53; @@ -202,19 +228,9 @@ static void Append8LE(uint8_t* buf, uint64_t val) { } static void GetSample(Thread* thread, void* arg) REQUIRES_SHARED(Locks::mutator_lock_) { - std::vector<ArtMethod*>* const stack_trace = Trace::AllocStackTrace(); - StackVisitor::WalkStack( - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* m = stack_visitor->GetMethod(); - // Ignore runtime frames (in particular callee save). - if (!m->IsRuntimeMethod()) { - stack_trace->push_back(m); - } - return true; - }, - thread, - /* context= */ nullptr, - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); + BuildStackTraceVisitor build_trace_visitor(thread); + build_trace_visitor.WalkStack(); + std::vector<ArtMethod*>* stack_trace = build_trace_visitor.GetStackTrace(); Trace* the_trace = reinterpret_cast<Trace*>(arg); the_trace->CompareAndUpdateStackTrace(thread, stack_trace); } diff --git a/test/461-get-reference-vreg/get_reference_vreg_jni.cc b/test/461-get-reference-vreg/get_reference_vreg_jni.cc index 817a647805..ddc86dffa4 100644 --- a/test/461-get-reference-vreg/get_reference_vreg_jni.cc +++ b/test/461-get-reference-vreg/get_reference_vreg_jni.cc @@ -25,50 +25,62 @@ namespace art { namespace { -jint FindMethodIndex(jobject this_value_jobj) { - ScopedObjectAccess soa(Thread::Current()); - std::unique_ptr<Context> context(Context::Create()); - ObjPtr<mirror::Object> this_value = soa.Decode<mirror::Object>(this_value_jobj); - jint found_method_index = 0; - StackVisitor::WalkStack( - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* m = stack_visitor->GetMethod(); - std::string m_name(m->GetName()); +class TestVisitor : public StackVisitor { + public: + TestVisitor(Thread* thread, Context* context, mirror::Object* this_value) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + this_value_(this_value), + found_method_index_(0) {} - if (m_name.compare("$noinline$testThisWithInstanceCall") == 0) { - found_method_index = 1; - uint32_t value = 0; - CHECK(stack_visitor->GetVReg(m, 1, kReferenceVReg, &value)); - CHECK_EQ(reinterpret_cast<mirror::Object*>(value), this_value); - CHECK_EQ(stack_visitor->GetThisObject(), this_value); - } else if (m_name.compare("$noinline$testThisWithStaticCall") == 0) { - found_method_index = 2; - uint32_t value = 0; - CHECK(stack_visitor->GetVReg(m, 1, kReferenceVReg, &value)); - } else if (m_name.compare("$noinline$testParameter") == 0) { - found_method_index = 3; - uint32_t value = 0; - CHECK(stack_visitor->GetVReg(m, 1, kReferenceVReg, &value)); - } else if (m_name.compare("$noinline$testObjectInScope") == 0) { - found_method_index = 4; - uint32_t value = 0; - CHECK(stack_visitor->GetVReg(m, 0, kReferenceVReg, &value)); - } + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + std::string m_name(m->GetName()); - return true; - }, - soa.Self(), - context.get(), - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); - return found_method_index; -} + if (m_name.compare("$noinline$testThisWithInstanceCall") == 0) { + found_method_index_ = 1; + uint32_t value = 0; + CHECK(GetVReg(m, 1, kReferenceVReg, &value)); + CHECK_EQ(reinterpret_cast<mirror::Object*>(value), this_value_); + CHECK_EQ(GetThisObject(), this_value_); + } else if (m_name.compare("$noinline$testThisWithStaticCall") == 0) { + found_method_index_ = 2; + uint32_t value = 0; + CHECK(GetVReg(m, 1, kReferenceVReg, &value)); + } else if (m_name.compare("$noinline$testParameter") == 0) { + found_method_index_ = 3; + uint32_t value = 0; + CHECK(GetVReg(m, 1, kReferenceVReg, &value)); + } else if (m_name.compare("$noinline$testObjectInScope") == 0) { + found_method_index_ = 4; + uint32_t value = 0; + CHECK(GetVReg(m, 0, kReferenceVReg, &value)); + } + + return true; + } + + mirror::Object* this_value_; + + // Value returned to Java to ensure the methods testSimpleVReg and testPairVReg + // have been found and tested. + jint found_method_index_; +}; extern "C" JNIEXPORT jint JNICALL Java_Main_doNativeCallRef(JNIEnv*, jobject value) { - return FindMethodIndex(value); + ScopedObjectAccess soa(Thread::Current()); + std::unique_ptr<Context> context(Context::Create()); + TestVisitor visitor(soa.Self(), context.get(), soa.Decode<mirror::Object>(value).Ptr()); + visitor.WalkStack(); + return visitor.found_method_index_; } extern "C" JNIEXPORT jint JNICALL Java_Main_doStaticNativeCallRef(JNIEnv*, jclass) { - return FindMethodIndex(nullptr); + ScopedObjectAccess soa(Thread::Current()); + std::unique_ptr<Context> context(Context::Create()); + TestVisitor visitor(soa.Self(), context.get(), nullptr); + visitor.WalkStack(); + return visitor.found_method_index_; } } // namespace diff --git a/test/543-env-long-ref/env_long_ref.cc b/test/543-env-long-ref/env_long_ref.cc index 1885f8d9f5..165f5bf412 100644 --- a/test/543-env-long-ref/env_long_ref.cc +++ b/test/543-env-long-ref/env_long_ref.cc @@ -23,28 +23,44 @@ namespace art { +namespace { + +class TestVisitor : public StackVisitor { + public: + TestVisitor(const ScopedObjectAccess& soa, Context* context, jobject expected_value) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(soa.Self(), context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + expected_value_(expected_value), + found_(false), + soa_(soa) {} + + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + std::string m_name(m->GetName()); + + if (m_name == "testCase") { + found_ = true; + uint32_t value = 0; + CHECK(GetVReg(m, 1, kReferenceVReg, &value)); + CHECK_EQ(reinterpret_cast<mirror::Object*>(value), + soa_.Decode<mirror::Object>(expected_value_).Ptr()); + } + return true; + } + + jobject expected_value_; + bool found_; + const ScopedObjectAccess& soa_; +}; + +} // namespace + extern "C" JNIEXPORT void JNICALL Java_Main_lookForMyRegisters(JNIEnv*, jclass, jobject value) { ScopedObjectAccess soa(Thread::Current()); std::unique_ptr<Context> context(Context::Create()); - bool found = false; - StackVisitor::WalkStack( - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* m = stack_visitor->GetMethod(); - std::string m_name(m->GetName()); - - if (m_name == "testCase") { - found = true; - uint32_t stack_value = 0; - CHECK(stack_visitor->GetVReg(m, 1, kReferenceVReg, &stack_value)); - CHECK_EQ(reinterpret_cast<mirror::Object*>(stack_value), - soa.Decode<mirror::Object>(value).Ptr()); - } - return true; - }, - soa.Self(), - context.get(), - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); - CHECK(found); + TestVisitor visitor(soa, context.get(), value); + visitor.WalkStack(); + CHECK(visitor.found_); } } // namespace art diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc index b2b363447f..7b88842fcc 100644 --- a/test/570-checker-osr/osr.cc +++ b/test/570-checker-osr/osr.cc @@ -23,33 +23,39 @@ #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "stack_map.h" -#include "thread-current-inl.h" namespace art { -namespace { +class OsrVisitor : public StackVisitor { + public: + explicit OsrVisitor(Thread* thread, const char* method_name) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + method_name_(method_name), + in_osr_method_(false), + in_interpreter_(false) {} -template <typename Handler> -void ProcessMethodWithName(JNIEnv* env, jstring method_name, const Handler& handler) { - ScopedUtfChars chars(env, method_name); - CHECK(chars.c_str() != nullptr); - ScopedObjectAccess soa(Thread::Current()); - StackVisitor::WalkStack( - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - std::string m_name(stack_visitor->GetMethod()->GetName()); - - if (m_name.compare(chars.c_str()) == 0) { - handler(stack_visitor); - return false; - } - return true; - }, - soa.Self(), - /* context= */ nullptr, - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); -} + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + std::string m_name(m->GetName()); -} // namespace + if (m_name.compare(method_name_) == 0) { + const OatQuickMethodHeader* header = + Runtime::Current()->GetJit()->GetCodeCache()->LookupOsrMethodHeader(m); + if (header != nullptr && header == GetCurrentOatQuickMethodHeader()) { + in_osr_method_ = true; + } else if (IsShadowFrame()) { + in_interpreter_ = true; + } + return false; + } + return true; + } + + const char* const method_name_; + bool in_osr_method_; + bool in_interpreter_; +}; extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInOsrCode(JNIEnv* env, jclass, @@ -59,19 +65,12 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInOsrCode(JNIEnv* env, // Just return true for non-jit configurations to stop the infinite loop. return JNI_TRUE; } - bool in_osr_code = false; - ProcessMethodWithName( - env, - method_name, - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* m = stack_visitor->GetMethod(); - const OatQuickMethodHeader* header = - Runtime::Current()->GetJit()->GetCodeCache()->LookupOsrMethodHeader(m); - if (header != nullptr && header == stack_visitor->GetCurrentOatQuickMethodHeader()) { - in_osr_code = true; - } - }); - return in_osr_code; + ScopedUtfChars chars(env, method_name); + CHECK(chars.c_str() != nullptr); + ScopedObjectAccess soa(Thread::Current()); + OsrVisitor visitor(soa.Self(), chars.c_str()); + visitor.WalkStack(); + return visitor.in_osr_method_; } extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInInterpreter(JNIEnv* env, @@ -81,56 +80,86 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInInterpreter(JNIEnv* env, // The return value is irrelevant if we're not using JIT. return false; } - bool in_interpreter = false; - ProcessMethodWithName( - env, - method_name, - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* m = stack_visitor->GetMethod(); - const OatQuickMethodHeader* header = - Runtime::Current()->GetJit()->GetCodeCache()->LookupOsrMethodHeader(m); - if ((header == nullptr || header != stack_visitor->GetCurrentOatQuickMethodHeader()) && - stack_visitor->IsShadowFrame()) { - in_interpreter = true; - } - }); - return in_interpreter; + ScopedUtfChars chars(env, method_name); + CHECK(chars.c_str() != nullptr); + ScopedObjectAccess soa(Thread::Current()); + OsrVisitor visitor(soa.Self(), chars.c_str()); + visitor.WalkStack(); + return visitor.in_interpreter_; } +class ProfilingInfoVisitor : public StackVisitor { + public: + explicit ProfilingInfoVisitor(Thread* thread, const char* method_name) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + method_name_(method_name) {} + + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + std::string m_name(m->GetName()); + + if (m_name.compare(method_name_) == 0) { + ProfilingInfo::Create(Thread::Current(), m, /* retry_allocation */ true); + return false; + } + return true; + } + + const char* const method_name_; +}; + extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasProfilingInfo(JNIEnv* env, jclass, jstring method_name) { if (!Runtime::Current()->UseJitCompilation()) { return; } - ProcessMethodWithName( - env, - method_name, - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* m = stack_visitor->GetMethod(); - ProfilingInfo::Create(Thread::Current(), m, /* retry_allocation */ true); - }); + ScopedUtfChars chars(env, method_name); + CHECK(chars.c_str() != nullptr); + ScopedObjectAccess soa(Thread::Current()); + ProfilingInfoVisitor visitor(soa.Self(), chars.c_str()); + visitor.WalkStack(); } +class OsrCheckVisitor : public StackVisitor { + public: + OsrCheckVisitor(Thread* thread, const char* method_name) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + method_name_(method_name) {} + + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + std::string m_name(m->GetName()); + + jit::Jit* jit = Runtime::Current()->GetJit(); + if (m_name.compare(method_name_) == 0) { + while (jit->GetCodeCache()->LookupOsrMethodHeader(m) == nullptr) { + // Sleep to yield to the compiler thread. + usleep(1000); + // Will either ensure it's compiled or do the compilation itself. + jit->CompileMethod(m, Thread::Current(), /* osr */ true); + } + return false; + } + return true; + } + + const char* const method_name_; +}; + extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasOsrCode(JNIEnv* env, jclass, jstring method_name) { if (!Runtime::Current()->UseJitCompilation()) { return; } - ProcessMethodWithName( - env, - method_name, - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* m = stack_visitor->GetMethod(); - jit::Jit* jit = Runtime::Current()->GetJit(); - while (jit->GetCodeCache()->LookupOsrMethodHeader(m) == nullptr) { - // Sleep to yield to the compiler thread. - usleep(1000); - // Will either ensure it's compiled or do the compilation itself. - jit->CompileMethod(m, Thread::Current(), /* osr */ true); - } - }); + ScopedUtfChars chars(env, method_name); + CHECK(chars.c_str() != nullptr); + ScopedObjectAccess soa(Thread::Current()); + OsrCheckVisitor visitor(soa.Self(), chars.c_str()); + visitor.WalkStack(); } } // namespace art diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc index 393e773275..581aa74d4e 100644 --- a/test/common/stack_inspect.cc +++ b/test/common/stack_inspect.cc @@ -66,30 +66,42 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterpretedAt(JNIEnv* env, // public static native boolean isInterpretedFunction(String smali); -static bool IsMethodInterpreted(Thread* self, - const ArtMethod* goal, - const bool require_deoptable, - /* out */ bool* method_is_interpreted) - REQUIRES_SHARED(Locks::mutator_lock_) { - *method_is_interpreted = true; - bool method_found = false; - bool prev_was_runtime = true; - StackVisitor::WalkStack( - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - if (goal == stack_visitor->GetMethod()) { - *method_is_interpreted = - (require_deoptable && prev_was_runtime) || stack_visitor->IsShadowFrame(); - method_found = true; - return false; - } - prev_was_runtime = stack_visitor->GetMethod()->IsRuntimeMethod(); - return true; - }, - self, - /* context= */ nullptr, - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); - return method_found; -} +// TODO Remove 'allow_runtime_frames' option once we have deoptimization through runtime frames. +struct MethodIsInterpretedVisitor : public StackVisitor { + public: + MethodIsInterpretedVisitor(Thread* thread, ArtMethod* goal, bool require_deoptable) + : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), + goal_(goal), + method_is_interpreted_(true), + method_found_(false), + prev_was_runtime_(true), + require_deoptable_(require_deoptable) {} + + bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { + if (goal_ == GetMethod()) { + method_is_interpreted_ = (require_deoptable_ && prev_was_runtime_) || IsShadowFrame(); + method_found_ = true; + return false; + } + prev_was_runtime_ = GetMethod()->IsRuntimeMethod(); + return true; + } + + bool IsInterpreted() { + return method_is_interpreted_; + } + + bool IsFound() { + return method_found_; + } + + private: + const ArtMethod* goal_; + bool method_is_interpreted_; + bool method_found_; + bool prev_was_runtime_; + bool require_deoptable_; +}; // TODO Remove 'require_deoptimizable' option once we have deoptimization through runtime frames. extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterpretedFunction( @@ -107,18 +119,23 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterpretedFunction( env->ThrowNew(env->FindClass("java/lang/Error"), "Unable to interpret method argument!"); return JNI_FALSE; } + bool result; + bool found; { ScopedObjectAccess soa(env); ArtMethod* goal = jni::DecodeArtMethod(id); - bool is_interpreted; - if (!IsMethodInterpreted(soa.Self(), goal, require_deoptimizable, &is_interpreted)) { - env->ThrowNew(env->FindClass("java/lang/Error"), "Unable to find given method in stack!"); - return JNI_FALSE; - } + MethodIsInterpretedVisitor v(soa.Self(), goal, require_deoptimizable); + v.WalkStack(); bool enters_interpreter = Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge( goal->GetEntryPointFromQuickCompiledCode()); - return (is_interpreted || enters_interpreter); + result = (v.IsInterpreted() || enters_interpreter); + found = v.IsFound(); + } + if (!found) { + env->ThrowNew(env->FindClass("java/lang/Error"), "Unable to find given method in stack!"); + return JNI_FALSE; } + return result; } // public static native void assertIsInterpreted(); @@ -179,24 +196,24 @@ extern "C" JNIEXPORT void JNICALL Java_Main_assertCallerIsManaged(JNIEnv* env, j } } +struct GetCallingFrameVisitor : public StackVisitor { + GetCallingFrameVisitor(Thread* thread, Context* context) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {} + + bool VisitFrame() override NO_THREAD_SAFETY_ANALYSIS { + // Discard stubs and Main.getThisOfCaller. + return GetMethod() == nullptr || GetMethod()->IsNative(); + } +}; + extern "C" JNIEXPORT jobject JNICALL Java_Main_getThisOfCaller( JNIEnv* env, jclass cls ATTRIBUTE_UNUSED) { ScopedObjectAccess soa(env); std::unique_ptr<art::Context> context(art::Context::Create()); - jobject result = nullptr; - StackVisitor::WalkStack( - [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) { - // Discard stubs and Main.getThisOfCaller. - if (stack_visitor->GetMethod() == nullptr || stack_visitor->GetMethod()->IsNative()) { - return true; - } - result = soa.AddLocalReference<jobject>(stack_visitor->GetThisObject()); - return false; - }, - soa.Self(), - context.get(), - art::StackVisitor::StackWalkKind::kIncludeInlinedFrames); - return result; + GetCallingFrameVisitor visitor(soa.Self(), context.get()); + visitor.WalkStack(); + return soa.AddLocalReference<jobject>(visitor.GetThisObject()); } } // namespace art |