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/flow_tracker.h"
20 #include "src/trace_processor/importers/common/slice_tracker.h"
21 #include "src/trace_processor/importers/common/slice_translation_table.h"
22 #include "src/trace_processor/storage/trace_storage.h"
23 #include "src/trace_processor/types/trace_processor_context.h"
24 #include "test/gtest_and_gmock.h"
25
26 namespace perfetto {
27 namespace trace_processor {
28 namespace {
29
30 using ::testing::Eq;
31
TEST(FlowTrackerTest,SingleFlowEventExplicitInSliceBinding)32 TEST(FlowTrackerTest, SingleFlowEventExplicitInSliceBinding) {
33 TraceProcessorContext context;
34 context.storage.reset(new TraceStorage());
35 context.slice_tracker.reset(new SliceTracker(&context));
36 context.slice_translation_table.reset(
37 new SliceTranslationTable(context.storage.get()));
38 auto& slice_tracker = context.slice_tracker;
39 FlowTracker tracker(&context);
40 slice_tracker->SetOnSliceBeginCallback(
41 [&tracker](TrackId track_id, SliceId slice_id) {
42 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
43 });
44
45 FlowId flow_id = 1;
46 TrackId track_1(1);
47 TrackId track_2(2);
48
49 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
50 SliceId out_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_1).value();
51 tracker.Begin(track_1, flow_id);
52 slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1));
53
54 slice_tracker->Begin(140, track_2, StringId::Raw(2), StringId::Raw(2));
55 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value();
56 tracker.End(track_2, flow_id, /* bind_enclosing = */ true,
57 /* close_flow = */ false);
58 slice_tracker->End(160, track_2, StringId::Raw(2), StringId::Raw(2));
59
60 const auto& flows = context.storage->flow_table();
61 EXPECT_EQ(flows.row_count(), 1u);
62 EXPECT_EQ(flows.slice_out()[0], out_slice_id);
63 EXPECT_EQ(flows.slice_in()[0], in_slice_id);
64 }
65
TEST(FlowTrackerTest,SingleFlowEventWaitForNextSlice)66 TEST(FlowTrackerTest, SingleFlowEventWaitForNextSlice) {
67 TraceProcessorContext context;
68 context.storage.reset(new TraceStorage());
69 context.slice_tracker.reset(new SliceTracker(&context));
70 context.slice_translation_table.reset(
71 new SliceTranslationTable(context.storage.get()));
72 auto& slice_tracker = context.slice_tracker;
73 FlowTracker tracker(&context);
74 slice_tracker->SetOnSliceBeginCallback(
75 [&tracker](TrackId track_id, SliceId slice_id) {
76 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
77 });
78
79 FlowId flow_id = 1;
80 TrackId track_1(1);
81 TrackId track_2(2);
82
83 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
84 SliceId out_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_1).value();
85 tracker.Begin(track_1, flow_id);
86 slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1));
87
88 tracker.End(track_2, flow_id, /* bind_enclosing = */ false,
89 /* close_flow = */ false);
90
91 const auto& flows = context.storage->flow_table();
92
93 EXPECT_EQ(flows.row_count(), 0u);
94
95 slice_tracker->Begin(140, track_2, StringId::Raw(2), StringId::Raw(2));
96 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value();
97 slice_tracker->End(160, track_2, StringId::Raw(2), StringId::Raw(2));
98
99 EXPECT_EQ(flows.row_count(), 1u);
100 EXPECT_EQ(flows.slice_out()[0], out_slice_id);
101 EXPECT_EQ(flows.slice_in()[0], in_slice_id);
102 }
103
TEST(FlowTrackerTest,SingleFlowEventWaitForNextSliceScoped)104 TEST(FlowTrackerTest, SingleFlowEventWaitForNextSliceScoped) {
105 TraceProcessorContext context;
106 context.storage.reset(new TraceStorage());
107 context.slice_tracker.reset(new SliceTracker(&context));
108 context.slice_translation_table.reset(
109 new SliceTranslationTable(context.storage.get()));
110 auto& slice_tracker = context.slice_tracker;
111 FlowTracker tracker(&context);
112 slice_tracker->SetOnSliceBeginCallback(
113 [&tracker](TrackId track_id, SliceId slice_id) {
114 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
115 });
116
117 FlowId flow_id = 1;
118 TrackId track_1(1);
119 TrackId track_2(2);
120
121 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
122 SliceId out_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_1).value();
123 tracker.Begin(track_1, flow_id);
124 slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1));
125
126 tracker.End(track_2, flow_id, /* bind_enclosing = */ false,
127 /* close_flow = */ false);
128
129 const auto& flows = context.storage->flow_table();
130
131 EXPECT_EQ(flows.row_count(), 0u);
132
133 slice_tracker->Scoped(140, track_2, StringId::Raw(2), StringId::Raw(2), 100);
134 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value();
135
136 EXPECT_EQ(flows.row_count(), 1u);
137 EXPECT_EQ(flows.slice_out()[0], out_slice_id);
138 EXPECT_EQ(flows.slice_in()[0], in_slice_id);
139 }
140
TEST(FlowTrackerTest,TwoFlowEventsWaitForNextSlice)141 TEST(FlowTrackerTest, TwoFlowEventsWaitForNextSlice) {
142 TraceProcessorContext context;
143 context.storage.reset(new TraceStorage());
144 context.slice_tracker.reset(new SliceTracker(&context));
145 context.slice_translation_table.reset(
146 new SliceTranslationTable(context.storage.get()));
147 auto& slice_tracker = context.slice_tracker;
148 FlowTracker tracker(&context);
149 slice_tracker->SetOnSliceBeginCallback(
150 [&tracker](TrackId track_id, SliceId slice_id) {
151 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
152 });
153
154 FlowId flow1_id = 1;
155 FlowId flow2_id = 2;
156 TrackId track_1(1);
157 TrackId track_2(2);
158
159 // begin flow1 in enclosing slice1
160 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
161 SliceId out_slice1_id =
162 slice_tracker->GetTopmostSliceOnTrack(track_1).value();
163 tracker.Begin(track_1, flow1_id);
164 tracker.End(track_2, flow1_id, /* bind_enclosing = */ false,
165 /* close_flow = */ false);
166 slice_tracker->End(120, track_1, StringId::Raw(1), StringId::Raw(1));
167
168 // begin flow2 in enclosing slice2
169 slice_tracker->Begin(130, track_1, StringId::Raw(2), StringId::Raw(2));
170 SliceId out_slice2_id =
171 slice_tracker->GetTopmostSliceOnTrack(track_1).value();
172 tracker.Begin(track_1, flow2_id);
173 tracker.End(track_2, flow2_id, /* bind_enclosing = */ false,
174 /* close_flow = */ false);
175 slice_tracker->End(140, track_1, StringId::Raw(2), StringId::Raw(2));
176
177 const auto& flows = context.storage->flow_table();
178
179 EXPECT_EQ(flows.row_count(), 0u);
180
181 // close all pending flows
182 slice_tracker->Begin(160, track_2, StringId::Raw(3), StringId::Raw(3));
183 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value();
184 slice_tracker->End(170, track_2, StringId::Raw(3), StringId::Raw(3));
185
186 EXPECT_EQ(flows.row_count(), 2u);
187 EXPECT_EQ(flows.slice_out()[0], out_slice1_id);
188 EXPECT_EQ(flows.slice_in()[0], in_slice_id);
189 EXPECT_EQ(flows.slice_out()[1], out_slice2_id);
190 EXPECT_EQ(flows.slice_in()[1], in_slice_id);
191 }
192
TEST(FlowTrackerTest,TwoFlowEventsSliceInSlice)193 TEST(FlowTrackerTest, TwoFlowEventsSliceInSlice) {
194 TraceProcessorContext context;
195 context.storage.reset(new TraceStorage());
196 context.slice_tracker.reset(new SliceTracker(&context));
197 context.slice_translation_table.reset(
198 new SliceTranslationTable(context.storage.get()));
199 auto& slice_tracker = context.slice_tracker;
200 FlowTracker tracker(&context);
201 slice_tracker->SetOnSliceBeginCallback(
202 [&tracker](TrackId track_id, SliceId slice_id) {
203 tracker.ClosePendingEventsOnTrack(track_id, slice_id);
204 });
205
206 FlowId flow1_id = 1;
207 FlowId flow2_id = 2;
208 TrackId track_1(1);
209 TrackId track_2(2);
210
211 // start two nested slices
212 slice_tracker->Begin(100, track_1, StringId::Raw(1), StringId::Raw(1));
213 SliceId out_slice1_id =
214 slice_tracker->GetTopmostSliceOnTrack(track_1).value();
215 slice_tracker->Begin(120, track_1, StringId::Raw(2), StringId::Raw(2));
216 SliceId out_slice2_id =
217 slice_tracker->GetTopmostSliceOnTrack(track_1).value();
218
219 tracker.Begin(track_1, flow1_id);
220
221 slice_tracker->End(140, track_1, StringId::Raw(2), StringId::Raw(2));
222
223 tracker.Begin(track_1, flow2_id);
224
225 slice_tracker->End(150, track_1, StringId::Raw(1), StringId::Raw(1));
226
227 slice_tracker->Begin(160, track_2, StringId::Raw(3), StringId::Raw(3));
228 SliceId in_slice_id = slice_tracker->GetTopmostSliceOnTrack(track_2).value();
229
230 tracker.End(track_2, flow1_id, /* bind_enclosing = */ true,
231 /* close_flow = */ false);
232 tracker.End(track_2, flow2_id, /* bind_enclosing = */ true,
233 /* close_flow = */ false);
234
235 slice_tracker->End(170, track_2, StringId::Raw(3), StringId::Raw(3));
236
237 const auto& flows = context.storage->flow_table();
238 EXPECT_EQ(flows.row_count(), 2u);
239 EXPECT_EQ(flows.slice_out()[0], out_slice2_id);
240 EXPECT_EQ(flows.slice_in()[0], in_slice_id);
241 EXPECT_EQ(flows.slice_out()[1], out_slice1_id);
242 EXPECT_EQ(flows.slice_in()[1], in_slice_id);
243 }
244
TEST(FlowTrackerTest,FlowEventsWithStep)245 TEST(FlowTrackerTest, FlowEventsWithStep) {
246 TraceProcessorContext context;
247 context.storage.reset(new TraceStorage());
248 context.slice_tracker.reset(new SliceTracker(&context));
249 context.slice_translation_table.reset(
250 new SliceTranslationTable(context.storage.get()));
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 EXPECT_EQ(flows.slice_out()[0], out_slice1_id);
286 EXPECT_EQ(flows.slice_in()[0], inout_slice2_id);
287 EXPECT_EQ(flows.slice_out()[1], inout_slice2_id);
288 EXPECT_EQ(flows.slice_in()[1], in_slice_id);
289 }
290
291 } // namespace
292 } // namespace trace_processor
293 } // namespace perfetto
294