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 | |