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
27namespace dart {
28
29// Forward declarations.
30class ObjectPointerVisitor;
31class LocalVariable;
32
33extern FrameLayout runtime_frame_layout;
34
35// Generic stack frame.
36class 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.
189class 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.
211class 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.
238class 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.
350class 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).
397class 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)
439void ValidateFrames();
440#endif
441
442DART_FORCE_INLINE static intptr_t LocalVarIndex(intptr_t fp_offset,
443 intptr_t var_index) {
444 return fp_offset + var_index;
445}
446
447DART_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.
452DART_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.
457DART_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.
463static const uword kInterruptStackLimit = ~static_cast<uword>(0);
464
465DART_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