blob: 3ac912f8027fd148e0b99ce8276ee899b56132a4 [file] [log] [blame]
/*
* 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