// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include #include "platform/assert.h" #include "vm/dart_api_impl.h" #include "vm/dart_api_state.h" #include "vm/globals.h" #include "vm/timeline.h" #include "vm/timeline_analysis.h" #include "vm/unit_test.h" namespace dart { #ifndef PRODUCT template class TimelineRecorderOverride : public ValueObject { public: TimelineRecorderOverride() : recorder_(Timeline::recorder()) { Timeline::recorder_ = new T(); } explicit TimelineRecorderOverride(T* recorder) : recorder_(Timeline::recorder()) { Timeline::recorder_ = recorder; } ~TimelineRecorderOverride() { Timeline::Clear(); delete Timeline::recorder_; Timeline::recorder_ = recorder_; } T* recorder() { return static_cast(Timeline::recorder()); } private: TimelineEventRecorder* recorder_; }; class TimelineTestHelper : public AllStatic { public: static void SetStream(TimelineEvent* event, TimelineStream* stream) { event->StreamInit(stream); } static void FakeThreadEvent(TimelineEventBlock* block, intptr_t ftid, const char* label = "fake", TimelineStream* stream = NULL) { TimelineEvent* event = block->StartEvent(); ASSERT(event != NULL); event->DurationBegin(label); event->thread_ = OSThread::ThreadIdFromIntPtr(ftid); if (stream != NULL) { event->StreamInit(stream); } } static void SetBlockThread(TimelineEventBlock* block, intptr_t ftid) { block->thread_id_ = OSThread::ThreadIdFromIntPtr(ftid); } static void FakeDuration(TimelineEventRecorder* recorder, const char* label, int64_t start, int64_t end) { ASSERT(recorder != NULL); ASSERT(start < end); ASSERT(label != NULL); TimelineEvent* event = recorder->StartEvent(); ASSERT(event != NULL); event->Duration(label, start, end); event->Complete(); } static void FakeBegin(TimelineEventRecorder* recorder, const char* label, int64_t start) { ASSERT(recorder != NULL); ASSERT(label != NULL); ASSERT(start >= 0); TimelineEvent* event = recorder->StartEvent(); ASSERT(event != NULL); event->Begin(label, start); event->Complete(); } static void FakeEnd(TimelineEventRecorder* recorder, const char* label, int64_t end) { ASSERT(recorder != NULL); ASSERT(label != NULL); ASSERT(end >= 0); TimelineEvent* event = recorder->StartEvent(); ASSERT(event != NULL); event->End(label, end); event->Complete(); } static void FinishBlock(TimelineEventBlock* block) { block->Finish(); } }; TEST_CASE(TimelineEventIsValid) { // Create a test stream. TimelineStream stream("testStream", "testStream", true); TimelineEvent event; TimelineTestHelper::SetStream(&event, &stream); // Starts invalid. EXPECT(!event.IsValid()); // Becomes valid. event.Instant("hello"); EXPECT(event.IsValid()); // Becomes invalid. event.Reset(); EXPECT(!event.IsValid()); } TEST_CASE(TimelineEventDuration) { // Create a test stream. TimelineStream stream("testStream", "testStream", true); // Create a test event. TimelineEvent event; TimelineTestHelper::SetStream(&event, &stream); event.DurationBegin("apple"); // Measure the duration. int64_t current_duration = event.TimeDuration(); event.DurationEnd(); // Verify that duration is larger. EXPECT_GE(event.TimeDuration(), current_duration); } TEST_CASE(TimelineEventDurationPrintJSON) { // Create a test stream. TimelineStream stream("testStream", "testStream", true); // Create a test event. TimelineEvent event; TimelineTestHelper::SetStream(&event, &stream); event.DurationBegin("apple"); { // Test printing to JSON. JSONStream js; event.PrintJSON(&js); // Check category EXPECT_SUBSTRING("\"cat\":\"testStream\"", js.ToCString()); // Check name. EXPECT_SUBSTRING("\"name\":\"apple\"", js.ToCString()); // Check phase. EXPECT_SUBSTRING("\"ph\":\"X\"", js.ToCString()); // Check that ts key is present. EXPECT_SUBSTRING("\"ts\":", js.ToCString()); // Check that dur key is present. EXPECT_SUBSTRING("\"dur\":", js.ToCString()); } event.DurationEnd(); } #if defined(HOST_OS_ANDROID) || defined(HOST_OS_LINUX) TEST_CASE(TimelineEventPrintSystrace) { const intptr_t kBufferLength = 1024; char buffer[kBufferLength]; // Create a test stream. TimelineStream stream("testStream", "testStream", true); // Create a test event. TimelineEvent event; TimelineTestHelper::SetStream(&event, &stream); // Test a Begin event. event.Begin("apple", 1, 2); TimelineEventSystraceRecorder::PrintSystrace(&event, &buffer[0], kBufferLength); EXPECT_SUBSTRING("|apple", buffer); EXPECT_SUBSTRING("B|", buffer); // Test an End event. event.End("apple", 2, 3); TimelineEventSystraceRecorder::PrintSystrace(&event, &buffer[0], kBufferLength); EXPECT_STREQ("E", buffer); // Test a Counter event. We only report the first counter value (in this case // "4"). event.Counter("CTR", 1); // We have two counters. event.SetNumArguments(2); // Set the first counter value. event.CopyArgument(0, "cats", "4"); // Set the second counter value. event.CopyArgument(1, "dogs", "1"); TimelineEventSystraceRecorder::PrintSystrace(&event, &buffer[0], kBufferLength); EXPECT_SUBSTRING("C|", buffer); EXPECT_SUBSTRING("|CTR|4", buffer); // Test a duration event. This event kind is not supported so we should // serialize it to an empty string. event.Duration("DUR", 0, 1, 2, 3); TimelineEventSystraceRecorder::PrintSystrace(&event, &buffer[0], kBufferLength); EXPECT_STREQ("", buffer); } #endif // defined(HOST_OS_ANDROID) || defined(HOST_OS_LINUX) TEST_CASE(TimelineEventArguments) { // Create a test stream. TimelineStream stream("testStream", "testStream", true); // Create a test event. TimelineEvent event; TimelineTestHelper::SetStream(&event, &stream); // Allocate room for four arguments. event.SetNumArguments(4); // Reset. event.Reset(); event.DurationBegin("apple"); event.SetNumArguments(2); event.CopyArgument(0, "arg1", "value1"); event.CopyArgument(1, "arg2", "value2"); event.DurationEnd(); } TEST_CASE(TimelineEventArgumentsPrintJSON) { // Create a test stream. TimelineStream stream("testStream", "testStream", true); // Create a test event. TimelineEvent event; TimelineTestHelper::SetStream(&event, &stream); event.DurationBegin("apple"); event.SetNumArguments(2); event.CopyArgument(0, "arg1", "value1"); event.CopyArgument(1, "arg2", "value2"); event.DurationEnd(); { // Test printing to JSON. JSONStream js; event.PrintJSON(&js); // Check both arguments. EXPECT_SUBSTRING("\"arg1\":\"value1\"", js.ToCString()); EXPECT_SUBSTRING("\"arg2\":\"value2\"", js.ToCString()); } } TEST_CASE(TimelineEventBufferPrintJSON) { JSONStream js; TimelineEventFilter filter; Timeline::recorder()->PrintJSON(&js, &filter); // Check the type. EXPECT_SUBSTRING("\"type\":\"Timeline\"", js.ToCString()); // Check that there is a traceEvents array. EXPECT_SUBSTRING("\"traceEvents\":[", js.ToCString()); } // Count the number of each event type seen. class EventCounterRecorder : public TimelineEventCallbackRecorder { public: EventCounterRecorder() { for (intptr_t i = 0; i < TimelineEvent::kNumEventTypes; i++) { counts_[i] = 0; } } void OnEvent(TimelineEvent* event) { counts_[event->event_type()]++; } intptr_t CountFor(TimelineEvent::EventType type) { return counts_[type]; } intptr_t Size() { return -1; } private: intptr_t counts_[TimelineEvent::kNumEventTypes]; }; TEST_CASE(TimelineEventCallbackRecorderBasic) { TimelineRecorderOverride override; // Initial counts are all zero. for (intptr_t i = TimelineEvent::kNone + 1; i < TimelineEvent::kNumEventTypes; i++) { EXPECT_EQ(0, override.recorder()->CountFor( static_cast(i))); } // Create a test stream. TimelineStream stream("testStream", "testStream", true); TimelineEvent* event = NULL; event = stream.StartEvent(); EXPECT_EQ(0, override.recorder()->CountFor(TimelineEvent::kDuration)); event->DurationBegin("cabbage"); EXPECT_EQ(0, override.recorder()->CountFor(TimelineEvent::kDuration)); event->DurationEnd(); EXPECT_EQ(0, override.recorder()->CountFor(TimelineEvent::kDuration)); event->Complete(); EXPECT_EQ(1, override.recorder()->CountFor(TimelineEvent::kDuration)); event = stream.StartEvent(); EXPECT_EQ(0, override.recorder()->CountFor(TimelineEvent::kInstant)); event->Instant("instantCabbage"); EXPECT_EQ(0, override.recorder()->CountFor(TimelineEvent::kInstant)); event->Complete(); EXPECT_EQ(1, override.recorder()->CountFor(TimelineEvent::kInstant)); event = stream.StartEvent(); EXPECT_EQ(0, override.recorder()->CountFor(TimelineEvent::kAsyncBegin)); int64_t async_id = override.recorder()->GetNextAsyncId(); EXPECT(async_id >= 0); event->AsyncBegin("asyncBeginCabbage", async_id); EXPECT_EQ(0, override.recorder()->CountFor(TimelineEvent::kAsyncBegin)); event->Complete(); EXPECT_EQ(1, override.recorder()->CountFor(TimelineEvent::kAsyncBegin)); event = stream.StartEvent(); EXPECT_EQ(0, override.recorder()->CountFor(TimelineEvent::kAsyncInstant)); event->AsyncInstant("asyncInstantCabbage", async_id); EXPECT_EQ(0, override.recorder()->CountFor(TimelineEvent::kAsyncInstant)); event->Complete(); EXPECT_EQ(1, override.recorder()->CountFor(TimelineEvent::kAsyncInstant)); event = stream.StartEvent(); EXPECT_EQ(0, override.recorder()->CountFor(TimelineEvent::kAsyncEnd)); event->AsyncEnd("asyncEndCabbage", async_id); EXPECT_EQ(0, override.recorder()->CountFor(TimelineEvent::kAsyncEnd)); event->Complete(); EXPECT_EQ(1, override.recorder()->CountFor(TimelineEvent::kAsyncEnd)); } static bool LabelMatch(TimelineEvent* event, const char* label) { ASSERT(event != NULL); return strcmp(event->label(), label) == 0; } TEST_CASE(TimelineAnalysis_ThreadBlockCount) { TimelineRecorderOverride override; ASSERT(Timeline::recorder() != NULL); // Blocks owned by thread "1". TimelineEventBlock* block_1_0 = Timeline::recorder()->GetNewBlock(); TimelineTestHelper::SetBlockThread(block_1_0, 1); TimelineEventBlock* block_1_1 = Timeline::recorder()->GetNewBlock(); TimelineTestHelper::SetBlockThread(block_1_1, 1); TimelineEventBlock* block_1_2 = Timeline::recorder()->GetNewBlock(); TimelineTestHelper::SetBlockThread(block_1_2, 1); // Blocks owned by thread "2". TimelineEventBlock* block_2_0 = Timeline::recorder()->GetNewBlock(); TimelineTestHelper::SetBlockThread(block_2_0, 2); // Blocks owned by thread "3". TimelineEventBlock* block_3_0 = Timeline::recorder()->GetNewBlock(); TimelineTestHelper::SetBlockThread(block_3_0, 3); USE(block_3_0); // Add events to each block for thread 1. TimelineTestHelper::FakeThreadEvent(block_1_2, 1, "B1"); TimelineTestHelper::FakeThreadEvent(block_1_2, 1, "B2"); TimelineTestHelper::FakeThreadEvent(block_1_2, 1, "B3"); // Sleep to ensure timestamps differ. OS::Sleep(32); TimelineTestHelper::FakeThreadEvent(block_1_0, 1, "A1"); OS::Sleep(32); TimelineTestHelper::FakeThreadEvent(block_1_1, 1, "C1"); TimelineTestHelper::FakeThreadEvent(block_1_1, 1, "C2"); OS::Sleep(32); // Add events to each block for thread 2. TimelineTestHelper::FakeThreadEvent(block_2_0, 2, "A"); TimelineTestHelper::FakeThreadEvent(block_2_0, 2, "B"); TimelineTestHelper::FakeThreadEvent(block_2_0, 2, "C"); TimelineTestHelper::FakeThreadEvent(block_2_0, 2, "D"); TimelineTestHelper::FakeThreadEvent(block_2_0, 2, "E"); TimelineTestHelper::FakeThreadEvent(block_2_0, 2, "F"); Zone* zone = thread->zone(); Isolate* isolate = thread->isolate(); // Discover threads in Timeline::recorder(). TimelineAnalysis ta(zone, isolate, Timeline::recorder()); ta.BuildThreads(); EXPECT(!ta.has_error()); // block_3_0 is never used by a thread, so we only have two threads. EXPECT_EQ(2, ta.NumThreads()); // Extract both threads. TimelineAnalysisThread* thread_1 = ta.GetThread(OSThread::ThreadIdFromIntPtr(1)); TimelineAnalysisThread* thread_2 = ta.GetThread(OSThread::ThreadIdFromIntPtr(2)); EXPECT_EQ(OSThread::ThreadIdFromIntPtr(1), thread_1->id()); EXPECT_EQ(OSThread::ThreadIdFromIntPtr(2), thread_2->id()); // Thread "1" should have three blocks. EXPECT_EQ(3, thread_1->NumBlocks()); // Verify that blocks for thread "1" are sorted based on start time. EXPECT_EQ(thread_1->At(0), block_1_2); EXPECT_EQ(thread_1->At(1), block_1_0); EXPECT_EQ(thread_1->At(2), block_1_1); // Verify that block_1_2 has three events. EXPECT_EQ(3, block_1_2->length()); // Verify that block_1_0 has one events. EXPECT_EQ(1, block_1_0->length()); // Verify that block_1_1 has two events. EXPECT_EQ(2, block_1_1->length()); // Thread '2" should have one block.' EXPECT_EQ(1, thread_2->NumBlocks()); EXPECT_EQ(thread_2->At(0), block_2_0); // Verify that block_2_0 has six events. EXPECT_EQ(6, block_2_0->length()); { TimelineAnalysisThreadEventIterator it(thread_1); // Six events spread across three blocks. EXPECT(it.HasNext()); EXPECT(LabelMatch(it.Next(), "B1")); EXPECT(it.HasNext()); EXPECT(LabelMatch(it.Next(), "B2")); EXPECT(it.HasNext()); EXPECT(LabelMatch(it.Next(), "B3")); EXPECT(it.HasNext()); EXPECT(LabelMatch(it.Next(), "A1")); EXPECT(it.HasNext()); EXPECT(LabelMatch(it.Next(), "C1")); EXPECT(it.HasNext()); EXPECT(LabelMatch(it.Next(), "C2")); EXPECT(!it.HasNext()); } { TimelineAnalysisThreadEventIterator it(thread_2); // Six events spread across three blocks. EXPECT(it.HasNext()); EXPECT(LabelMatch(it.Next(), "A")); EXPECT(it.HasNext()); EXPECT(LabelMatch(it.Next(), "B")); EXPECT(it.HasNext()); EXPECT(LabelMatch(it.Next(), "C")); EXPECT(it.HasNext()); EXPECT(LabelMatch(it.Next(), "D")); EXPECT(it.HasNext()); EXPECT(LabelMatch(it.Next(), "E")); EXPECT(it.HasNext()); EXPECT(LabelMatch(it.Next(), "F")); EXPECT(!it.HasNext()); } } TEST_CASE(TimelineRingRecorderJSONOrder) { TimelineStream stream("testStream", "testStream", true); TimelineEventRingRecorder* recorder = new TimelineEventRingRecorder(TimelineEventBlock::kBlockSize * 2); TimelineRecorderOverride override(recorder); TimelineEventBlock* block_0 = Timeline::recorder()->GetNewBlock(); EXPECT(block_0 != NULL); TimelineEventBlock* block_1 = Timeline::recorder()->GetNewBlock(); EXPECT(block_1 != NULL); // Test that we wrapped. EXPECT(block_0 == Timeline::recorder()->GetNewBlock()); // Emit the earlier event into block_1. TimelineTestHelper::FakeThreadEvent(block_1, 2, "Alpha", &stream); OS::Sleep(32); // Emit the later event into block_0. TimelineTestHelper::FakeThreadEvent(block_0, 2, "Beta", &stream); TimelineTestHelper::FinishBlock(block_0); TimelineTestHelper::FinishBlock(block_1); JSONStream js; TimelineEventFilter filter; Timeline::recorder()->PrintJSON(&js, &filter); // trace-event has a requirement that events for a thread must have // monotonically increasing timestamps. // Verify that "Alpha" comes before "Beta" even though "Beta" is in the first // block. const char* alpha = strstr(js.ToCString(), "Alpha"); const char* beta = strstr(js.ToCString(), "Beta"); EXPECT(alpha < beta); } TEST_CASE(TimelinePauses_Basic) { Zone* zone = thread->zone(); Isolate* isolate = thread->isolate(); OSThread* os_thread = thread->os_thread(); ASSERT(os_thread != NULL); ThreadId tid = os_thread->trace_id(); { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeDuration(Timeline::recorder(), "a", 0, 10); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(!pauses.has_error()); EXPECT_EQ(10, pauses.InclusiveTime("a")); EXPECT_EQ(10, pauses.ExclusiveTime("a")); EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); EXPECT_EQ(10, pauses.MaxExclusiveTime("a")); } } { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeDuration(Timeline::recorder(), "a", 0, 10); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 0, 10); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(!pauses.has_error()); EXPECT_EQ(10, pauses.InclusiveTime("a")); EXPECT_EQ(0, pauses.ExclusiveTime("a")); EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); EXPECT_EQ(0, pauses.MaxExclusiveTime("a")); EXPECT_EQ(10, pauses.InclusiveTime("b")); EXPECT_EQ(10, pauses.ExclusiveTime("b")); EXPECT_EQ(10, pauses.MaxInclusiveTime("b")); EXPECT_EQ(10, pauses.MaxExclusiveTime("b")); } } { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeDuration(Timeline::recorder(), "a", 0, 10); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 1, 8); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(!pauses.has_error()); EXPECT_EQ(10, pauses.InclusiveTime("a")); EXPECT_EQ(3, pauses.ExclusiveTime("a")); EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); EXPECT_EQ(3, pauses.MaxExclusiveTime("a")); EXPECT_EQ(7, pauses.InclusiveTime("b")); EXPECT_EQ(7, pauses.ExclusiveTime("b")); EXPECT_EQ(7, pauses.MaxInclusiveTime("b")); EXPECT_EQ(7, pauses.MaxExclusiveTime("b")); } } { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeDuration(Timeline::recorder(), "a", 0, 10); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 0, 1); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 1, 2); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 2, 3); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 3, 4); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 4, 5); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 5, 6); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 6, 7); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 7, 8); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 8, 9); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 9, 10); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(!pauses.has_error()); EXPECT_EQ(10, pauses.InclusiveTime("a")); EXPECT_EQ(0, pauses.ExclusiveTime("a")); EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); EXPECT_EQ(0, pauses.MaxExclusiveTime("a")); EXPECT_EQ(10, pauses.InclusiveTime("b")); EXPECT_EQ(10, pauses.ExclusiveTime("b")); EXPECT_EQ(1, pauses.MaxInclusiveTime("b")); EXPECT_EQ(1, pauses.MaxExclusiveTime("b")); } } { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeDuration(Timeline::recorder(), "a", 0, 10); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 0, 5); TimelineTestHelper::FakeDuration(Timeline::recorder(), "c", 1, 4); TimelineTestHelper::FakeDuration(Timeline::recorder(), "d", 5, 10); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(!pauses.has_error()); EXPECT_EQ(10, pauses.InclusiveTime("a")); EXPECT_EQ(0, pauses.ExclusiveTime("a")); EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); EXPECT_EQ(0, pauses.MaxExclusiveTime("a")); EXPECT_EQ(5, pauses.InclusiveTime("b")); EXPECT_EQ(2, pauses.ExclusiveTime("b")); EXPECT_EQ(5, pauses.MaxInclusiveTime("b")); EXPECT_EQ(2, pauses.MaxExclusiveTime("b")); EXPECT_EQ(3, pauses.InclusiveTime("c")); EXPECT_EQ(3, pauses.ExclusiveTime("c")); EXPECT_EQ(3, pauses.MaxInclusiveTime("c")); EXPECT_EQ(3, pauses.MaxExclusiveTime("c")); EXPECT_EQ(5, pauses.InclusiveTime("d")); EXPECT_EQ(5, pauses.ExclusiveTime("d")); EXPECT_EQ(5, pauses.MaxInclusiveTime("d")); EXPECT_EQ(5, pauses.MaxExclusiveTime("d")); } } { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeDuration(Timeline::recorder(), "a", 0, 10); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 1, 9); TimelineTestHelper::FakeDuration(Timeline::recorder(), "c", 2, 8); TimelineTestHelper::FakeDuration(Timeline::recorder(), "d", 3, 7); TimelineTestHelper::FakeDuration(Timeline::recorder(), "e", 4, 6); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(!pauses.has_error()); EXPECT_EQ(10, pauses.InclusiveTime("a")); EXPECT_EQ(2, pauses.ExclusiveTime("a")); EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); EXPECT_EQ(2, pauses.MaxExclusiveTime("a")); EXPECT_EQ(8, pauses.InclusiveTime("b")); EXPECT_EQ(2, pauses.ExclusiveTime("b")); EXPECT_EQ(8, pauses.MaxInclusiveTime("b")); EXPECT_EQ(2, pauses.MaxExclusiveTime("b")); EXPECT_EQ(6, pauses.InclusiveTime("c")); EXPECT_EQ(2, pauses.ExclusiveTime("c")); EXPECT_EQ(6, pauses.MaxInclusiveTime("c")); EXPECT_EQ(2, pauses.MaxExclusiveTime("c")); EXPECT_EQ(4, pauses.InclusiveTime("d")); EXPECT_EQ(2, pauses.ExclusiveTime("d")); EXPECT_EQ(4, pauses.MaxInclusiveTime("d")); EXPECT_EQ(2, pauses.MaxExclusiveTime("d")); EXPECT_EQ(2, pauses.InclusiveTime("e")); EXPECT_EQ(2, pauses.ExclusiveTime("e")); EXPECT_EQ(2, pauses.MaxInclusiveTime("e")); EXPECT_EQ(2, pauses.MaxExclusiveTime("e")); } } { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeDuration(Timeline::recorder(), "a", 0, 10); TimelineTestHelper::FakeDuration(Timeline::recorder(), "a", 1, 9); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(!pauses.has_error()); EXPECT_EQ(10, pauses.InclusiveTime("a")); EXPECT_EQ(10, pauses.ExclusiveTime("a")); EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); EXPECT_EQ(8, pauses.MaxExclusiveTime("a")); } } } TEST_CASE(TimelinePauses_BeginEnd) { Zone* zone = thread->zone(); Isolate* isolate = thread->isolate(); OSThread* os_thread = thread->os_thread(); ASSERT(os_thread != NULL); ThreadId tid = os_thread->trace_id(); { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeBegin(Timeline::recorder(), "a", 0); TimelineTestHelper::FakeEnd(Timeline::recorder(), "a", 10); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(!pauses.has_error()); EXPECT_EQ(10, pauses.InclusiveTime("a")); EXPECT_EQ(10, pauses.ExclusiveTime("a")); EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); EXPECT_EQ(10, pauses.MaxExclusiveTime("a")); } } { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeBegin(Timeline::recorder(), "a", 0); TimelineTestHelper::FakeBegin(Timeline::recorder(), "b", 0); TimelineTestHelper::FakeEnd(Timeline::recorder(), "b", 10); TimelineTestHelper::FakeEnd(Timeline::recorder(), "a", 10); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(!pauses.has_error()); EXPECT_EQ(10, pauses.InclusiveTime("a")); EXPECT_EQ(0, pauses.ExclusiveTime("a")); EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); EXPECT_EQ(0, pauses.MaxExclusiveTime("a")); EXPECT_EQ(10, pauses.InclusiveTime("b")); EXPECT_EQ(10, pauses.ExclusiveTime("b")); EXPECT_EQ(10, pauses.MaxInclusiveTime("b")); EXPECT_EQ(10, pauses.MaxExclusiveTime("b")); } } { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeBegin(Timeline::recorder(), "a", 0); TimelineTestHelper::FakeBegin(Timeline::recorder(), "b", 1); TimelineTestHelper::FakeEnd(Timeline::recorder(), "b", 8); TimelineTestHelper::FakeEnd(Timeline::recorder(), "a", 10); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(!pauses.has_error()); EXPECT_EQ(10, pauses.InclusiveTime("a")); EXPECT_EQ(3, pauses.ExclusiveTime("a")); EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); EXPECT_EQ(3, pauses.MaxExclusiveTime("a")); EXPECT_EQ(7, pauses.InclusiveTime("b")); EXPECT_EQ(7, pauses.ExclusiveTime("b")); EXPECT_EQ(7, pauses.MaxInclusiveTime("b")); EXPECT_EQ(7, pauses.MaxExclusiveTime("b")); } } { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeBegin(Timeline::recorder(), "a", 0); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 0, 1); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 1, 2); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 2, 3); TimelineTestHelper::FakeBegin(Timeline::recorder(), "b", 3); TimelineTestHelper::FakeEnd(Timeline::recorder(), "b", 4); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 4, 5); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 5, 6); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 6, 7); TimelineTestHelper::FakeBegin(Timeline::recorder(), "b", 7); TimelineTestHelper::FakeEnd(Timeline::recorder(), "b", 8); TimelineTestHelper::FakeBegin(Timeline::recorder(), "b", 8); TimelineTestHelper::FakeEnd(Timeline::recorder(), "b", 9); TimelineTestHelper::FakeDuration(Timeline::recorder(), "b", 9, 10); TimelineTestHelper::FakeEnd(Timeline::recorder(), "a", 10); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(!pauses.has_error()); EXPECT_EQ(10, pauses.InclusiveTime("a")); EXPECT_EQ(0, pauses.ExclusiveTime("a")); EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); EXPECT_EQ(0, pauses.MaxExclusiveTime("a")); EXPECT_EQ(10, pauses.InclusiveTime("b")); EXPECT_EQ(10, pauses.ExclusiveTime("b")); EXPECT_EQ(1, pauses.MaxInclusiveTime("b")); EXPECT_EQ(1, pauses.MaxExclusiveTime("b")); } } { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeBegin(Timeline::recorder(), "a", 0); TimelineTestHelper::FakeBegin(Timeline::recorder(), "b", 0); TimelineTestHelper::FakeBegin(Timeline::recorder(), "c", 1); TimelineTestHelper::FakeEnd(Timeline::recorder(), "c", 4); TimelineTestHelper::FakeEnd(Timeline::recorder(), "b", 5); TimelineTestHelper::FakeBegin(Timeline::recorder(), "d", 5); TimelineTestHelper::FakeEnd(Timeline::recorder(), "d", 10); TimelineTestHelper::FakeEnd(Timeline::recorder(), "a", 10); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(!pauses.has_error()); EXPECT_EQ(10, pauses.InclusiveTime("a")); EXPECT_EQ(0, pauses.ExclusiveTime("a")); EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); EXPECT_EQ(0, pauses.MaxExclusiveTime("a")); EXPECT_EQ(5, pauses.InclusiveTime("b")); EXPECT_EQ(2, pauses.ExclusiveTime("b")); EXPECT_EQ(5, pauses.MaxInclusiveTime("b")); EXPECT_EQ(2, pauses.MaxExclusiveTime("b")); EXPECT_EQ(3, pauses.InclusiveTime("c")); EXPECT_EQ(3, pauses.ExclusiveTime("c")); EXPECT_EQ(3, pauses.MaxInclusiveTime("c")); EXPECT_EQ(3, pauses.MaxExclusiveTime("c")); EXPECT_EQ(5, pauses.InclusiveTime("d")); EXPECT_EQ(5, pauses.ExclusiveTime("d")); EXPECT_EQ(5, pauses.MaxInclusiveTime("d")); EXPECT_EQ(5, pauses.MaxExclusiveTime("d")); } } { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeBegin(Timeline::recorder(), "a", 0); TimelineTestHelper::FakeBegin(Timeline::recorder(), "b", 1); TimelineTestHelper::FakeBegin(Timeline::recorder(), "c", 2); TimelineTestHelper::FakeBegin(Timeline::recorder(), "d", 3); TimelineTestHelper::FakeBegin(Timeline::recorder(), "e", 4); TimelineTestHelper::FakeEnd(Timeline::recorder(), "e", 6); TimelineTestHelper::FakeEnd(Timeline::recorder(), "d", 7); TimelineTestHelper::FakeEnd(Timeline::recorder(), "c", 8); TimelineTestHelper::FakeEnd(Timeline::recorder(), "b", 9); TimelineTestHelper::FakeEnd(Timeline::recorder(), "a", 10); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(!pauses.has_error()); EXPECT_EQ(10, pauses.InclusiveTime("a")); EXPECT_EQ(2, pauses.ExclusiveTime("a")); EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); EXPECT_EQ(2, pauses.MaxExclusiveTime("a")); EXPECT_EQ(8, pauses.InclusiveTime("b")); EXPECT_EQ(2, pauses.ExclusiveTime("b")); EXPECT_EQ(8, pauses.MaxInclusiveTime("b")); EXPECT_EQ(2, pauses.MaxExclusiveTime("b")); EXPECT_EQ(6, pauses.InclusiveTime("c")); EXPECT_EQ(2, pauses.ExclusiveTime("c")); EXPECT_EQ(6, pauses.MaxInclusiveTime("c")); EXPECT_EQ(2, pauses.MaxExclusiveTime("c")); EXPECT_EQ(4, pauses.InclusiveTime("d")); EXPECT_EQ(2, pauses.ExclusiveTime("d")); EXPECT_EQ(4, pauses.MaxInclusiveTime("d")); EXPECT_EQ(2, pauses.MaxExclusiveTime("d")); EXPECT_EQ(2, pauses.InclusiveTime("e")); EXPECT_EQ(2, pauses.ExclusiveTime("e")); EXPECT_EQ(2, pauses.MaxInclusiveTime("e")); EXPECT_EQ(2, pauses.MaxExclusiveTime("e")); } } { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeBegin(Timeline::recorder(), "a", 0); TimelineTestHelper::FakeBegin(Timeline::recorder(), "a", 1); TimelineTestHelper::FakeEnd(Timeline::recorder(), "a", 9); TimelineTestHelper::FakeEnd(Timeline::recorder(), "a", 10); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(!pauses.has_error()); EXPECT_EQ(10, pauses.InclusiveTime("a")); EXPECT_EQ(10, pauses.ExclusiveTime("a")); EXPECT_EQ(10, pauses.MaxInclusiveTime("a")); EXPECT_EQ(8, pauses.MaxExclusiveTime("a")); } } { TimelineRecorderOverride override; // Test case. TimelineTestHelper::FakeBegin(Timeline::recorder(), "a", 0); TimelineTestHelper::FakeBegin(Timeline::recorder(), "b", 1); // Pop "a" without popping "b" first. TimelineTestHelper::FakeEnd(Timeline::recorder(), "a", 10); { TimelinePauses pauses(zone, isolate, Timeline::recorder()); pauses.Setup(); pauses.CalculatePauseTimesForThread(tid); EXPECT(pauses.has_error()); } } } #endif // !PRODUCT } // namespace dart