Index: runtime/vm/profiler_service.cc |
diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc |
index a4e6b835ce7a6fdda2704a892a7588a8ff677145..9a0d5fc07a19f6d501db1a3d3a9008c122bfcccf 100644 |
--- a/runtime/vm/profiler_service.cc |
+++ b/runtime/vm/profiler_service.cc |
@@ -797,7 +797,8 @@ class ProfileCodeTable : public ZoneAllocated { |
ProfileTrieNode::ProfileTrieNode(intptr_t table_index) |
: table_index_(table_index), |
count_(0), |
- children_(0) { |
+ children_(0), |
+ frame_id_(-1) { |
ASSERT(table_index_ >= 0); |
} |
@@ -1056,6 +1057,7 @@ class ProfileBuilder : public ValueObject { |
return; |
} |
samples_ = sample_buffer->BuildProcessedSampleBuffer(filter_); |
+ profile_->samples_ = samples_; |
profile_->sample_count_ = samples_->length(); |
} |
@@ -1333,6 +1335,8 @@ class ProfileBuilder : public ValueObject { |
ASSERT(sample->At(frame_index) != 0); |
current = ProcessFrame(current, sample_index, sample, frame_index); |
} |
+ |
+ sample->set_timeline_trie(current); |
} |
} |
@@ -1973,6 +1977,8 @@ class ProfileBuilder : public ValueObject { |
Profile::Profile(Isolate* isolate) |
: isolate_(isolate), |
+ zone_(Thread::Current()->zone()), |
+ samples_(NULL), |
live_code_(NULL), |
dead_code_(NULL), |
tag_code_(NULL), |
@@ -2034,16 +2040,90 @@ ProfileTrieNode* Profile::GetTrieRoot(TrieKind trie_kind) { |
} |
-void Profile::PrintJSON(JSONStream* stream) { |
- ScopeTimer sw("Profile::PrintJSON", FLAG_trace_profiler); |
+void Profile::PrintHeaderJSON(JSONObject* obj) { |
+ obj->AddProperty("samplePeriod", |
+ static_cast<intptr_t>(FLAG_profile_period)); |
+ obj->AddProperty("stackDepth", |
+ static_cast<intptr_t>(FLAG_max_profile_depth)); |
+ obj->AddProperty("sampleCount", sample_count()); |
+ obj->AddProperty("timeSpan", MicrosecondsToSeconds(GetTimeSpan())); |
+} |
+ |
+ |
+void Profile::PrintTimelineFrameJSON(JSONObject* frames, |
+ ProfileTrieNode* current, |
+ ProfileTrieNode* parent, |
+ intptr_t* next_id) { |
+ ASSERT(current->frame_id() == -1); |
+ const intptr_t id = *next_id; |
+ *next_id = id + 1; |
+ current->set_frame_id(id); |
+ ASSERT(current->frame_id() != -1); |
+ |
+ { |
+ // The samples from many isolates may be merged into a single timeline, |
+ // so prefix frames id with the isolate. |
+ intptr_t isolate_id = reinterpret_cast<intptr_t>(isolate_); |
+ const char* key = zone_->PrintToString("%" Pd "-%" Pd, |
+ isolate_id, current->frame_id()); |
+ JSONObject frame(frames, key); |
+ frame.AddProperty("category", "Dart"); |
+ ProfileFunction* func = GetFunction(current->table_index()); |
+ frame.AddProperty("name", func->Name()); |
+ if (parent != NULL) { |
+ ASSERT(parent->frame_id() != -1); |
+ frame.AddPropertyF("parent", "%" Pd "-%" Pd, |
+ isolate_id, parent->frame_id()); |
+ } |
+ } |
+ |
+ for (intptr_t i = 0; i < current->NumChildren(); i++) { |
+ ProfileTrieNode* child = current->At(i); |
+ PrintTimelineFrameJSON(frames, child, current, next_id); |
+ } |
+} |
+ |
+ |
+void Profile::PrintTimelineJSON(JSONStream* stream) { |
+ ScopeTimer sw("Profile::PrintTimelineJSON", FLAG_trace_profiler); |
+ JSONObject obj(stream); |
+ obj.AddProperty("type", "_CpuProfileTimeline"); |
+ PrintHeaderJSON(&obj); |
+ { |
+ JSONObject frames(&obj, "stackFrames"); |
+ ProfileTrieNode* root = GetTrieRoot(kInclusiveFunction); |
+ intptr_t next_id = 0; |
+ PrintTimelineFrameJSON(&frames, root, NULL, &next_id); |
+ } |
+ { |
+ JSONArray events(&obj, "traceEvents"); |
+ intptr_t pid = OS::ProcessId(); |
+ intptr_t isolate_id = reinterpret_cast<intptr_t>(isolate_); |
+ for (intptr_t sample_index = 0; |
+ sample_index < samples_->length(); |
+ sample_index++) { |
+ ProcessedSample* sample = samples_->At(sample_index); |
+ JSONObject event(&events); |
+ event.AddProperty("ph", "P"); // kind = sample event |
+ event.AddProperty64("pid", pid); |
+ event.AddProperty64("tid", OSThread::ThreadIdToIntPtr(sample->tid())); |
+ event.AddPropertyTimeMicros("ts", sample->timestamp()); |
+ event.AddProperty("cat", "Dart"); |
+ |
+ ProfileTrieNode* trie = sample->timeline_trie(); |
+ ASSERT(trie->frame_id() != -1); |
+ event.AddPropertyF("sf", "%" Pd "-%" Pd, |
+ isolate_id, trie->frame_id()); |
+ } |
+ } |
+} |
+ |
+ |
+void Profile::PrintProfileJSON(JSONStream* stream) { |
+ ScopeTimer sw("Profile::PrintProfileJSON", FLAG_trace_profiler); |
JSONObject obj(stream); |
obj.AddProperty("type", "_CpuProfile"); |
- obj.AddProperty("samplePeriod", |
- static_cast<intptr_t>(FLAG_profile_period)); |
- obj.AddProperty("stackDepth", |
- static_cast<intptr_t>(FLAG_max_profile_depth)); |
- obj.AddProperty("sampleCount", sample_count()); |
- obj.AddProperty("timeSpan", MicrosecondsToSeconds(GetTimeSpan())); |
+ PrintHeaderJSON(&obj); |
{ |
JSONArray codes(&obj, "codes"); |
for (intptr_t i = 0; i < live_code_->length(); i++) { |
@@ -2196,7 +2276,8 @@ void ProfilerService::PrintJSONImpl(Thread* thread, |
JSONStream* stream, |
Profile::TagOrder tag_order, |
intptr_t extra_tags, |
- SampleFilter* filter) { |
+ SampleFilter* filter, |
+ bool as_timeline) { |
Isolate* isolate = thread->isolate(); |
// Disable thread interrupts while processing the buffer. |
DisableThreadInterruptsScope dtis(thread); |
@@ -2212,7 +2293,11 @@ void ProfilerService::PrintJSONImpl(Thread* thread, |
HANDLESCOPE(thread); |
Profile profile(isolate); |
profile.Build(thread, filter, tag_order, extra_tags); |
- profile.PrintJSON(stream); |
+ if (as_timeline) { |
+ profile.PrintTimelineJSON(stream); |
+ } else { |
+ profile.PrintProfileJSON(stream); |
+ } |
} |
} |
@@ -2235,7 +2320,8 @@ void ProfilerService::PrintJSON(JSONStream* stream, |
Thread* thread = Thread::Current(); |
Isolate* isolate = thread->isolate(); |
NoAllocationSampleFilter filter(isolate); |
- PrintJSONImpl(thread, stream, tag_order, extra_tags, &filter); |
+ const bool as_timeline = false; |
+ PrintJSONImpl(thread, stream, tag_order, extra_tags, &filter, as_timeline); |
} |
@@ -2263,7 +2349,18 @@ void ProfilerService::PrintAllocationJSON(JSONStream* stream, |
Thread* thread = Thread::Current(); |
Isolate* isolate = thread->isolate(); |
ClassAllocationSampleFilter filter(isolate, cls); |
- PrintJSONImpl(thread, stream, tag_order, kNoExtraTags, &filter); |
+ const bool as_timeline = false; |
+ PrintJSONImpl(thread, stream, tag_order, kNoExtraTags, &filter, as_timeline); |
+} |
+ |
+ |
+void ProfilerService::PrintTimelineJSON(JSONStream* stream, |
+ Profile::TagOrder tag_order) { |
+ Thread* thread = Thread::Current(); |
+ Isolate* isolate = thread->isolate(); |
+ NoAllocationSampleFilter filter(isolate); |
+ const bool as_timeline = true; |
+ PrintJSONImpl(thread, stream, tag_order, kNoExtraTags, &filter, as_timeline); |
} |