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
74 auto f = flows[0];
75 EXPECT_EQ(f.slice_out(), out_slice_id);
76 EXPECT_EQ(f.slice_in(), in_slice_id);
77 }
78
TEST_F(FlowTrackerTest,SingleFlowEventWaitForNextSlice)79 TEST_F(FlowTrackerTest, SingleFlowEventWaitForNextSlice) {
80 auto& slice_tracker = context_.slice_tracker;
81 FlowTracker tracker(&context_);
82 slice_tracker->SetOnSliceBeginCallback(
83 [&tracker](TrackId track_id, SliceId slice_id) {
84 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
85 });
86
87 FlowId flow_id = 1;
88 TrackId track_1(1);
89 TrackId track_2(2);
90
91 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
92 SliceId out_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_1).value();
93 tracker.Begin(track_1, flow_id);
94 slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1));
95
96 tracker.End(track_2, flow_id, /* bind_enclosing = */ false,
97 /* close_flow = */ false);
98
99 const auto& flows = context_.storage->flow_table();
100
101 EXPECT_EQ(flows.row_count(), 0u);
102
103 slice_tracker->Begin(140, track_2, StringId::Raw(2), StringId::Raw(2));
104 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value();
105 slice_tracker->End(160, track_2, StringId::Raw(2), StringId::Raw(2));
106
107 EXPECT_EQ(flows.row_count(), 1u);
108
109 auto f = flows[0];
110 EXPECT_EQ(f.slice_out(), out_slice_id);
111 EXPECT_EQ(f.slice_in(), in_slice_id);
112 }
113
TEST_F(FlowTrackerTest,SingleFlowEventWaitForNextSliceScoped)114 TEST_F(FlowTrackerTest, SingleFlowEventWaitForNextSliceScoped) {
115 auto& slice_tracker = context_.slice_tracker;
116 FlowTracker tracker(&context_);
117 slice_tracker->SetOnSliceBeginCallback(
118 [&tracker](TrackId track_id, SliceId slice_id) {
119 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
120 });
121
122 FlowId flow_id = 1;
123 TrackId track_1(1);
124 TrackId track_2(2);
125
126 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
127 SliceId out_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_1).value();
128 tracker.Begin(track_1, flow_id);
129 slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1));
130
131 tracker.End(track_2, flow_id, /* bind_enclosing = */ false,
132 /* close_flow = */ false);
133
134 const auto& flows = context_.storage->flow_table();
135
136 EXPECT_EQ(flows.row_count(), 0u);
137
138 slice_tracker->Scoped(140, track_2, StringId::Raw(2), StringId::Raw(2), 100);
139 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value();
140
141 EXPECT_EQ(flows.row_count(), 1u);
142
143 auto f = flows[0];
144 EXPECT_EQ(f.slice_out(), out_slice_id);
145 EXPECT_EQ(f.slice_in(), in_slice_id);
146 }
147
TEST_F(FlowTrackerTest,TwoFlowEventsWaitForNextSlice)148 TEST_F(FlowTrackerTest, TwoFlowEventsWaitForNextSlice) {
149 auto& slice_tracker = context_.slice_tracker;
150 FlowTracker tracker(&context_);
151 slice_tracker->SetOnSliceBeginCallback(
152 [&tracker](TrackId track_id, SliceId slice_id) {
153 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
154 });
155
156 FlowId flow1_id = 1;
157 FlowId flow2_id = 2;
158 TrackId track_1(1);
159 TrackId track_2(2);
160
161 // begin flow1 in enclosing slice1
162 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
163 SliceId out_slice1_id =
164 slice_tracker->GetTopmostSliceOnTrack(track_1).value();
165 tracker.Begin(track_1, flow1_id);
166 tracker.End(track_2, flow1_id, /* bind_enclosing = */ false,
167 /* close_flow = */ false);
168 slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1));
169
170 // begin flow2 in enclosing slice2
171 slice_tracker->Begin(130, track_1, StringId::Raw(2), StringId::Raw(2));
172 SliceId out_slice2_id =
173 slice_tracker->GetTopmostSliceOnTrack(track_1).value();
174 tracker.Begin(track_1, flow2_id);
175 tracker.End(track_2, flow2_id, /* bind_enclosing = */ false,
176 /* close_flow = */ false);
177 slice_tracker->End(140, track_1, StringId::Raw(2), StringId::Raw(2));
178
179 const auto& flows = context_.storage->flow_table();
180
181 EXPECT_EQ(flows.row_count(), 0u);
182
183 // close all pending flows
184 slice_tracker->Begin(160, track_2, StringId::Raw(3), StringId::Raw(3));
185 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value();
186 slice_tracker->End(170, track_2, StringId::Raw(3), StringId::Raw(3));
187
188 EXPECT_EQ(flows.row_count(), 2u);
189
190 auto f = flows[0];
191 EXPECT_EQ(f.slice_out(), out_slice1_id);
192 EXPECT_EQ(f.slice_in(), in_slice_id);
193
194 f = flows[1];
195 EXPECT_EQ(f.slice_out(), out_slice2_id);
196 EXPECT_EQ(f.slice_in(), in_slice_id);
197 }
198
TEST_F(FlowTrackerTest,TwoFlowEventsSliceInSlice)199 TEST_F(FlowTrackerTest, TwoFlowEventsSliceInSlice) {
200 auto& slice_tracker = context_.slice_tracker;
201 FlowTracker tracker(&context_);
202 slice_tracker->SetOnSliceBeginCallback(
203 [&tracker](TrackId track_id, SliceId slice_id) {
204 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
205 });
206
207 FlowId flow1_id = 1;
208 FlowId flow2_id = 2;
209 TrackId track_1(1);
210 TrackId track_2(2);
211
212 // start two nested slices
213 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
214 SliceId out_slice1_id =
215 slice_tracker->GetTopmostSliceOnTrack(track_1).value();
216 slice_tracker->Begin(120, track_1, StringId::Raw(2), StringId::Raw(2));
217 SliceId out_slice2_id =
218 slice_tracker->GetTopmostSliceOnTrack(track_1).value();
219
220 tracker.Begin(track_1, flow1_id);
221
222 slice_tracker->End(140, track_1, StringId::Raw(2), StringId::Raw(2));
223
224 tracker.Begin(track_1, flow2_id);
225
226 slice_tracker->End(150, track_1, StringId::Raw(1), StringId::Raw(1));
227
228 slice_tracker->Begin(160, track_2, StringId::Raw(3), StringId::Raw(3));
229 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value();
230
231 tracker.End(track_2, flow1_id, /* bind_enclosing = */ true,
232 /* close_flow = */ false);
233 tracker.End(track_2, flow2_id, /* bind_enclosing = */ true,
234 /* close_flow = */ false);
235
236 slice_tracker->End(170, track_2, StringId::Raw(3), StringId::Raw(3));
237
238 const auto& flows = context_.storage->flow_table();
239 EXPECT_EQ(flows.row_count(), 2u);
240
241 auto f = flows[0];
242 EXPECT_EQ(f.slice_out(), out_slice2_id);
243 EXPECT_EQ(f.slice_in(), in_slice_id);
244
245 f = flows[1];
246 EXPECT_EQ(f.slice_out(), out_slice1_id);
247 EXPECT_EQ(f.slice_in(), in_slice_id);
248 }
249
TEST_F(FlowTrackerTest,FlowEventsWithStep)250 TEST_F(FlowTrackerTest, FlowEventsWithStep) {
251 auto& slice_tracker = context_.slice_tracker;
252 FlowTracker tracker(&context_);
253 slice_tracker->SetOnSliceBeginCallback(
254 [&tracker](TrackId track_id, SliceId slice_id) {
255 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
256 });
257
258 FlowId flow_id = 1;
259 TrackId track_1(1);
260 TrackId track_2(2);
261
262 // flow begin inside slice1 on track1
263 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
264 SliceId out_slice1_id =
265 slice_tracker->GetTopmostSliceOnTrack(track_1).value();
266 tracker.Begin(track_1, flow_id);
267 slice_tracker->End(140, track_1, StringId::Raw(1), StringId::Raw(1));
268
269 // flow step inside slice2 on track2
270 slice_tracker->Begin(160, track_2, StringId::Raw(2), StringId::Raw(2));
271 SliceId inout_slice2_id =
272 slice_tracker->GetTopmostSliceOnTrack(track_2).value();
273 tracker.Step(track_2, flow_id);
274 slice_tracker->End(170, track_2, StringId::Raw(2), StringId::Raw(2));
275
276 // flow end inside slice3 on track3
277 slice_tracker->Begin(180, track_1, StringId::Raw(3), StringId::Raw(3));
278 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_1).value();
279 tracker.End(track_1, flow_id, /* bind_enclosing = */ true,
280 /* close_flow = */ false);
281 slice_tracker->End(190, track_1, StringId::Raw(3), StringId::Raw(3));
282
283 const auto& flows = context_.storage->flow_table();
284 EXPECT_EQ(flows.row_count(), 2u);
285
286 auto f = flows[0];
287 EXPECT_EQ(f.slice_out(), out_slice1_id);
288 EXPECT_EQ(f.slice_in(), inout_slice2_id);
289
290 f = flows[1];
291 EXPECT_EQ(f.slice_out(), inout_slice2_id);
292 EXPECT_EQ(f.slice_in(), in_slice_id);
293 }
294
295 } // namespace
296 } // namespace trace_processor
297 } // namespace perfetto
298