Index: runtime/vm/profiler.cc |
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc |
index fdfba514f24d4e10ebbbe4c064d64bde793c953d..a2dec35ef97f947b7d8313003b1ddc8a50a95634 100644 |
--- a/runtime/vm/profiler.cc |
+++ b/runtime/vm/profiler.cc |
@@ -1684,21 +1684,16 @@ static void SetPCMarkerIfSafe(Sample* sample) { |
} |
-class ProfilerDartStackWalker : public ValueObject { |
+// Given an exit frame, walk the Dart stack. |
+class ProfilerDartExitStackWalker : public ValueObject { |
public: |
- ProfilerDartStackWalker(Isolate* isolate, Sample* sample) |
+ ProfilerDartExitStackWalker(Isolate* isolate, Sample* sample) |
: sample_(sample), |
frame_iterator_(isolate) { |
ASSERT(sample_ != NULL); |
} |
- ProfilerDartStackWalker(Isolate* isolate, Sample* sample, uword fp) |
- : sample_(sample), |
- frame_iterator_(fp, isolate) { |
- ASSERT(sample_ != NULL); |
- } |
- |
- int walk() { |
+ void walk() { |
intptr_t frame_index = 0; |
StackFrame* frame = frame_iterator_.NextFrame(); |
while (frame != NULL) { |
@@ -1709,7 +1704,6 @@ class ProfilerDartStackWalker : public ValueObject { |
} |
frame = frame_iterator_.NextFrame(); |
} |
- return frame_index; |
} |
private: |
@@ -1718,13 +1712,148 @@ class ProfilerDartStackWalker : public ValueObject { |
}; |
-// Notes on stack frame walking: |
-// |
-// The sampling profiler will collect up to Sample::kNumStackFrames stack frames |
-// The stack frame walking code uses the frame pointer to traverse the stack. |
+// Executing Dart code, walk the stack. |
+class ProfilerDartStackWalker : public ValueObject { |
+ public: |
+ ProfilerDartStackWalker(Isolate* isolate, |
+ Sample* sample, |
+ uword stack_lower, |
+ uword stack_upper, |
+ uword pc, |
+ uword fp, |
+ uword sp) |
+ : isolate_(isolate), |
+ sample_(sample), |
+ stack_upper_(stack_upper), |
+ stack_lower_(stack_lower) { |
+ ASSERT(sample_ != NULL); |
+ pc_ = reinterpret_cast<uword*>(pc); |
+ fp_ = reinterpret_cast<uword*>(fp); |
+ sp_ = reinterpret_cast<uword*>(sp); |
+ } |
+ |
+ void walk() { |
+ if (!ValidFramePointer()) { |
+ sample_->set_ignore_sample(true); |
+ return; |
+ } |
+ ASSERT(ValidFramePointer()); |
+ uword return_pc = InitialReturnAddress(); |
+ if (StubCode::InInvocationStubForIsolate(isolate_, return_pc)) { |
+ // Edge case- we have called out from the Invocation Stub but have not |
+ // created the stack frame of the callee. Attempt to locate the exit |
+ // frame before walking the stack. |
+ if (!NextExit() || !ValidFramePointer()) { |
+ // Nothing to sample. |
+ sample_->set_ignore_sample(true); |
+ return; |
+ } |
+ } |
+ for (int i = 0; i < FLAG_profile_depth; i++) { |
+ sample_->SetAt(i, reinterpret_cast<uword>(pc_)); |
+ if (!Next()) { |
+ return; |
+ } |
+ } |
+ } |
+ |
+ private: |
+ bool Next() { |
+ if (!ValidFramePointer()) { |
+ return false; |
+ } |
+ if (StubCode::InInvocationStubForIsolate(isolate_, |
+ reinterpret_cast<uword>(pc_))) { |
+ // In invocation stub. |
+ return NextExit(); |
+ } |
+ // In regular Dart frame. |
+ uword* new_pc = CallerPC(); |
+ // Check if we've moved into the invocation stub. |
+ if (StubCode::InInvocationStubForIsolate(isolate_, |
+ reinterpret_cast<uword>(new_pc))) { |
+ // New PC is inside invocation stub, skip. |
+ return NextExit(); |
+ } |
+ uword* new_fp = CallerFP(); |
+ if (new_fp <= fp_) { |
+ // FP didn't move to a higher address. |
+ return false; |
+ } |
+ // Success, update fp and pc. |
+ fp_ = new_fp; |
+ pc_ = new_pc; |
+ return true; |
+ } |
+ |
+ bool NextExit() { |
+ if (!ValidFramePointer()) { |
+ return false; |
+ } |
+ uword* new_fp = ExitLink(); |
+ if (new_fp == NULL) { |
+ // No exit link. |
+ return false; |
+ } |
+ if (new_fp <= fp_) { |
+ // FP didn't move to a higher address. |
+ return false; |
+ } |
+ if (!ValidFramePointer(new_fp)) { |
+ return false; |
+ } |
+ // Success, update fp and pc. |
+ fp_ = new_fp; |
+ pc_ = CallerPC(); |
+ return true; |
+ } |
+ |
+ uword InitialReturnAddress() const { |
+ ASSERT(sp_ != NULL); |
+ return *(sp_); |
+ } |
+ |
+ uword* CallerPC() const { |
+ ASSERT(fp_ != NULL); |
+ return reinterpret_cast<uword*>(*(fp_ + kSavedCallerPcSlotFromFp)); |
+ } |
+ |
+ uword* CallerFP() const { |
+ ASSERT(fp_ != NULL); |
+ return reinterpret_cast<uword*>(*(fp_ + kSavedCallerFpSlotFromFp)); |
+ } |
+ |
+ uword* ExitLink() const { |
+ ASSERT(fp_ != NULL); |
+ return reinterpret_cast<uword*>(*(fp_ + kExitLinkSlotFromEntryFp)); |
+ } |
+ |
+ bool ValidFramePointer() const { |
+ return ValidFramePointer(fp_); |
+ } |
+ |
+ bool ValidFramePointer(uword* fp) const { |
+ if (fp == NULL) { |
+ return false; |
+ } |
+ uword cursor = reinterpret_cast<uword>(fp); |
+ cursor += sizeof(fp); |
+ return (cursor >= stack_lower_) && (cursor < stack_upper_); |
+ } |
+ |
+ uword* pc_; |
+ uword* fp_; |
+ uword* sp_; |
+ Isolate* isolate_; |
+ Sample* sample_; |
+ const uword stack_upper_; |
+ uword stack_lower_; |
+}; |
+ |
+ |
// If the VM is compiled without frame pointers (which is the default on |
// recent GCC versions with optimizing enabled) the stack walking code may |
-// fail (sometimes leading to a crash). |
+// fail. |
// |
class ProfilerNativeStackWalker : public ValueObject { |
public: |
@@ -1743,7 +1872,7 @@ class ProfilerNativeStackWalker : public ValueObject { |
ASSERT(sample_ != NULL); |
} |
- void walk(Heap* heap) { |
+ void walk() { |
const uword kMaxStep = VirtualMemory::PageSize(); |
sample_->SetAt(0, original_pc_); |
@@ -1914,9 +2043,13 @@ void Profiler::RecordSampleInterruptCallback( |
// Walk the call stack. |
if (FLAG_profile_vm) { |
// Always walk the native stack collecting both native and Dart frames. |
- ProfilerNativeStackWalker stackWalker(sample, stack_lower, stack_upper, |
- state.pc, state.fp, state.sp); |
- stackWalker.walk(isolate->heap()); |
+ ProfilerNativeStackWalker stackWalker(sample, |
+ stack_lower, |
+ stack_upper, |
+ state.pc, |
+ state.fp, |
+ state.sp); |
+ stackWalker.walk(); |
} else { |
// Attempt to walk only the Dart call stack, falling back to walking |
// the native stack. |
@@ -1924,12 +2057,29 @@ void Profiler::RecordSampleInterruptCallback( |
(isolate->top_exit_frame_info() != 0) && |
(isolate->vm_tag() != VMTag::kScriptTagId)) { |
// We have a valid exit frame info, use the Dart stack walker. |
- ProfilerDartStackWalker stackWalker(isolate, sample); |
+ ProfilerDartExitStackWalker stackWalker(isolate, sample); |
+ stackWalker.walk(); |
+ } else if ((isolate->stub_code() != NULL) && |
+ (isolate->top_exit_frame_info() == 0) && |
+ (isolate->vm_tag() == VMTag::kScriptTagId)) { |
+ // We are executing Dart code. We have frame pointers. |
+ ProfilerDartStackWalker stackWalker(isolate, |
+ sample, |
+ stack_lower, |
+ stack_upper, |
+ state.pc, |
+ state.fp, |
+ state.sp); |
stackWalker.walk(); |
} else { |
- ProfilerNativeStackWalker stackWalker(sample, stack_lower, stack_upper, |
- state.pc, state.fp, state.sp); |
- stackWalker.walk(isolate->heap()); |
+ // Fall back to an extremely conservative stack walker. |
+ ProfilerNativeStackWalker stackWalker(sample, |
+ stack_lower, |
+ stack_upper, |
+ state.pc, |
+ state.fp, |
+ state.sp); |
+ stackWalker.walk(); |
} |
} |
} |