1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <vector>
18
19 #include "src/trace_processor/importers/common/args_translation_table.h"
20 #include "src/trace_processor/importers/common/flow_tracker.h"
21 #include "src/trace_processor/importers/common/slice_tracker.h"
22 #include "src/trace_processor/importers/common/slice_translation_table.h"
23 #include "src/trace_processor/storage/trace_storage.h"
24 #include "src/trace_processor/types/trace_processor_context.h"
25 #include "test/gtest_and_gmock.h"
26
27 namespace perfetto {
28 namespace trace_processor {
29 namespace {
30
31 using ::testing::Eq;
32
33 class FlowTrackerTest : public ::testing::Test {
34 public:
FlowTrackerTest()35 FlowTrackerTest() {
36 context_.storage = std::make_unique<TraceStorage>();
37 context_.args_translation_table =
38 std::make_unique<ArgsTranslationTable>(context_.storage.get());
39 context_.slice_translation_table =
40 std::make_unique<SliceTranslationTable>(context_.storage.get());
41 context_.slice_tracker = std::make_unique<SliceTracker>(&context_);
42 }
43
44 protected:
45 TraceProcessorContext context_;
46 };
47
TEST_F(FlowTrackerTest,SingleFlowEventExplicitInSliceBinding)48 TEST_F(FlowTrackerTest, SingleFlowEventExplicitInSliceBinding) {
49 auto& slice_tracker = context_.slice_tracker;
50 FlowTracker tracker(&context_);
51 slice_tracker->SetOnSliceBeginCallback(
52 [&tracker](TrackId track_id, SliceId slice_id) {
53 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
54 });
55
56 FlowId flow_id = 1;
57 TrackId track_1(1);
58 TrackId track_2(2);
59
60 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
61 SliceId out_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_1).value();
62 tracker.Begin(track_1, flow_id);
63 slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1));
64
65 slice_tracker->Begin(140, track_2, StringId::Raw(2), StringId::Raw(2));
66 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value();
67 tracker.End(track_2, flow_id, /* bind_enclosing = */ true,
68 /* close_flow = */ false);
69 slice_tracker->End(160, track_2, StringId::Raw(2), StringId::Raw(2));
70
71 const auto& flows = context_.storage->flow_table();
72 EXPECT_EQ(flows.row_count(), 1u);
73 EXPECT_EQ(flows.slice_out()[0], out_slice_id);
74 EXPECT_EQ(flows.slice_in()[0], in_slice_id);
75 }
76
TEST_F(FlowTrackerTest,SingleFlowEventWaitForNextSlice)77 TEST_F(FlowTrackerTest, SingleFlowEventWaitForNextSlice) {
78 auto& slice_tracker = context_.slice_tracker;
79 FlowTracker tracker(&context_);
80 slice_tracker->SetOnSliceBeginCallback(
81 [&tracker](TrackId track_id, SliceId slice_id) {
82 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
83 });
84
85 FlowId flow_id = 1;
86 TrackId track_1(1);
87 TrackId track_2(2);
88
89 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
90 SliceId out_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_1).value();
91 tracker.Begin(track_1, flow_id);
92 slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1));
93
94 tracker.End(track_2, flow_id, /* bind_enclosing = */ false,
95 /* close_flow = */ false);
96
97 const auto& flows = context_.storage->flow_table();
98
99 EXPECT_EQ(flows.row_count(), 0u);
100
101 slice_tracker->Begin(140, track_2, StringId::Raw(2), StringId::Raw(2));
102 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value();
103 slice_tracker->End(160, track_2, StringId::Raw(2), StringId::Raw(2));
104
105 EXPECT_EQ(flows.row_count(), 1u);
106 EXPECT_EQ(flows.slice_out()[0], out_slice_id);
107 EXPECT_EQ(flows.slice_in()[0], in_slice_id);
108 }
109
TEST_F(FlowTrackerTest,SingleFlowEventWaitForNextSliceScoped)110 TEST_F(FlowTrackerTest, SingleFlowEventWaitForNextSliceScoped) {
111 auto& slice_tracker = context_.slice_tracker;
112 FlowTracker tracker(&context_);
113 slice_tracker->SetOnSliceBeginCallback(
114 [&tracker](TrackId track_id, SliceId slice_id) {
115 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
116 });
117
118 FlowId flow_id = 1;
119 TrackId track_1(1);
120 TrackId track_2(2);
121
122 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
123 SliceId out_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_1).value();
124 tracker.Begin(track_1, flow_id);
125 slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1));
126
127 tracker.End(track_2, flow_id, /* bind_enclosing = */ false,
128 /* close_flow = */ false);
129
130 const auto& flows = context_.storage->flow_table();
131
132 EXPECT_EQ(flows.row_count(), 0u);
133
134 slice_tracker->Scoped(140, track_2, StringId::Raw(2), StringId::Raw(2), 100);
135 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value();
136
137 EXPECT_EQ(flows.row_count(), 1u);
138 EXPECT_EQ(flows.slice_out()[0], out_slice_id);
139 EXPECT_EQ(flows.slice_in()[0], in_slice_id);
140 }
141
TEST_F(FlowTrackerTest,TwoFlowEventsWaitForNextSlice)142 TEST_F(FlowTrackerTest, TwoFlowEventsWaitForNextSlice) {
143 auto& slice_tracker = context_.slice_tracker;
144 FlowTracker tracker(&context_);
145 slice_tracker->SetOnSliceBeginCallback(
146 [&tracker](TrackId track_id, SliceId slice_id) {
147 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
148 });
149
150 FlowId flow1_id = 1;
151 FlowId flow2_id = 2;
152 TrackId track_1(1);
153 TrackId track_2(2);
154
155 // begin flow1 in enclosing slice1
156 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
157 SliceId out_slice1_id =
158 slice_tracker->GetTopmostSliceOnTrack(track_1).value();
159 tracker.Begin(track_1, flow1_id);
160 tracker.End(track_2, flow1_id, /* bind_enclosing = */ false,
161 /* close_flow = */ false);
162 slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1));
163
164 // begin flow2 in enclosing slice2
165 slice_tracker->Begin(130, track_1, StringId::Raw(2), StringId::Raw(2));
166 SliceId out_slice2_id =
167 slice_tracker->GetTopmostSliceOnTrack(track_1).value();
168 tracker.Begin(track_1, flow2_id);
169 tracker.End(track_2, flow2_id, /* bind_enclosing = */ false,
170 /* close_flow = */ false);
171 slice_tracker->End(140, track_1, StringId::Raw(2), StringId::Raw(2));
172
173 const auto& flows = context_.storage->flow_table();
174
175 EXPECT_EQ(flows.row_count(), 0u);
176
177 // close all pending flows
178 slice_tracker->Begin(160, track_2, StringId::Raw(3), StringId::Raw(3));
179 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value();
180 slice_tracker->End(170, track_2, StringId::Raw(3), StringId::Raw(3));
181
182 EXPECT_EQ(flows.row_count(), 2u);
183 EXPECT_EQ(flows.slice_out()[0], out_slice1_id);
184 EXPECT_EQ(flows.slice_in()[0], in_slice_id);
185 EXPECT_EQ(flows.slice_out()[1], out_slice2_id);
186 EXPECT_EQ(flows.slice_in()[1], in_slice_id);
187 }
188
TEST_F(FlowTrackerTest,TwoFlowEventsSliceInSlice)189 TEST_F(FlowTrackerTest, TwoFlowEventsSliceInSlice) {
190 auto& slice_tracker = context_.slice_tracker;
191 FlowTracker tracker(&context_);
192 slice_tracker->SetOnSliceBeginCallback(
193 [&tracker](TrackId track_id, SliceId slice_id) {
194 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
195 });
196
197 FlowId flow1_id = 1;
198 FlowId flow2_id = 2;
199 TrackId track_1(1);
200 TrackId track_2(2);
201
202 // start two nested slices
203 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
204 SliceId out_slice1_id =
205 slice_tracker->GetTopmostSliceOnTrack(track_1).value();
206 slice_tracker->Begin(120, track_1, StringId::Raw(2), StringId::Raw(2));
207 SliceId out_slice2_id =
208 slice_tracker->GetTopmostSliceOnTrack(track_1).value();
209
210 tracker.Begin(track_1, flow1_id);
211
212 slice_tracker->End(140, track_1, StringId::Raw(2), StringId::Raw(2));
213
214 tracker.Begin(track_1, flow2_id);
215
216 slice_tracker->End(150, track_1, StringId::Raw(1), StringId::Raw(1));
217
218 slice_tracker->Begin(160, track_2, StringId::Raw(3), StringId::Raw(3));
219 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value();
220
221 tracker.End(track_2, flow1_id, /* bind_enclosing = */ true,
222 /* close_flow = */ false);
223 tracker.End(track_2, flow2_id, /* bind_enclosing = */ true,
224 /* close_flow = */ false);
225
226 slice_tracker->End(170, track_2, StringId::Raw(3), StringId::Raw(3));
227
228 const auto& flows = context_.storage->flow_table();
229 EXPECT_EQ(flows.row_count(), 2u);
230 EXPECT_EQ(flows.slice_out()[0], out_slice2_id);
231 EXPECT_EQ(flows.slice_in()[0], in_slice_id);
232 EXPECT_EQ(flows.slice_out()[1], out_slice1_id);
233 EXPECT_EQ(flows.slice_in()[1], in_slice_id);
234 }
235
TEST_F(FlowTrackerTest,FlowEventsWithStep)236 TEST_F(FlowTrackerTest, FlowEventsWithStep) {
237 auto& slice_tracker = context_.slice_tracker;
238 FlowTracker tracker(&context_);
239 slice_tracker->SetOnSliceBeginCallback(
240 [&tracker](TrackId track_id, SliceId slice_id) {
241 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
242 });
243
244 FlowId flow_id = 1;
245 TrackId track_1(1);
246 TrackId track_2(2);
247
248 // flow begin inside slice1 on track1
249 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
250 SliceId out_slice1_id =
251 slice_tracker->GetTopmostSliceOnTrack(track_1).value();
252 tracker.Begin(track_1, flow_id);
253 slice_tracker->End(140, track_1, StringId::Raw(1), StringId::Raw(1));
254
255 // flow step inside slice2 on track2
256 slice_tracker->Begin(160, track_2, StringId::Raw(2), StringId::Raw(2));
257 SliceId inout_slice2_id =
258 slice_tracker->GetTopmostSliceOnTrack(track_2).value();
259 tracker.Step(track_2, flow_id);
260 slice_tracker->End(170, track_2, StringId::Raw(2), StringId::Raw(2));
261
262 // flow end inside slice3 on track3
263 slice_tracker->Begin(180, track_1, StringId::Raw(3), StringId::Raw(3));
264 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_1).value();
265 tracker.End(track_1, flow_id, /* bind_enclosing = */ true,
266 /* close_flow = */ false);
267 slice_tracker->End(190, track_1, StringId::Raw(3), StringId::Raw(3));
268
269 const auto& flows = context_.storage->flow_table();
270 EXPECT_EQ(flows.row_count(), 2u);
271 EXPECT_EQ(flows.slice_out()[0], out_slice1_id);
272 EXPECT_EQ(flows.slice_in()[0], inout_slice2_id);
273 EXPECT_EQ(flows.slice_out()[1], inout_slice2_id);
274 EXPECT_EQ(flows.slice_in()[1], in_slice_id);
275 }
276
277 } // namespace
278 } // namespace trace_processor
279 } // namespace perfetto
280