| // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #ifndef RUNTIME_VM_STACK_FRAME_H_ |
| #define RUNTIME_VM_STACK_FRAME_H_ |
| |
| #include "vm/allocation.h" |
| #include "vm/frame_layout.h" |
| #include "vm/interpreter.h" |
| #include "vm/object.h" |
| #include "vm/stack_frame_kbc.h" |
| #include "vm/stub_code.h" |
| |
| #if defined(TARGET_ARCH_IA32) |
| #include "vm/stack_frame_ia32.h" |
| #elif defined(TARGET_ARCH_X64) |
| #include "vm/stack_frame_x64.h" |
| #elif defined(TARGET_ARCH_ARM) |
| #include "vm/stack_frame_arm.h" |
| #elif defined(TARGET_ARCH_ARM64) |
| #include "vm/stack_frame_arm64.h" |
| #elif defined(TARGET_ARCH_RISCV32) || defined(TARGET_ARCH_RISCV64) |
| #include "vm/stack_frame_riscv.h" |
| #else |
| #error Unknown architecture. |
| #endif |
| |
| namespace dart { |
| |
| // Forward declarations. |
| class ObjectPointerVisitor; |
| class LocalVariable; |
| |
| extern FrameLayout runtime_frame_layout; |
| |
| // Generic stack frame. |
| class StackFrame : public ValueObject { |
| public: |
| virtual ~StackFrame() {} |
| |
| // Accessors to get the pc, sp and fp of a frame. |
| uword sp() const { return sp_; } |
| uword fp() const { return fp_; } |
| uword pc() const { return pc_; } |
| |
| // The pool pointer is not implemented on all architectures. |
| static int SavedCallerPpSlotFromFp() { |
| // Never called on an interpreter frame. |
| if (runtime_frame_layout.saved_caller_pp_from_fp != |
| kSavedCallerFpSlotFromFp) { |
| return runtime_frame_layout.saved_caller_pp_from_fp; |
| } |
| UNREACHABLE(); |
| return 0; |
| } |
| |
| bool IsMarkedForLazyDeopt() const { |
| ASSERT(!is_interpreted()); |
| uword raw_pc = |
| *reinterpret_cast<uword*>(sp() + (kSavedPcSlotFromSp * kWordSize)); |
| return raw_pc == StubCode::DeoptimizeLazyFromReturn().EntryPoint(); |
| } |
| void MarkForLazyDeopt() { |
| ASSERT(!is_interpreted()); |
| set_pc(StubCode::DeoptimizeLazyFromReturn().EntryPoint()); |
| } |
| void UnmarkForLazyDeopt() { |
| // If this frame was marked for lazy deopt, pc_ was computed to be the |
| // original return address using the pending deopts table in GetCallerPc. |
| // Write this value back into the frame. |
| ASSERT(!is_interpreted()); |
| uword original_pc = pc(); |
| ASSERT(original_pc != StubCode::DeoptimizeLazyFromReturn().EntryPoint()); |
| set_pc(original_pc); |
| } |
| |
| void set_pc(uword value) { |
| *reinterpret_cast<uword*>(sp() + ((is_interpreted() ? kKBCSavedPcSlotFromSp |
| : kSavedPcSlotFromSp) * |
| kWordSize)) = value; |
| pc_ = value; |
| } |
| |
| void set_pc_marker(CodePtr code) { |
| *reinterpret_cast<CodePtr*>( |
| fp() + ((is_interpreted() ? kKBCPcMarkerSlotFromFp |
| : runtime_frame_layout.code_from_fp) * |
| kWordSize)) = code; |
| } |
| |
| // Visit objects in the frame. |
| virtual void VisitObjectPointers(ObjectPointerVisitor* visitor); |
| |
| const char* ToCString() const; |
| |
| // Check validity of a frame, used for assertion purposes. |
| virtual bool IsValid() const; |
| |
| // Returns true iff the current frame is a bare instructions dart frame. |
| bool IsBareInstructionsDartFrame() const; |
| |
| // Returns true iff the current frame is a bare instructions stub frame. |
| bool IsBareInstructionsStubFrame() const; |
| |
| // Frame type. |
| virtual bool IsDartFrame(bool validate = true) const { |
| ASSERT(!validate || IsValid()); |
| return !(IsEntryFrame() || IsExitFrame() || IsStubFrame()); |
| } |
| virtual bool IsStubFrame() const; |
| virtual bool IsEntryFrame() const { return false; } |
| virtual bool IsExitFrame() const { return false; } |
| |
| #if defined(DART_DYNAMIC_MODULES) |
| bool is_interpreted() const { return is_interpreted_; } |
| #else |
| bool is_interpreted() const { return false; } |
| #endif |
| |
| FunctionPtr LookupDartFunction() const; |
| CodePtr LookupDartCode() const; |
| BytecodePtr LookupDartBytecode() const; |
| bool FindExceptionHandler(Thread* thread, |
| uword* handler_pc, |
| bool* needs_stacktrace, |
| bool* is_catch_all, |
| bool* is_optimized) const; |
| // Returns token_pos of the pc(), or -1 if none exists. |
| TokenPosition GetTokenPos() const; |
| |
| static void DumpCurrentTrace(); |
| |
| uword GetCallerSp() const { |
| return fp() + |
| ((is_interpreted() ? kKBCCallerSpSlotFromFp : kCallerSpSlotFromFp) * |
| kWordSize); |
| } |
| |
| protected: |
| explicit StackFrame(Thread* thread) |
| : fp_(0), sp_(0), pc_(0), thread_(thread) {} |
| |
| // Name of the frame, used for generic frame printing functionality. |
| virtual const char* GetName() const { |
| if (IsBareInstructionsStubFrame()) return "bare-stub"; |
| if (IsStubFrame()) return "stub"; |
| return IsBareInstructionsDartFrame() ? "bare-dart" : "dart"; |
| } |
| |
| Isolate* isolate() const { return thread_->isolate(); } |
| IsolateGroup* isolate_group() const { return thread_->isolate_group(); } |
| |
| Thread* thread() const { return thread_; } |
| |
| private: |
| CodePtr GetCodeObject() const; |
| BytecodePtr GetBytecodeObject() const; |
| |
| uword GetCallerFp() const { |
| return *(reinterpret_cast<uword*>( |
| fp() + ((is_interpreted() ? kKBCSavedCallerFpSlotFromFp |
| : kSavedCallerFpSlotFromFp) * |
| kWordSize))); |
| } |
| |
| uword GetCallerPc() const { |
| uword raw_pc = *(reinterpret_cast<uword*>( |
| fp() + ((is_interpreted() ? kKBCSavedCallerPcSlotFromFp |
| : kSavedCallerPcSlotFromFp) * |
| kWordSize))); |
| ASSERT(raw_pc != StubCode::DeoptimizeLazyFromThrow().EntryPoint()); |
| if (raw_pc == StubCode::DeoptimizeLazyFromReturn().EntryPoint()) { |
| return thread_->pending_deopts().FindPendingDeopt(GetCallerFp()); |
| } |
| return raw_pc; |
| } |
| |
| uword fp_; |
| uword sp_; |
| uword pc_; |
| Thread* thread_; |
| |
| #if defined(DART_DYNAMIC_MODULES) |
| bool is_interpreted_ = false; |
| #endif |
| |
| // The iterators FrameSetIterator and StackFrameIterator set the private |
| // fields fp_ and sp_ when they return the respective frame objects. |
| friend class FrameSetIterator; |
| friend class StackFrameIterator; |
| // UntaggedSuspendState::VisitSuspendStatePointers creates a temporary |
| // StackFrame objects for the copied frames of the suspended functions. |
| friend class UntaggedSuspendState; |
| friend class ProfilerDartStackWalker; |
| DISALLOW_COPY_AND_ASSIGN(StackFrame); |
| }; |
| |
| // Exit frame is used to mark the transition from dart code into dart VM |
| // runtime code. |
| class ExitFrame : public StackFrame { |
| public: |
| bool IsValid() const { return sp() == 0; } |
| bool IsDartFrame(bool validate = true) const { return false; } |
| bool IsStubFrame() const { return false; } |
| bool IsExitFrame() const { return true; } |
| |
| // Visit objects in the frame. |
| virtual void VisitObjectPointers(ObjectPointerVisitor* visitor); |
| |
| protected: |
| virtual const char* GetName() const { return "exit"; } |
| |
| private: |
| explicit ExitFrame(Thread* thread) : StackFrame(thread) {} |
| |
| friend class StackFrameIterator; |
| DISALLOW_COPY_AND_ASSIGN(ExitFrame); |
| }; |
| |
| // Entry Frame is used to mark the transition from dart VM runtime code into |
| // dart code. |
| class EntryFrame : public StackFrame { |
| public: |
| bool IsValid() const { |
| return StubCode::InInvocationStub(pc(), is_interpreted()); |
| } |
| bool IsDartFrame(bool validate = true) const { return false; } |
| bool IsStubFrame() const { return false; } |
| bool IsEntryFrame() const { return true; } |
| |
| // Visit objects in the frame. |
| virtual void VisitObjectPointers(ObjectPointerVisitor* visitor); |
| |
| protected: |
| virtual const char* GetName() const { return "entry"; } |
| |
| private: |
| explicit EntryFrame(Thread* thread) : StackFrame(thread) {} |
| |
| friend class StackFrameIterator; |
| DISALLOW_COPY_AND_ASSIGN(EntryFrame); |
| }; |
| |
| // A StackFrameIterator can be initialized with a thread other than the |
| // current thread. Because this is generally a bad idea, it is only allowed on |
| // Windows- where it is needed for the profiler. It is the responsibility of |
| // users of StackFrameIterator to ensure that the thread given is not running |
| // concurrently. |
| class StackFrameIterator { |
| public: |
| enum CrossThreadPolicy { |
| kNoCrossThreadIteration = 0, |
| kAllowCrossThreadIteration = 1, |
| }; |
| |
| // Iterators for iterating over all frames from the last ExitFrame to the |
| // first EntryFrame. |
| StackFrameIterator(ValidationPolicy validation_policy, |
| Thread* thread, |
| CrossThreadPolicy cross_thread_policy); |
| StackFrameIterator(uword last_fp, |
| ValidationPolicy validation_policy, |
| Thread* thread, |
| CrossThreadPolicy cross_thread_policy); |
| |
| // Iterator for iterating over all frames from the current frame (given by its |
| // fp, sp, and pc) to the first EntryFrame. |
| StackFrameIterator(uword fp, |
| uword sp, |
| uword pc, |
| ValidationPolicy validation_policy, |
| Thread* thread, |
| CrossThreadPolicy cross_thread_policy); |
| |
| explicit StackFrameIterator(const StackFrameIterator& orig); |
| |
| // Checks if a next frame exists. |
| bool HasNextFrame() const { return frames_.fp_ != 0; } |
| |
| // Get next frame. Becomes invalid after the next call to NextFrame. |
| StackFrame* NextFrame(); |
| |
| bool validate() const { return validate_; } |
| |
| private: |
| // Iterator for iterating over the set of frames (dart or stub) which exist |
| // in one EntryFrame and ExitFrame block. |
| class FrameSetIterator : public ValueObject { |
| public: |
| // Checks if a next non entry/exit frame exists in the set. |
| bool HasNext() const { |
| if (fp_ == 0) { |
| return false; |
| } |
| const uword pc = *(reinterpret_cast<uword*>( |
| sp_ + |
| ((is_interpreted() ? kKBCSavedPcSlotFromSp : kSavedPcSlotFromSp) * |
| kWordSize))); |
| return !StubCode::InInvocationStub(pc, is_interpreted()); |
| } |
| |
| // Get next non entry/exit frame in the set (assumes a next frame exists). |
| StackFrame* NextFrame(bool validate); |
| |
| private: |
| explicit FrameSetIterator(Thread* thread) |
| : fp_(0), sp_(0), pc_(0), stack_frame_(thread), thread_(thread) {} |
| |
| #if defined(DART_DYNAMIC_MODULES) |
| bool is_interpreted() const { return is_interpreted_; } |
| void CheckIfInterpreted(uword exit_marker); |
| #else |
| bool is_interpreted() const { return false; } |
| #endif |
| |
| void Unpoison(); |
| |
| uword fp_; |
| uword sp_; |
| uword pc_; |
| StackFrame stack_frame_; // Singleton frame returned by NextFrame(). |
| Thread* thread_; |
| |
| #if defined(DART_DYNAMIC_MODULES) |
| bool is_interpreted_ = false; |
| #endif |
| |
| friend class StackFrameIterator; |
| DISALLOW_COPY_AND_ASSIGN(FrameSetIterator); |
| }; |
| |
| // Get next exit frame. |
| ExitFrame* NextExitFrame(); |
| |
| // Get next entry frame. |
| EntryFrame* NextEntryFrame(); |
| |
| // Get an iterator to the next set of frames between an entry and exit |
| // frame. |
| FrameSetIterator* NextFrameSet() { return &frames_; } |
| |
| // Setup last or next exit frames so that we are ready to iterate over |
| // stack frames. |
| void SetupLastExitFrameData(); |
| void SetupNextExitFrameData(); |
| |
| void CheckInterpreterExitFrame(uword exit_marker); |
| |
| bool validate_; // Validate each frame as we traverse the frames. |
| EntryFrame entry_; // Singleton entry frame returned by NextEntryFrame(). |
| ExitFrame exit_; // Singleton exit frame returned by NextExitFrame(). |
| FrameSetIterator frames_; |
| StackFrame* current_frame_; // Points to the current frame in the iterator. |
| Thread* thread_; |
| |
| friend class ProfilerDartStackWalker; |
| }; |
| |
| // Iterator for iterating over all dart frames (skips over exit frames, |
| // entry frames and stub frames). |
| // A DartFrameIterator can be initialized with an isolate other than the |
| // current thread's isolate. Because this is generally a bad idea, |
| // it is only allowed on Windows- where it is needed for the profiler. |
| // It is the responsibility of users of DartFrameIterator to ensure that the |
| // isolate given is not running concurrently on another thread. |
| class DartFrameIterator { |
| public: |
| explicit DartFrameIterator( |
| Thread* thread, |
| StackFrameIterator::CrossThreadPolicy cross_thread_policy) |
| : frames_(ValidationPolicy::kDontValidateFrames, |
| thread, |
| cross_thread_policy) {} |
| explicit DartFrameIterator( |
| uword last_fp, |
| Thread* thread, |
| StackFrameIterator::CrossThreadPolicy cross_thread_policy) |
| : frames_(last_fp, |
| ValidationPolicy::kDontValidateFrames, |
| thread, |
| cross_thread_policy) {} |
| |
| DartFrameIterator(uword fp, |
| uword sp, |
| uword pc, |
| Thread* thread, |
| StackFrameIterator::CrossThreadPolicy cross_thread_policy) |
| : frames_(fp, |
| sp, |
| pc, |
| ValidationPolicy::kDontValidateFrames, |
| thread, |
| cross_thread_policy) {} |
| |
| explicit DartFrameIterator(const DartFrameIterator& orig) |
| : frames_(orig.frames_) {} |
| |
| // Get next dart frame. |
| StackFrame* NextFrame() { |
| StackFrame* frame = frames_.NextFrame(); |
| while (frame != nullptr && !frame->IsDartFrame(frames_.validate())) { |
| frame = frames_.NextFrame(); |
| } |
| return frame; |
| } |
| |
| private: |
| StackFrameIterator frames_; |
| }; |
| |
| // Iterator for iterating over all inlined dart functions in an optimized |
| // dart frame (the iteration includes the function that is inlining the |
| // other functions). |
| class InlinedFunctionsIterator : public ValueObject { |
| public: |
| InlinedFunctionsIterator(const Code& code, uword pc); |
| bool Done() const { return index_ == -1; } |
| void Advance(); |
| |
| FunctionPtr function() const { |
| ASSERT(!Done()); |
| return function_.ptr(); |
| } |
| |
| uword pc() const { |
| ASSERT(!Done()); |
| return pc_; |
| } |
| |
| CodePtr code() const { |
| ASSERT(!Done()); |
| return code_.ptr(); |
| } |
| |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| intptr_t GetDeoptFpOffset() const; |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| |
| private: |
| void SetDone() { index_ = -1; } |
| |
| intptr_t index_; |
| intptr_t num_materializations_; |
| intptr_t dest_frame_size_; |
| Code& code_; |
| TypedData& deopt_info_; |
| Function& function_; |
| uword pc_; |
| GrowableArray<DeoptInstr*> deopt_instructions_; |
| ObjectPool& object_table_; |
| |
| DISALLOW_COPY_AND_ASSIGN(InlinedFunctionsIterator); |
| }; |
| |
| #if defined(DEBUG) |
| void ValidateFrames(); |
| #endif |
| |
| DART_FORCE_INLINE static intptr_t LocalVarIndex(intptr_t fp_offset, |
| intptr_t var_index) { |
| return fp_offset + var_index; |
| } |
| |
| DART_FORCE_INLINE static uword ParamAddress(uword fp, intptr_t reverse_index) { |
| return fp + (kParamEndSlotFromFp * kWordSize) + (reverse_index * kWordSize); |
| } |
| |
| // Both fp and other_fp are compiled code frame pointers. |
| DART_FORCE_INLINE static bool IsCalleeFrameOf(uword fp, uword other_fp) { |
| return other_fp < fp; |
| } |
| |
| // Value for stack limit that is used to cause an interrupt. |
| static constexpr uword kInterruptStackLimit = ~static_cast<uword>(0); |
| |
| DART_FORCE_INLINE static uword LocalVarAddress(uword fp, intptr_t index) { |
| return fp + LocalVarIndex(0, index) * kWordSize; |
| } |
| |
| } // namespace dart |
| |
| #endif // RUNTIME_VM_STACK_FRAME_H_ |