Index: runtime/vm/profiler.cc |
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc |
index d8ec5da69b8c6796a9570be71e38a0e308e84d52..49df08a5cb5c2fdffcee93c37e676556b45c76f7 100644 |
--- a/runtime/vm/profiler.cc |
+++ b/runtime/vm/profiler.cc |
@@ -13,6 +13,7 @@ |
#include "vm/object.h" |
#include "vm/os.h" |
#include "vm/profiler.h" |
+#include "vm/reusable_handles.h" |
#include "vm/signal_handler.h" |
#include "vm/simulator.h" |
#include "vm/stack_frame.h" |
@@ -910,6 +911,98 @@ class CodeRegionTable : public ValueObject { |
}; |
+class FixTopFrameVisitor : public SampleVisitor { |
+ public: |
+ explicit FixTopFrameVisitor(Isolate* isolate) |
+ : SampleVisitor(isolate), |
+ vm_isolate_(Dart::vm_isolate()) { |
+ } |
+ |
+ void VisitSample(Sample* sample) { |
+ if (sample->processed()) { |
+ // Already processed. |
+ return; |
+ } |
+ REUSABLE_CODE_HANDLESCOPE(isolate()); |
+ // Mark that we've processed this sample. |
+ sample->set_processed(true); |
+ // Lookup code object for leaf frame. |
+ Code& code = reused_code_handle.Handle(); |
+ code = FindCodeForPC(sample->At(0)); |
+ sample->set_leaf_frame_is_dart(!code.IsNull()); |
+ if (sample->pc_marker() == 0) { |
+ // No pc marker. Nothing to do. |
+ return; |
+ } |
+ if (!code.IsNull() && (code.compile_timestamp() > sample->timestamp())) { |
+ // Code compiled after sample. Ignore. |
+ return; |
+ } |
+ if (sample->leaf_frame_is_dart()) { |
+ CheckForMissingDartFrame(code, sample); |
+ } |
+ } |
+ |
+ private: |
+ void CheckForMissingDartFrame(const Code& code, Sample* sample) const { |
+ // Some stubs (and intrinsics) do not push a frame onto the stack leaving |
+ // the frame pointer in the caller. |
+ // |
+ // PC -> STUB |
+ // FP -> DART3 <-+ |
+ // DART2 <-| <- TOP FRAME RETURN ADDRESS. |
+ // DART1 <-| |
+ // ..... |
+ // |
+ // In this case, traversing the linked stack frames will not collect a PC |
+ // inside DART3. The stack will incorrectly be: STUB, DART2, DART1. |
+ // In Dart code, after pushing the FP onto the stack, an IP in the current |
+ // function is pushed onto the stack as well. This stack slot is called |
+ // the PC marker. We can use the PC marker to insert DART3 into the stack |
+ // so that it will correctly be: STUB, DART3, DART2, DART1. Note the |
+ // inserted PC may not accurately reflect the true return address from STUB. |
+ ASSERT(!code.IsNull()); |
+ if (sample->sp() == sample->fp()) { |
+ // Haven't pushed pc marker yet. |
+ return; |
+ } |
+ uword pc_marker = sample->pc_marker(); |
+ if (code.ContainsInstructionAt(pc_marker)) { |
+ // PC marker is in the same code as pc, no missing frame. |
+ return; |
+ } |
+ if (!ContainedInDartCodeHeaps(pc_marker)) { |
+ // Not a valid PC marker. |
+ return; |
+ } |
+ sample->InsertCallerForTopFrame(pc_marker); |
+ } |
+ |
+ bool ContainedInDartCodeHeaps(uword pc) const { |
+ return isolate()->heap()->CodeContains(pc) || |
+ vm_isolate()->heap()->CodeContains(pc); |
+ } |
+ |
+ Isolate* vm_isolate() const { |
+ return vm_isolate_; |
+ } |
+ |
+ RawCode* FindCodeForPC(uword pc) const { |
+ // Check current isolate for pc. |
+ if (isolate()->heap()->CodeContains(pc)) { |
+ return Code::LookupCode(pc); |
+ } |
+ // Check VM isolate for pc. |
+ if (vm_isolate()->heap()->CodeContains(pc)) { |
+ return Code::LookupCodeInVmIsolate(pc); |
+ } |
+ return Code::null(); |
+ } |
+ |
+ Isolate* vm_isolate_; |
+}; |
+ |
+ |
class CodeRegionTableBuilder : public SampleVisitor { |
public: |
CodeRegionTableBuilder(Isolate* isolate, |
@@ -1356,6 +1449,12 @@ void Profiler::PrintJSON(Isolate* isolate, JSONStream* stream, |
&dead_code_table, |
&tag_code_table); |
{ |
+ // Preprocess samples and fix the caller when the top PC is in a |
+ // stub or intrinsic without a frame. |
+ FixTopFrameVisitor fixTopFrame(isolate); |
+ sample_buffer->VisitSamples(&fixTopFrame); |
+ } |
+ { |
// Build CodeRegion tables. |
ScopeStopwatch sw("CodeRegionTableBuilder"); |
sample_buffer->VisitSamples(&builder); |
@@ -1600,6 +1699,8 @@ class ProfilerNativeStackWalker : public ValueObject { |
// the isolates stack limit. |
lower_bound_ = original_sp_; |
} |
+ // Store the PC marker for the top frame. |
+ sample_->set_pc_marker(GetCurrentFramePcMarker(fp)); |
int i = 0; |
for (; i < FLAG_profile_depth; i++) { |
if (FLAG_profile_verify_stack_walk) { |
@@ -1662,6 +1763,13 @@ class ProfilerNativeStackWalker : public ValueObject { |
return reinterpret_cast<uword*>(*(fp + kSavedCallerFpSlotFromFp)); |
} |
+ uword GetCurrentFramePcMarker(uword* fp) const { |
+ if (!ValidFramePointer(fp)) { |
+ return 0; |
+ } |
+ return *(fp + kPcMarkerSlotFromFp); |
+ } |
+ |
bool ValidFramePointer(uword* fp) const { |
if (fp == NULL) { |
return false; |
@@ -1672,7 +1780,6 @@ class ProfilerNativeStackWalker : public ValueObject { |
return r; |
} |
- |
Sample* sample_; |
const uword stack_upper_; |
const uword original_pc_; |
@@ -1705,6 +1812,8 @@ void Profiler::RecordSampleInterruptCallback( |
sample->Init(isolate, OS::GetCurrentTimeMicros(), state.tid); |
sample->set_vm_tag(isolate->vm_tag()); |
sample->set_user_tag(isolate->user_tag()); |
+ sample->set_sp(state.sp); |
+ sample->set_fp(state.fp); |
if (FLAG_profile_native_stack) { |
// Collect native and Dart frames. |
uword stack_lower = 0; |