| 1 | // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | #ifndef RUNTIME_VM_STACK_FRAME_H_ |
| 6 | #define RUNTIME_VM_STACK_FRAME_H_ |
| 7 | |
| 8 | #include "vm/allocation.h" |
| 9 | #include "vm/frame_layout.h" |
| 10 | #include "vm/interpreter.h" |
| 11 | #include "vm/object.h" |
| 12 | #include "vm/stack_frame_kbc.h" |
| 13 | #include "vm/stub_code.h" |
| 14 | |
| 15 | #if defined(TARGET_ARCH_IA32) |
| 16 | #include "vm/stack_frame_ia32.h" |
| 17 | #elif defined(TARGET_ARCH_X64) |
| 18 | #include "vm/stack_frame_x64.h" |
| 19 | #elif defined(TARGET_ARCH_ARM) |
| 20 | #include "vm/stack_frame_arm.h" |
| 21 | #elif defined(TARGET_ARCH_ARM64) |
| 22 | #include "vm/stack_frame_arm64.h" |
| 23 | #else |
| 24 | #error Unknown architecture. |
| 25 | #endif |
| 26 | |
| 27 | namespace dart { |
| 28 | |
| 29 | // Forward declarations. |
| 30 | class ObjectPointerVisitor; |
| 31 | class LocalVariable; |
| 32 | |
| 33 | extern FrameLayout runtime_frame_layout; |
| 34 | |
| 35 | // Generic stack frame. |
| 36 | class StackFrame : public ValueObject { |
| 37 | public: |
| 38 | virtual ~StackFrame() {} |
| 39 | |
| 40 | // Accessors to get the pc, sp and fp of a frame. |
| 41 | uword sp() const { return sp_; } |
| 42 | uword fp() const { return fp_; } |
| 43 | uword pc() const { return pc_; } |
| 44 | |
| 45 | // The pool pointer is not implemented on all architectures. |
| 46 | static int SavedCallerPpSlotFromFp() { |
| 47 | // Never called on an interpreter frame. |
| 48 | if (runtime_frame_layout.saved_caller_pp_from_fp != |
| 49 | kSavedCallerFpSlotFromFp) { |
| 50 | return runtime_frame_layout.saved_caller_pp_from_fp; |
| 51 | } |
| 52 | UNREACHABLE(); |
| 53 | return 0; |
| 54 | } |
| 55 | |
| 56 | bool IsMarkedForLazyDeopt() const { |
| 57 | ASSERT(!is_interpreted()); |
| 58 | uword raw_pc = |
| 59 | *reinterpret_cast<uword*>(sp() + (kSavedPcSlotFromSp * kWordSize)); |
| 60 | return raw_pc == StubCode::DeoptimizeLazyFromReturn().EntryPoint(); |
| 61 | } |
| 62 | void MarkForLazyDeopt() { |
| 63 | ASSERT(!is_interpreted()); |
| 64 | set_pc(StubCode::DeoptimizeLazyFromReturn().EntryPoint()); |
| 65 | } |
| 66 | void UnmarkForLazyDeopt() { |
| 67 | // If this frame was marked for lazy deopt, pc_ was computed to be the |
| 68 | // original return address using the pending deopts table in GetCallerPc. |
| 69 | // Write this value back into the frame. |
| 70 | ASSERT(!is_interpreted()); |
| 71 | uword original_pc = pc(); |
| 72 | ASSERT(original_pc != StubCode::DeoptimizeLazyFromReturn().EntryPoint()); |
| 73 | set_pc(original_pc); |
| 74 | } |
| 75 | |
| 76 | void set_pc(uword value) { |
| 77 | *reinterpret_cast<uword*>(sp() + ((is_interpreted() ? kKBCSavedPcSlotFromSp |
| 78 | : kSavedPcSlotFromSp) * |
| 79 | kWordSize)) = value; |
| 80 | pc_ = value; |
| 81 | } |
| 82 | |
| 83 | void set_pc_marker(CodePtr code) { |
| 84 | *reinterpret_cast<CodePtr*>( |
| 85 | fp() + ((is_interpreted() ? kKBCPcMarkerSlotFromFp |
| 86 | : runtime_frame_layout.code_from_fp) * |
| 87 | kWordSize)) = code; |
| 88 | } |
| 89 | |
| 90 | // Visit objects in the frame. |
| 91 | virtual void VisitObjectPointers(ObjectPointerVisitor* visitor); |
| 92 | |
| 93 | const char* ToCString() const; |
| 94 | |
| 95 | // Check validity of a frame, used for assertion purposes. |
| 96 | virtual bool IsValid() const; |
| 97 | |
| 98 | // Returns true iff the current frame is a bare instructions dart frame. |
| 99 | bool IsBareInstructionsDartFrame() const; |
| 100 | |
| 101 | // Returns true iff the current frame is a bare instructions stub frame. |
| 102 | bool IsBareInstructionsStubFrame() const; |
| 103 | |
| 104 | // Frame type. |
| 105 | virtual bool IsDartFrame(bool validate = true) const { |
| 106 | ASSERT(!validate || IsValid()); |
| 107 | return !(IsEntryFrame() || IsExitFrame() || IsStubFrame()); |
| 108 | } |
| 109 | virtual bool IsStubFrame() const; |
| 110 | virtual bool IsEntryFrame() const { return false; } |
| 111 | virtual bool IsExitFrame() const { return false; } |
| 112 | |
| 113 | virtual bool is_interpreted() const { return is_interpreted_; } |
| 114 | |
| 115 | FunctionPtr LookupDartFunction() const; |
| 116 | CodePtr LookupDartCode() const; |
| 117 | BytecodePtr LookupDartBytecode() const; |
| 118 | bool FindExceptionHandler(Thread* thread, |
| 119 | uword* handler_pc, |
| 120 | bool* needs_stacktrace, |
| 121 | bool* is_catch_all, |
| 122 | bool* is_optimized) const; |
| 123 | // Returns token_pos of the pc(), or -1 if none exists. |
| 124 | TokenPosition GetTokenPos() const; |
| 125 | |
| 126 | static void DumpCurrentTrace(); |
| 127 | |
| 128 | uword GetCallerSp() const { |
| 129 | return fp() + |
| 130 | ((is_interpreted() ? kKBCCallerSpSlotFromFp : kCallerSpSlotFromFp) * |
| 131 | kWordSize); |
| 132 | } |
| 133 | |
| 134 | protected: |
| 135 | explicit StackFrame(Thread* thread) |
| 136 | : fp_(0), sp_(0), pc_(0), thread_(thread), is_interpreted_(false) {} |
| 137 | |
| 138 | // Name of the frame, used for generic frame printing functionality. |
| 139 | virtual const char* GetName() const { |
| 140 | if (IsBareInstructionsStubFrame()) return "bare-stub" ; |
| 141 | if (IsStubFrame()) return "stub" ; |
| 142 | return IsBareInstructionsDartFrame() ? "bare-dart" : "dart" ; |
| 143 | } |
| 144 | |
| 145 | Isolate* isolate() const { return thread_->isolate(); } |
| 146 | IsolateGroup* isolate_group() const { return thread_->isolate_group(); } |
| 147 | |
| 148 | Thread* thread() const { return thread_; } |
| 149 | |
| 150 | private: |
| 151 | CodePtr GetCodeObject() const; |
| 152 | BytecodePtr GetBytecodeObject() const; |
| 153 | |
| 154 | uword GetCallerFp() const { |
| 155 | return *(reinterpret_cast<uword*>( |
| 156 | fp() + ((is_interpreted() ? kKBCSavedCallerFpSlotFromFp |
| 157 | : kSavedCallerFpSlotFromFp) * |
| 158 | kWordSize))); |
| 159 | } |
| 160 | |
| 161 | uword GetCallerPc() const { |
| 162 | uword raw_pc = *(reinterpret_cast<uword*>( |
| 163 | fp() + ((is_interpreted() ? kKBCSavedCallerPcSlotFromFp |
| 164 | : kSavedCallerPcSlotFromFp) * |
| 165 | kWordSize))); |
| 166 | ASSERT(raw_pc != StubCode::DeoptimizeLazyFromThrow().EntryPoint()); |
| 167 | if (raw_pc == StubCode::DeoptimizeLazyFromReturn().EntryPoint()) { |
| 168 | return isolate_group()->FindPendingDeoptAtSafepoint(GetCallerFp()); |
| 169 | } |
| 170 | return raw_pc; |
| 171 | } |
| 172 | |
| 173 | uword fp_; |
| 174 | uword sp_; |
| 175 | uword pc_; |
| 176 | Thread* thread_; |
| 177 | bool is_interpreted_; |
| 178 | |
| 179 | // The iterators FrameSetIterator and StackFrameIterator set the private |
| 180 | // fields fp_ and sp_ when they return the respective frame objects. |
| 181 | friend class FrameSetIterator; |
| 182 | friend class StackFrameIterator; |
| 183 | friend class ProfilerDartStackWalker; |
| 184 | DISALLOW_COPY_AND_ASSIGN(StackFrame); |
| 185 | }; |
| 186 | |
| 187 | // Exit frame is used to mark the transition from dart code into dart VM |
| 188 | // runtime code. |
| 189 | class ExitFrame : public StackFrame { |
| 190 | public: |
| 191 | bool IsValid() const { return sp() == 0; } |
| 192 | bool IsDartFrame(bool validate = true) const { return false; } |
| 193 | bool IsStubFrame() const { return false; } |
| 194 | bool IsExitFrame() const { return true; } |
| 195 | |
| 196 | // Visit objects in the frame. |
| 197 | virtual void VisitObjectPointers(ObjectPointerVisitor* visitor); |
| 198 | |
| 199 | protected: |
| 200 | virtual const char* GetName() const { return "exit" ; } |
| 201 | |
| 202 | private: |
| 203 | explicit ExitFrame(Thread* thread) : StackFrame(thread) {} |
| 204 | |
| 205 | friend class StackFrameIterator; |
| 206 | DISALLOW_COPY_AND_ASSIGN(ExitFrame); |
| 207 | }; |
| 208 | |
| 209 | // Entry Frame is used to mark the transition from dart VM runtime code into |
| 210 | // dart code. |
| 211 | class EntryFrame : public StackFrame { |
| 212 | public: |
| 213 | bool IsValid() const { |
| 214 | return StubCode::InInvocationStub(pc(), is_interpreted()); |
| 215 | } |
| 216 | bool IsDartFrame(bool validate = true) const { return false; } |
| 217 | bool IsStubFrame() const { return false; } |
| 218 | bool IsEntryFrame() const { return true; } |
| 219 | |
| 220 | // Visit objects in the frame. |
| 221 | virtual void VisitObjectPointers(ObjectPointerVisitor* visitor); |
| 222 | |
| 223 | protected: |
| 224 | virtual const char* GetName() const { return "entry" ; } |
| 225 | |
| 226 | private: |
| 227 | explicit EntryFrame(Thread* thread) : StackFrame(thread) {} |
| 228 | |
| 229 | friend class StackFrameIterator; |
| 230 | DISALLOW_COPY_AND_ASSIGN(EntryFrame); |
| 231 | }; |
| 232 | |
| 233 | // A StackFrameIterator can be initialized with a thread other than the |
| 234 | // current thread. Because this is generally a bad idea, it is only allowed on |
| 235 | // Windows- where it is needed for the profiler. It is the responsibility of |
| 236 | // users of StackFrameIterator to ensure that the thread given is not running |
| 237 | // concurrently. |
| 238 | class StackFrameIterator : public ValueObject { |
| 239 | public: |
| 240 | enum CrossThreadPolicy { |
| 241 | kNoCrossThreadIteration = 0, |
| 242 | kAllowCrossThreadIteration = 1, |
| 243 | }; |
| 244 | |
| 245 | // Iterators for iterating over all frames from the last ExitFrame to the |
| 246 | // first EntryFrame. |
| 247 | explicit StackFrameIterator(ValidationPolicy validation_policy, |
| 248 | Thread* thread, |
| 249 | CrossThreadPolicy cross_thread_policy); |
| 250 | StackFrameIterator(uword last_fp, |
| 251 | ValidationPolicy validation_policy, |
| 252 | Thread* thread, |
| 253 | CrossThreadPolicy cross_thread_policy); |
| 254 | |
| 255 | // Iterator for iterating over all frames from the current frame (given by its |
| 256 | // fp, sp, and pc) to the first EntryFrame. |
| 257 | StackFrameIterator(uword fp, |
| 258 | uword sp, |
| 259 | uword pc, |
| 260 | ValidationPolicy validation_policy, |
| 261 | Thread* thread, |
| 262 | CrossThreadPolicy cross_thread_policy); |
| 263 | |
| 264 | // Checks if a next frame exists. |
| 265 | bool HasNextFrame() const { return frames_.fp_ != 0; } |
| 266 | |
| 267 | // Get next frame. |
| 268 | StackFrame* NextFrame(); |
| 269 | |
| 270 | bool validate() const { return validate_; } |
| 271 | |
| 272 | private: |
| 273 | // Iterator for iterating over the set of frames (dart or stub) which exist |
| 274 | // in one EntryFrame and ExitFrame block. |
| 275 | class FrameSetIterator : public ValueObject { |
| 276 | public: |
| 277 | // Checks if a next non entry/exit frame exists in the set. |
| 278 | bool HasNext() const { |
| 279 | if (fp_ == 0) { |
| 280 | return false; |
| 281 | } |
| 282 | const uword pc = *(reinterpret_cast<uword*>( |
| 283 | sp_ + |
| 284 | ((is_interpreted() ? kKBCSavedPcSlotFromSp : kSavedPcSlotFromSp) * |
| 285 | kWordSize))); |
| 286 | return !StubCode::InInvocationStub(pc, is_interpreted()); |
| 287 | } |
| 288 | |
| 289 | // Get next non entry/exit frame in the set (assumes a next frame exists). |
| 290 | StackFrame* NextFrame(bool validate); |
| 291 | |
| 292 | private: |
| 293 | explicit FrameSetIterator(Thread* thread) |
| 294 | : fp_(0), |
| 295 | sp_(0), |
| 296 | pc_(0), |
| 297 | stack_frame_(thread), |
| 298 | thread_(thread), |
| 299 | is_interpreted_(false) {} |
| 300 | bool is_interpreted() const { return is_interpreted_; } |
| 301 | void CheckIfInterpreted(uword exit_marker); |
| 302 | void Unpoison(); |
| 303 | |
| 304 | uword fp_; |
| 305 | uword sp_; |
| 306 | uword pc_; |
| 307 | StackFrame stack_frame_; // Singleton frame returned by NextFrame(). |
| 308 | Thread* thread_; |
| 309 | bool is_interpreted_; |
| 310 | |
| 311 | friend class StackFrameIterator; |
| 312 | DISALLOW_COPY_AND_ASSIGN(FrameSetIterator); |
| 313 | }; |
| 314 | |
| 315 | // Get next exit frame. |
| 316 | ExitFrame* NextExitFrame(); |
| 317 | |
| 318 | // Get next entry frame. |
| 319 | EntryFrame* NextEntryFrame(); |
| 320 | |
| 321 | // Get an iterator to the next set of frames between an entry and exit |
| 322 | // frame. |
| 323 | FrameSetIterator* NextFrameSet() { return &frames_; } |
| 324 | |
| 325 | // Setup last or next exit frames so that we are ready to iterate over |
| 326 | // stack frames. |
| 327 | void SetupLastExitFrameData(); |
| 328 | void SetupNextExitFrameData(); |
| 329 | |
| 330 | void CheckInterpreterExitFrame(uword exit_marker); |
| 331 | |
| 332 | bool validate_; // Validate each frame as we traverse the frames. |
| 333 | EntryFrame entry_; // Singleton entry frame returned by NextEntryFrame(). |
| 334 | ExitFrame exit_; // Singleton exit frame returned by NextExitFrame(). |
| 335 | FrameSetIterator frames_; |
| 336 | StackFrame* current_frame_; // Points to the current frame in the iterator. |
| 337 | Thread* thread_; |
| 338 | |
| 339 | friend class ProfilerDartStackWalker; |
| 340 | DISALLOW_COPY_AND_ASSIGN(StackFrameIterator); |
| 341 | }; |
| 342 | |
| 343 | // Iterator for iterating over all dart frames (skips over exit frames, |
| 344 | // entry frames and stub frames). |
| 345 | // A DartFrameIterator can be initialized with an isolate other than the |
| 346 | // current thread's isolate. Because this is generally a bad idea, |
| 347 | // it is only allowed on Windows- where it is needed for the profiler. |
| 348 | // It is the responsibility of users of DartFrameIterator to ensure that the |
| 349 | // isolate given is not running concurrently on another thread. |
| 350 | class DartFrameIterator : public ValueObject { |
| 351 | public: |
| 352 | explicit DartFrameIterator( |
| 353 | Thread* thread, |
| 354 | StackFrameIterator::CrossThreadPolicy cross_thread_policy) |
| 355 | : frames_(ValidationPolicy::kDontValidateFrames, |
| 356 | thread, |
| 357 | cross_thread_policy) {} |
| 358 | explicit DartFrameIterator( |
| 359 | uword last_fp, |
| 360 | Thread* thread, |
| 361 | StackFrameIterator::CrossThreadPolicy cross_thread_policy) |
| 362 | : frames_(last_fp, |
| 363 | ValidationPolicy::kDontValidateFrames, |
| 364 | thread, |
| 365 | cross_thread_policy) {} |
| 366 | |
| 367 | DartFrameIterator(uword fp, |
| 368 | uword sp, |
| 369 | uword pc, |
| 370 | Thread* thread, |
| 371 | StackFrameIterator::CrossThreadPolicy cross_thread_policy) |
| 372 | : frames_(fp, |
| 373 | sp, |
| 374 | pc, |
| 375 | ValidationPolicy::kDontValidateFrames, |
| 376 | thread, |
| 377 | cross_thread_policy) {} |
| 378 | |
| 379 | // Get next dart frame. |
| 380 | StackFrame* NextFrame() { |
| 381 | StackFrame* frame = frames_.NextFrame(); |
| 382 | while (frame != NULL && !frame->IsDartFrame(frames_.validate())) { |
| 383 | frame = frames_.NextFrame(); |
| 384 | } |
| 385 | return frame; |
| 386 | } |
| 387 | |
| 388 | private: |
| 389 | StackFrameIterator frames_; |
| 390 | |
| 391 | DISALLOW_COPY_AND_ASSIGN(DartFrameIterator); |
| 392 | }; |
| 393 | |
| 394 | // Iterator for iterating over all inlined dart functions in an optimized |
| 395 | // dart frame (the iteration includes the function that is inlining the |
| 396 | // other functions). |
| 397 | class InlinedFunctionsIterator : public ValueObject { |
| 398 | public: |
| 399 | InlinedFunctionsIterator(const Code& code, uword pc); |
| 400 | bool Done() const { return index_ == -1; } |
| 401 | void Advance(); |
| 402 | |
| 403 | FunctionPtr function() const { |
| 404 | ASSERT(!Done()); |
| 405 | return function_.raw(); |
| 406 | } |
| 407 | |
| 408 | uword pc() const { |
| 409 | ASSERT(!Done()); |
| 410 | return pc_; |
| 411 | } |
| 412 | |
| 413 | CodePtr code() const { |
| 414 | ASSERT(!Done()); |
| 415 | return code_.raw(); |
| 416 | } |
| 417 | |
| 418 | #if !defined(DART_PRECOMPILED_RUNTIME) |
| 419 | intptr_t GetDeoptFpOffset() const; |
| 420 | #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| 421 | |
| 422 | private: |
| 423 | void SetDone() { index_ = -1; } |
| 424 | |
| 425 | intptr_t index_; |
| 426 | intptr_t num_materializations_; |
| 427 | intptr_t dest_frame_size_; |
| 428 | Code& code_; |
| 429 | TypedData& deopt_info_; |
| 430 | Function& function_; |
| 431 | uword pc_; |
| 432 | GrowableArray<DeoptInstr*> deopt_instructions_; |
| 433 | ObjectPool& object_table_; |
| 434 | |
| 435 | DISALLOW_COPY_AND_ASSIGN(InlinedFunctionsIterator); |
| 436 | }; |
| 437 | |
| 438 | #if defined(DEBUG) |
| 439 | void ValidateFrames(); |
| 440 | #endif |
| 441 | |
| 442 | DART_FORCE_INLINE static intptr_t LocalVarIndex(intptr_t fp_offset, |
| 443 | intptr_t var_index) { |
| 444 | return fp_offset + var_index; |
| 445 | } |
| 446 | |
| 447 | DART_FORCE_INLINE static uword ParamAddress(uword fp, intptr_t reverse_index) { |
| 448 | return fp + (kParamEndSlotFromFp * kWordSize) + (reverse_index * kWordSize); |
| 449 | } |
| 450 | |
| 451 | // Both fp and other_fp are compiled code frame pointers. |
| 452 | DART_FORCE_INLINE static bool IsCalleeFrameOf(uword fp, uword other_fp) { |
| 453 | return other_fp < fp; |
| 454 | } |
| 455 | |
| 456 | // Both fp and other_fp are bytecode frame pointers. |
| 457 | DART_FORCE_INLINE static bool IsBytecodeCalleeFrameOf(uword fp, |
| 458 | uword other_fp) { |
| 459 | return other_fp > fp; |
| 460 | } |
| 461 | |
| 462 | // Value for stack limit that is used to cause an interrupt. |
| 463 | static const uword kInterruptStackLimit = ~static_cast<uword>(0); |
| 464 | |
| 465 | DART_FORCE_INLINE static uword LocalVarAddress(uword fp, intptr_t index) { |
| 466 | return fp + LocalVarIndex(0, index) * kWordSize; |
| 467 | } |
| 468 | |
| 469 | } // namespace dart |
| 470 | |
| 471 | #endif // RUNTIME_VM_STACK_FRAME_H_ |
| 472 | |