Index: runtime/vm/timeline_analysis.cc |
diff --git a/runtime/vm/timeline_analysis.cc b/runtime/vm/timeline_analysis.cc |
index 34bedda4bf06c973163a1e8201621528f17129a5..5cb2e2fb12bbb944e6646b525aad074f1452e0d5 100644 |
--- a/runtime/vm/timeline_analysis.cc |
+++ b/runtime/vm/timeline_analysis.cc |
@@ -5,11 +5,12 @@ |
#include "vm/timeline_analysis.h" |
#include "vm/flags.h" |
+#include "vm/log.h" |
+#include "vm/os_thread.h" |
namespace dart { |
-DECLARE_FLAG(bool, trace_timeline); |
- |
+DEFINE_FLAG(bool, trace_timeline_analysis, false, "Trace timeline analysis"); |
TimelineAnalysisThread::TimelineAnalysisThread(ThreadId id) |
: id_(id) { |
@@ -37,6 +38,9 @@ static int CompareBlocksLowerTimeBound(TimelineEventBlock* const* a, |
void TimelineAnalysisThread::Finalize() { |
blocks_.Sort(CompareBlocksLowerTimeBound); |
+ ISL_Print("Thread %" Px " has %" Pd " blocks\n", |
+ OSThread::ThreadIdToIntPtr(id_), |
+ blocks_.length()); |
} |
@@ -156,9 +160,13 @@ void TimelineAnalysis::DiscoverThreads() { |
continue; |
} |
if (!block->CheckBlock()) { |
- // Skip bad blocks. |
- // TODO(johnmccutchan): Make this into an error? |
- continue; |
+ if (FLAG_trace_timeline_analysis) { |
+ ISL_Print("DiscoverThreads block %" Pd " " |
+ "violates invariants.\n", block->block_index()); |
+ } |
+ SetError("Block %" Pd " violates invariants. See " |
+ "TimelineEventBlock::CheckBlock", block->block_index()); |
+ return; |
} |
TimelineAnalysisThread* thread = GetOrAddThread(block->thread()); |
ASSERT(thread != NULL); |
@@ -187,6 +195,30 @@ void TimelineAnalysis::SetError(const char* format, ...) { |
} |
+TimelineLabelPauseInfo::TimelineLabelPauseInfo(const char* name) |
+ : name_(name), |
+ inclusive_micros_(0), |
+ exclusive_micros_(0), |
+ max_duration_micros_(0) { |
+ ASSERT(name_ != NULL); |
+} |
+ |
+ |
+void TimelineLabelPauseInfo::OnPush(int64_t micros) { |
+ add_inclusive_micros(micros); |
+ add_exclusive_micros(micros); |
+ if (micros > max_duration_micros_) { |
+ max_duration_micros_ = micros; |
+ } |
+} |
+ |
+ |
+void TimelineLabelPauseInfo::OnChildPush(int64_t micros) { |
+ ASSERT(micros >= 0); |
+ add_exclusive_micros(-micros); |
+} |
+ |
+ |
TimelinePauses::TimelinePauses(Zone* zone, |
Isolate* isolate, |
TimelineEventRecorder* recorder) |
@@ -194,7 +226,170 @@ TimelinePauses::TimelinePauses(Zone* zone, |
} |
-void TimelinePauses::CalculatePauseTimes() { |
+void TimelinePauses::Setup() { |
+ BuildThreads(); |
+} |
+ |
+ |
+void TimelinePauses::CalculatePauseTimesForThread(ThreadId tid) { |
+ if (has_error()) { |
+ return; |
+ } |
+ TimelineAnalysisThread* thread = GetThread(tid); |
+ if (thread == NULL) { |
+ SetError("Thread %" Px " does not exist.", OSThread::ThreadIdToIntPtr(tid)); |
+ return; |
+ } |
+ ProcessThread(thread); |
+} |
+ |
+ |
+TimelineLabelPauseInfo* TimelinePauses::GetLabel(const char* name) const { |
+ ASSERT(name != NULL); |
+ // Linear lookup because we expect N (# of labels in an isolate) to be small. |
+ for (intptr_t i = 0; i < labels_.length(); i++) { |
+ TimelineLabelPauseInfo* label = labels_.At(i); |
+ if (strcmp(label->name(), name) == 0) { |
+ return label; |
+ } |
+ } |
+ return NULL; |
+} |
+ |
+ |
+int64_t TimelinePauses::InclusiveTime(const char* name) const { |
+ TimelineLabelPauseInfo* pause_info = GetLabel(name); |
+ ASSERT(pause_info != NULL); |
+ return pause_info->inclusive_micros(); |
+} |
+ |
+ |
+int64_t TimelinePauses::ExclusiveTime(const char* name) const { |
+ TimelineLabelPauseInfo* pause_info = GetLabel(name); |
+ ASSERT(pause_info != NULL); |
+ return pause_info->exclusive_micros(); |
+} |
+ |
+ |
+int64_t TimelinePauses::MaxDurationTime(const char* name) const { |
+ TimelineLabelPauseInfo* pause_info = GetLabel(name); |
+ ASSERT(pause_info != NULL); |
+ return pause_info->max_duration_micros(); |
+} |
+ |
+ |
+void TimelinePauses::ProcessThread(TimelineAnalysisThread* thread) { |
+ ASSERT(thread != NULL); |
+ stack_.Clear(); |
+ labels_.Clear(); |
+ |
+ TimelineAnalysisThreadEventIterator it(thread); |
+ if (FLAG_trace_timeline_analysis) { |
+ ISL_Print(">>> TimelinePauses::ProcessThread %" Px "\n", |
+ OSThread::ThreadIdToIntPtr(thread->id())); |
+ } |
+ intptr_t event_count = 0; |
+ while (it.HasNext()) { |
+ TimelineEvent* event = it.Next(); |
+ if (!event->IsFinishedDuration()) { |
+ // We only care about finished duration events. |
+ continue; |
+ } |
+ int64_t start = event->TimeOrigin(); |
+ PopFinished(start); |
+ if (!CheckStack(event)) { |
+ SetError("Duration check fail."); |
+ return; |
+ } |
+ event_count++; |
+ Push(event); |
+ } |
+ // Pop remaining stack. |
+ PopFinished(kMaxInt64); |
+ if (FLAG_trace_timeline_analysis) { |
+ ISL_Print("<<< TimelinePauses::ProcessThread %" Px " had %" Pd " events\n", |
+ OSThread::ThreadIdToIntPtr(thread->id()), |
+ event_count); |
+ } |
+} |
+ |
+ |
+// Verify that |event| is contained within all parent events on the stack. |
+bool TimelinePauses::CheckStack(TimelineEvent* event) { |
+ ASSERT(event != NULL); |
+ for (intptr_t i = 0; i < stack_.length(); i++) { |
+ TimelineEvent* slot = stack_.At(i); |
+ if (!slot->DurationContains(event)) { |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+ |
+void TimelinePauses::PopFinished(int64_t start) { |
+ while (stack_.length() > 0) { |
+ TimelineEvent* top = stack_.Last(); |
+ if (top->DurationFinishedBefore(start)) { |
+ // Top of stack completes before |start|. |
+ stack_.RemoveLast(); |
+ if (FLAG_trace_timeline_analysis) { |
+ ISL_Print("Popping %s (%" Pd64 " <= %" Pd64 ")\n", |
+ top->label(), |
+ top->TimeEnd(), |
+ start); |
+ } |
+ } else { |
+ return; |
+ } |
+ } |
+} |
+ |
+ |
+void TimelinePauses::Push(TimelineEvent* event) { |
+ TimelineLabelPauseInfo* pause_info = GetOrAddLabel(event->label()); |
+ ASSERT(pause_info != NULL); |
+ // |pause_info| will be running for |event->TimeDuration()|. |
+ if (FLAG_trace_timeline_analysis) { |
+ ISL_Print("Pushing %s %" Pd64 " us\n", |
+ pause_info->name(), |
+ event->TimeDuration()); |
+ } |
+ pause_info->OnPush(event->TimeDuration()); |
+ TimelineLabelPauseInfo* top_pause_info = GetTopLabel(); |
+ if (top_pause_info != NULL) { |
+ // |top_pause_info| is under |event|'s shadow, adjust the exclusive micros. |
+ if (FLAG_trace_timeline_analysis) { |
+ ISL_Print("Adjusting %s by %" Pd64 " us\n", |
+ top_pause_info->name(), |
+ event->TimeDuration()); |
+ } |
+ top_pause_info->OnChildPush(event->TimeDuration()); |
+ } |
+ // Push onto the stack. |
+ stack_.Add(event); |
+} |
+ |
+ |
+TimelineLabelPauseInfo* TimelinePauses::GetTopLabel() { |
+ if (stack_.length() == 0) { |
+ return NULL; |
+ } |
+ TimelineEvent* event = stack_.Last(); |
+ return GetLabel(event->label()); |
+} |
+ |
+ |
+TimelineLabelPauseInfo* TimelinePauses::GetOrAddLabel(const char* name) { |
+ ASSERT(name != NULL); |
+ TimelineLabelPauseInfo* label = GetLabel(name); |
+ if (label != NULL) { |
+ return label; |
+ } |
+ // New label. |
+ label = new TimelineLabelPauseInfo(name); |
+ labels_.Add(label); |
+ return label; |
} |
} // namespace dart |