| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <vector> |
| |
| #include "src/trace_processor/importers/common/flow_tracker.h" |
| #include "src/trace_processor/importers/common/slice_tracker.h" |
| #include "src/trace_processor/importers/common/slice_translation_table.h" |
| #include "src/trace_processor/storage/trace_storage.h" |
| #include "src/trace_processor/types/trace_processor_context.h" |
| #include "test/gtest_and_gmock.h" |
| |
| namespace perfetto { |
| namespace trace_processor { |
| namespace { |
| |
| using ::testing::Eq; |
| |
| TEST(FlowTrackerTest, SingleFlowEventExplicitInSliceBinding) { |
| TraceProcessorContext context; |
| context.storage.reset(new TraceStorage()); |
| context.slice_tracker.reset(new SliceTracker(&context)); |
| context.slice_translation_table.reset( |
| new SliceTranslationTable(context.storage.get())); |
| auto& slice_tracker = context.slice_tracker; |
| FlowTracker tracker(&context); |
| slice_tracker->SetOnSliceBeginCallback( |
| [&tracker](TrackId track_id, SliceId slice_id) { |
| tracker.ClosePendingEventsOnTrack(track_id, slice_id); |
| }); |
| |
| FlowId flow_id = 1; |
| TrackId track_1(1); |
| TrackId track_2(2); |
| |
| slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1)); |
| SliceId out_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_1).value(); |
| tracker.Begin(track_1, flow_id); |
| slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1)); |
| |
| slice_tracker->Begin(140, track_2, StringId::Raw(2), StringId::Raw(2)); |
| SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value(); |
| tracker.End(track_2, flow_id, /* bind_enclosing = */ true, |
| /* close_flow = */ false); |
| slice_tracker->End(160, track_2, StringId::Raw(2), StringId::Raw(2)); |
| |
| const auto& flows = context.storage->flow_table(); |
| EXPECT_EQ(flows.row_count(), 1u); |
| EXPECT_EQ(flows.slice_out()[0], out_slice_id); |
| EXPECT_EQ(flows.slice_in()[0], in_slice_id); |
| } |
| |
| TEST(FlowTrackerTest, SingleFlowEventWaitForNextSlice) { |
| TraceProcessorContext context; |
| context.storage.reset(new TraceStorage()); |
| context.slice_tracker.reset(new SliceTracker(&context)); |
| context.slice_translation_table.reset( |
| new SliceTranslationTable(context.storage.get())); |
| auto& slice_tracker = context.slice_tracker; |
| FlowTracker tracker(&context); |
| slice_tracker->SetOnSliceBeginCallback( |
| [&tracker](TrackId track_id, SliceId slice_id) { |
| tracker.ClosePendingEventsOnTrack(track_id, slice_id); |
| }); |
| |
| FlowId flow_id = 1; |
| TrackId track_1(1); |
| TrackId track_2(2); |
| |
| slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1)); |
| SliceId out_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_1).value(); |
| tracker.Begin(track_1, flow_id); |
| slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1)); |
| |
| tracker.End(track_2, flow_id, /* bind_enclosing = */ false, |
| /* close_flow = */ false); |
| |
| const auto& flows = context.storage->flow_table(); |
| |
| EXPECT_EQ(flows.row_count(), 0u); |
| |
| slice_tracker->Begin(140, track_2, StringId::Raw(2), StringId::Raw(2)); |
| SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value(); |
| slice_tracker->End(160, track_2, StringId::Raw(2), StringId::Raw(2)); |
| |
| EXPECT_EQ(flows.row_count(), 1u); |
| EXPECT_EQ(flows.slice_out()[0], out_slice_id); |
| EXPECT_EQ(flows.slice_in()[0], in_slice_id); |
| } |
| |
| TEST(FlowTrackerTest, SingleFlowEventWaitForNextSliceScoped) { |
| TraceProcessorContext context; |
| context.storage.reset(new TraceStorage()); |
| context.slice_tracker.reset(new SliceTracker(&context)); |
| context.slice_translation_table.reset( |
| new SliceTranslationTable(context.storage.get())); |
| auto& slice_tracker = context.slice_tracker; |
| FlowTracker tracker(&context); |
| slice_tracker->SetOnSliceBeginCallback( |
| [&tracker](TrackId track_id, SliceId slice_id) { |
| tracker.ClosePendingEventsOnTrack(track_id, slice_id); |
| }); |
| |
| FlowId flow_id = 1; |
| TrackId track_1(1); |
| TrackId track_2(2); |
| |
| slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1)); |
| SliceId out_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_1).value(); |
| tracker.Begin(track_1, flow_id); |
| slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1)); |
| |
| tracker.End(track_2, flow_id, /* bind_enclosing = */ false, |
| /* close_flow = */ false); |
| |
| const auto& flows = context.storage->flow_table(); |
| |
| EXPECT_EQ(flows.row_count(), 0u); |
| |
| slice_tracker->Scoped(140, track_2, StringId::Raw(2), StringId::Raw(2), 100); |
| SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value(); |
| |
| EXPECT_EQ(flows.row_count(), 1u); |
| EXPECT_EQ(flows.slice_out()[0], out_slice_id); |
| EXPECT_EQ(flows.slice_in()[0], in_slice_id); |
| } |
| |
| TEST(FlowTrackerTest, TwoFlowEventsWaitForNextSlice) { |
| TraceProcessorContext context; |
| context.storage.reset(new TraceStorage()); |
| context.slice_tracker.reset(new SliceTracker(&context)); |
| context.slice_translation_table.reset( |
| new SliceTranslationTable(context.storage.get())); |
| auto& slice_tracker = context.slice_tracker; |
| FlowTracker tracker(&context); |
| slice_tracker->SetOnSliceBeginCallback( |
| [&tracker](TrackId track_id, SliceId slice_id) { |
| tracker.ClosePendingEventsOnTrack(track_id, slice_id); |
| }); |
| |
| FlowId flow1_id = 1; |
| FlowId flow2_id = 2; |
| TrackId track_1(1); |
| TrackId track_2(2); |
| |
| // begin flow1 in enclosing slice1 |
| slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1)); |
| SliceId out_slice1_id = |
| slice_tracker->GetTopmostSliceOnTrack(track_1).value(); |
| tracker.Begin(track_1, flow1_id); |
| tracker.End(track_2, flow1_id, /* bind_enclosing = */ false, |
| /* close_flow = */ false); |
| slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1)); |
| |
| // begin flow2 in enclosing slice2 |
| slice_tracker->Begin(130, track_1, StringId::Raw(2), StringId::Raw(2)); |
| SliceId out_slice2_id = |
| slice_tracker->GetTopmostSliceOnTrack(track_1).value(); |
| tracker.Begin(track_1, flow2_id); |
| tracker.End(track_2, flow2_id, /* bind_enclosing = */ false, |
| /* close_flow = */ false); |
| slice_tracker->End(140, track_1, StringId::Raw(2), StringId::Raw(2)); |
| |
| const auto& flows = context.storage->flow_table(); |
| |
| EXPECT_EQ(flows.row_count(), 0u); |
| |
| // close all pending flows |
| slice_tracker->Begin(160, track_2, StringId::Raw(3), StringId::Raw(3)); |
| SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value(); |
| slice_tracker->End(170, track_2, StringId::Raw(3), StringId::Raw(3)); |
| |
| EXPECT_EQ(flows.row_count(), 2u); |
| EXPECT_EQ(flows.slice_out()[0], out_slice1_id); |
| EXPECT_EQ(flows.slice_in()[0], in_slice_id); |
| EXPECT_EQ(flows.slice_out()[1], out_slice2_id); |
| EXPECT_EQ(flows.slice_in()[1], in_slice_id); |
| } |
| |
| TEST(FlowTrackerTest, TwoFlowEventsSliceInSlice) { |
| TraceProcessorContext context; |
| context.storage.reset(new TraceStorage()); |
| context.slice_tracker.reset(new SliceTracker(&context)); |
| context.slice_translation_table.reset( |
| new SliceTranslationTable(context.storage.get())); |
| auto& slice_tracker = context.slice_tracker; |
| FlowTracker tracker(&context); |
| slice_tracker->SetOnSliceBeginCallback( |
| [&tracker](TrackId track_id, SliceId slice_id) { |
| tracker.ClosePendingEventsOnTrack(track_id, slice_id); |
| }); |
| |
| FlowId flow1_id = 1; |
| FlowId flow2_id = 2; |
| TrackId track_1(1); |
| TrackId track_2(2); |
| |
| // start two nested slices |
| slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1)); |
| SliceId out_slice1_id = |
| slice_tracker->GetTopmostSliceOnTrack(track_1).value(); |
| slice_tracker->Begin(120, track_1, StringId::Raw(2), StringId::Raw(2)); |
| SliceId out_slice2_id = |
| slice_tracker->GetTopmostSliceOnTrack(track_1).value(); |
| |
| tracker.Begin(track_1, flow1_id); |
| |
| slice_tracker->End(140, track_1, StringId::Raw(2), StringId::Raw(2)); |
| |
| tracker.Begin(track_1, flow2_id); |
| |
| slice_tracker->End(150, track_1, StringId::Raw(1), StringId::Raw(1)); |
| |
| slice_tracker->Begin(160, track_2, StringId::Raw(3), StringId::Raw(3)); |
| SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value(); |
| |
| tracker.End(track_2, flow1_id, /* bind_enclosing = */ true, |
| /* close_flow = */ false); |
| tracker.End(track_2, flow2_id, /* bind_enclosing = */ true, |
| /* close_flow = */ false); |
| |
| slice_tracker->End(170, track_2, StringId::Raw(3), StringId::Raw(3)); |
| |
| const auto& flows = context.storage->flow_table(); |
| EXPECT_EQ(flows.row_count(), 2u); |
| EXPECT_EQ(flows.slice_out()[0], out_slice2_id); |
| EXPECT_EQ(flows.slice_in()[0], in_slice_id); |
| EXPECT_EQ(flows.slice_out()[1], out_slice1_id); |
| EXPECT_EQ(flows.slice_in()[1], in_slice_id); |
| } |
| |
| TEST(FlowTrackerTest, FlowEventsWithStep) { |
| TraceProcessorContext context; |
| context.storage.reset(new TraceStorage()); |
| context.slice_tracker.reset(new SliceTracker(&context)); |
| context.slice_translation_table.reset( |
| new SliceTranslationTable(context.storage.get())); |
| auto& slice_tracker = context.slice_tracker; |
| FlowTracker tracker(&context); |
| slice_tracker->SetOnSliceBeginCallback( |
| [&tracker](TrackId track_id, SliceId slice_id) { |
| tracker.ClosePendingEventsOnTrack(track_id, slice_id); |
| }); |
| |
| FlowId flow_id = 1; |
| TrackId track_1(1); |
| TrackId track_2(2); |
| |
| // flow begin inside slice1 on track1 |
| slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1)); |
| SliceId out_slice1_id = |
| slice_tracker->GetTopmostSliceOnTrack(track_1).value(); |
| tracker.Begin(track_1, flow_id); |
| slice_tracker->End(140, track_1, StringId::Raw(1), StringId::Raw(1)); |
| |
| // flow step inside slice2 on track2 |
| slice_tracker->Begin(160, track_2, StringId::Raw(2), StringId::Raw(2)); |
| SliceId inout_slice2_id = |
| slice_tracker->GetTopmostSliceOnTrack(track_2).value(); |
| tracker.Step(track_2, flow_id); |
| slice_tracker->End(170, track_2, StringId::Raw(2), StringId::Raw(2)); |
| |
| // flow end inside slice3 on track3 |
| slice_tracker->Begin(180, track_1, StringId::Raw(3), StringId::Raw(3)); |
| SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_1).value(); |
| tracker.End(track_1, flow_id, /* bind_enclosing = */ true, |
| /* close_flow = */ false); |
| slice_tracker->End(190, track_1, StringId::Raw(3), StringId::Raw(3)); |
| |
| const auto& flows = context.storage->flow_table(); |
| EXPECT_EQ(flows.row_count(), 2u); |
| EXPECT_EQ(flows.slice_out()[0], out_slice1_id); |
| EXPECT_EQ(flows.slice_in()[0], inout_slice2_id); |
| EXPECT_EQ(flows.slice_out()[1], inout_slice2_id); |
| EXPECT_EQ(flows.slice_in()[1], in_slice_id); |
| } |
| |
| } // namespace |
| } // namespace trace_processor |
| } // namespace perfetto |