1 /*
2 * Copyright (C) 2018 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 <cstdint>
18 #include <memory>
19 #include <optional>
20 #include <ostream>
21 #include <tuple>
22 #include <vector>
23
24 #include "src/trace_processor/importers/common/args_tracker.h"
25 #include "src/trace_processor/importers/common/args_translation_table.h"
26 #include "src/trace_processor/importers/common/slice_tracker.h"
27 #include "src/trace_processor/importers/common/slice_translation_table.h"
28 #include "src/trace_processor/storage/trace_storage.h"
29 #include "src/trace_processor/tables/slice_tables_py.h"
30 #include "src/trace_processor/types/trace_processor_context.h"
31 #include "src/trace_processor/types/variadic.h"
32 #include "test/gtest_and_gmock.h"
33
34 namespace perfetto::trace_processor {
35 namespace {
36
37 using ::testing::ElementsAre;
38 using ::testing::Eq;
39
40 struct SliceInfo {
41 int64_t start;
42 int64_t duration;
43
operator ==perfetto::trace_processor::__anon5ad6184d0111::SliceInfo44 bool operator==(const SliceInfo& other) const {
45 return std::tie(start, duration) == std::tie(other.start, other.duration);
46 }
47 };
48
PrintTo(const SliceInfo & info,::std::ostream * os)49 inline void PrintTo(const SliceInfo& info, ::std::ostream* os) {
50 *os << "SliceInfo{" << info.start << ", " << info.duration << "}";
51 }
52
ToSliceInfo(const tables::SliceTable & slices)53 std::vector<SliceInfo> ToSliceInfo(const tables::SliceTable& slices) {
54 std::vector<SliceInfo> infos;
55 for (auto it = slices.IterateRows(); it; ++it) {
56 infos.emplace_back(SliceInfo{it.ts(), it.dur()});
57 }
58 return infos;
59 }
60
61 class SliceTrackerTest : public ::testing::Test {
62 public:
SliceTrackerTest()63 SliceTrackerTest() {
64 context_.storage = std::make_unique<TraceStorage>();
65 context_.global_args_tracker =
66 std::make_unique<GlobalArgsTracker>(context_.storage.get());
67 context_.args_translation_table =
68 std::make_unique<ArgsTranslationTable>(context_.storage.get());
69 context_.slice_translation_table =
70 std::make_unique<SliceTranslationTable>(context_.storage.get());
71 }
72
73 protected:
74 TraceProcessorContext context_;
75 };
76
TEST_F(SliceTrackerTest,OneSliceDetailed)77 TEST_F(SliceTrackerTest, OneSliceDetailed) {
78 SliceTracker tracker(&context_);
79
80 constexpr TrackId track{22u};
81 tracker.Begin(2 /*ts*/, track, kNullStringId /*cat*/,
82 StringId::Raw(1) /*name*/);
83 tracker.End(10 /*ts*/, track, kNullStringId /*cat*/,
84 StringId::Raw(1) /*name*/);
85
86 const auto& slices = context_.storage->slice_table();
87 EXPECT_EQ(slices.row_count(), 1u);
88 EXPECT_EQ(slices[0].ts(), 2);
89 EXPECT_EQ(slices[0].dur(), 8);
90 EXPECT_EQ(slices[0].track_id(), track);
91 EXPECT_EQ(slices[0].category().value_or(kNullStringId).raw_id(), 0u);
92 EXPECT_EQ(slices[0].name().value_or(kNullStringId).raw_id(), 1u);
93 EXPECT_EQ(slices[0].depth(), 0u);
94 EXPECT_EQ(slices[0].arg_set_id(), kInvalidArgSetId);
95 }
96
TEST_F(SliceTrackerTest,OneSliceDetailedWithTranslatedName)97 TEST_F(SliceTrackerTest, OneSliceDetailedWithTranslatedName) {
98 SliceTracker tracker(&context_);
99
100 const StringId raw_name = context_.storage->InternString("raw_name");
101 const StringId mapped_name = context_.storage->InternString("mapped_name");
102 context_.slice_translation_table->AddNameTranslationRule("raw_name",
103 "mapped_name");
104
105 constexpr TrackId track{22u};
106 tracker.Begin(2 /*ts*/, track, kNullStringId /*cat*/, raw_name /*name*/);
107 tracker.End(10 /*ts*/, track, kNullStringId /*cat*/, raw_name /*name*/);
108
109 const auto& slices = context_.storage->slice_table();
110 EXPECT_EQ(slices.row_count(), 1u);
111 EXPECT_EQ(slices[0].ts(), 2);
112 EXPECT_EQ(slices[0].dur(), 8);
113 EXPECT_EQ(slices[0].track_id(), track);
114 EXPECT_EQ(slices[0].category().value_or(kNullStringId).raw_id(), 0u);
115 EXPECT_EQ(slices[0].name().value_or(kNullStringId).raw_id(),
116 mapped_name.raw_id());
117 EXPECT_EQ(slices[0].depth(), 0u);
118 EXPECT_EQ(slices[0].arg_set_id(), kInvalidArgSetId);
119 }
120
TEST_F(SliceTrackerTest,NegativeTimestamps)121 TEST_F(SliceTrackerTest, NegativeTimestamps) {
122 SliceTracker tracker(&context_);
123
124 constexpr TrackId track{22u};
125 tracker.Begin(-1000 /*ts*/, track, kNullStringId /*cat*/,
126 StringId::Raw(1) /*name*/);
127 tracker.End(-501 /*ts*/, track, kNullStringId /*cat*/,
128 StringId::Raw(1) /*name*/);
129
130 const auto& slices = context_.storage->slice_table();
131 EXPECT_EQ(slices.row_count(), 1u);
132
133 auto rr = slices[0];
134 EXPECT_EQ(rr.ts(), -1000);
135 EXPECT_EQ(rr.dur(), 499);
136 EXPECT_EQ(rr.track_id(), track);
137 EXPECT_EQ(rr.category().value_or(kNullStringId).raw_id(), 0u);
138 EXPECT_EQ(rr.name().value_or(kNullStringId).raw_id(), 1u);
139 EXPECT_EQ(rr.depth(), 0u);
140 EXPECT_EQ(rr.arg_set_id(), kInvalidArgSetId);
141 }
142
TEST_F(SliceTrackerTest,OneSliceWithArgs)143 TEST_F(SliceTrackerTest, OneSliceWithArgs) {
144 SliceTracker tracker(&context_);
145
146 constexpr TrackId track{22u};
147 tracker.Begin(2 /*ts*/, track, kNullStringId /*cat*/,
148 StringId::Raw(1) /*name*/,
149 [](ArgsTracker::BoundInserter* inserter) {
150 inserter->AddArg(/*flat_key=*/StringId::Raw(1),
151 /*key=*/StringId::Raw(2),
152 /*v=*/Variadic::Integer(10));
153 });
154 tracker.End(10 /*ts*/, track, kNullStringId /*cat*/,
155 StringId::Raw(1) /*name*/,
156 [](ArgsTracker::BoundInserter* inserter) {
157 inserter->AddArg(/*flat_key=*/StringId::Raw(3),
158 /*key=*/StringId::Raw(4),
159 /*v=*/Variadic::Integer(20));
160 });
161
162 const auto& slices = context_.storage->slice_table();
163 EXPECT_EQ(slices.row_count(), 1u);
164
165 auto sr = slices[0];
166 EXPECT_EQ(sr.ts(), 2);
167 EXPECT_EQ(sr.dur(), 8);
168 EXPECT_EQ(sr.track_id(), track);
169 EXPECT_EQ(sr.category().value_or(kNullStringId).raw_id(), 0u);
170 EXPECT_EQ(sr.name().value_or(kNullStringId).raw_id(), 1u);
171 EXPECT_EQ(sr.depth(), 0u);
172 auto set_id = sr.arg_set_id();
173
174 const auto& args = context_.storage->arg_table();
175 auto ar0 = args[0];
176 auto ar1 = args[1];
177 EXPECT_EQ(ar0.arg_set_id(), set_id);
178 EXPECT_EQ(ar0.flat_key().raw_id(), 1u);
179 EXPECT_EQ(ar0.key().raw_id(), 2u);
180 EXPECT_EQ(ar0.int_value(), 10);
181 EXPECT_EQ(ar1.arg_set_id(), set_id);
182 EXPECT_EQ(ar1.flat_key().raw_id(), 3u);
183 EXPECT_EQ(ar1.key().raw_id(), 4u);
184 EXPECT_EQ(ar1.int_value(), 20);
185 }
186
TEST_F(SliceTrackerTest,OneSliceWithArgsWithTranslatedName)187 TEST_F(SliceTrackerTest, OneSliceWithArgsWithTranslatedName) {
188 SliceTracker tracker(&context_);
189
190 const StringId raw_name = context_.storage->InternString("raw_name");
191 const StringId mapped_name = context_.storage->InternString("mapped_name");
192 context_.slice_translation_table->AddNameTranslationRule("raw_name",
193 "mapped_name");
194
195 constexpr TrackId track{22u};
196 tracker.Begin(2 /*ts*/, track, kNullStringId /*cat*/, raw_name /*name*/,
197 [](ArgsTracker::BoundInserter* inserter) {
198 inserter->AddArg(/*flat_key=*/StringId::Raw(1),
199 /*key=*/StringId::Raw(2),
200 /*v=*/Variadic::Integer(10));
201 });
202 tracker.End(10 /*ts*/, track, kNullStringId /*cat*/, raw_name /*name*/,
203 [](ArgsTracker::BoundInserter* inserter) {
204 inserter->AddArg(/*flat_key=*/StringId::Raw(3),
205 /*key=*/StringId::Raw(4),
206 /*v=*/Variadic::Integer(20));
207 });
208
209 const auto& slices = context_.storage->slice_table();
210 EXPECT_EQ(slices.row_count(), 1u);
211
212 auto sr = slices[0];
213 EXPECT_EQ(sr.ts(), 2);
214 EXPECT_EQ(sr.dur(), 8);
215 EXPECT_EQ(sr.track_id(), track);
216 EXPECT_EQ(sr.category().value_or(kNullStringId).raw_id(), 0u);
217 EXPECT_EQ(sr.name().value_or(kNullStringId).raw_id(), mapped_name.raw_id());
218 EXPECT_EQ(sr.depth(), 0u);
219 auto set_id = sr.arg_set_id();
220
221 const auto& args = context_.storage->arg_table();
222 auto ar0 = args[0];
223 auto ar1 = args[1];
224 EXPECT_EQ(ar0.arg_set_id(), set_id);
225 EXPECT_EQ(ar0.flat_key().raw_id(), 1u);
226 EXPECT_EQ(ar0.key().raw_id(), 2u);
227 EXPECT_EQ(ar0.int_value(), 10);
228 EXPECT_EQ(ar1.arg_set_id(), set_id);
229 EXPECT_EQ(ar1.flat_key().raw_id(), 3u);
230 EXPECT_EQ(ar1.key().raw_id(), 4u);
231 EXPECT_EQ(ar1.int_value(), 20);
232 }
233
TEST_F(SliceTrackerTest,TwoSliceDetailed)234 TEST_F(SliceTrackerTest, TwoSliceDetailed) {
235 SliceTracker tracker(&context_);
236
237 constexpr TrackId track{22u};
238 tracker.Begin(2 /*ts*/, track, kNullStringId /*cat*/,
239 StringId::Raw(1) /*name*/);
240 tracker.Begin(3 /*ts*/, track, kNullStringId /*cat*/,
241 StringId::Raw(2) /*name*/);
242 tracker.End(5 /*ts*/, track);
243 tracker.End(10 /*ts*/, track);
244
245 const auto& slices = context_.storage->slice_table();
246
247 EXPECT_EQ(slices.row_count(), 2u);
248
249 auto sr0 = slices[0];
250 EXPECT_EQ(sr0.ts(), 2);
251 EXPECT_EQ(sr0.dur(), 8);
252 EXPECT_EQ(sr0.track_id(), track);
253 EXPECT_EQ(sr0.category().value_or(kNullStringId).raw_id(), 0u);
254 EXPECT_EQ(sr0.name().value_or(kNullStringId).raw_id(), 1u);
255 EXPECT_EQ(sr0.depth(), 0u);
256 EXPECT_EQ(sr0.parent_stack_id(), 0);
257
258 auto sr1 = slices[1];
259 EXPECT_EQ(sr1.ts(), 3);
260 EXPECT_EQ(sr1.dur(), 2);
261 EXPECT_EQ(sr1.track_id(), track);
262 EXPECT_EQ(sr1.category().value_or(kNullStringId).raw_id(), 0u);
263 EXPECT_EQ(sr1.name().value_or(kNullStringId).raw_id(), 2u);
264 EXPECT_EQ(sr1.depth(), 1u);
265 EXPECT_NE(sr1.stack_id(), 0);
266
267 EXPECT_EQ(sr0.stack_id(), sr1.parent_stack_id());
268 }
269
TEST_F(SliceTrackerTest,Scoped)270 TEST_F(SliceTrackerTest, Scoped) {
271 SliceTracker tracker(&context_);
272
273 constexpr TrackId track{22u};
274 tracker.Begin(0 /*ts*/, track, kNullStringId, kNullStringId);
275 tracker.Begin(1 /*ts*/, track, kNullStringId, kNullStringId);
276 tracker.Scoped(2 /*ts*/, track, kNullStringId, kNullStringId, 6);
277 tracker.End(9 /*ts*/, track);
278 tracker.End(10 /*ts*/, track);
279
280 auto slices = ToSliceInfo(context_.storage->slice_table());
281 EXPECT_THAT(slices,
282 ElementsAre(SliceInfo{0, 10}, SliceInfo{1, 8}, SliceInfo{2, 6}));
283 }
284
TEST_F(SliceTrackerTest,ScopedWithTranslatedName)285 TEST_F(SliceTrackerTest, ScopedWithTranslatedName) {
286 SliceTracker tracker(&context_);
287
288 const StringId raw_name = context_.storage->InternString("raw_name");
289 context_.slice_translation_table->AddNameTranslationRule("raw_name",
290 "mapped_name");
291
292 constexpr TrackId track{22u};
293 tracker.Begin(0 /*ts*/, track, kNullStringId, raw_name);
294 tracker.Begin(1 /*ts*/, track, kNullStringId, raw_name);
295 tracker.Scoped(2 /*ts*/, track, kNullStringId, raw_name, 6);
296 tracker.End(9 /*ts*/, track);
297 tracker.End(10 /*ts*/, track);
298
299 auto slices = ToSliceInfo(context_.storage->slice_table());
300 EXPECT_THAT(slices,
301 ElementsAre(SliceInfo{0, 10}, SliceInfo{1, 8}, SliceInfo{2, 6}));
302 }
303
TEST_F(SliceTrackerTest,ParentId)304 TEST_F(SliceTrackerTest, ParentId) {
305 SliceTracker tracker(&context_);
306
307 constexpr TrackId track{22u};
308 tracker.Begin(100, track, kNullStringId, kNullStringId);
309 tracker.Begin(101, track, kNullStringId, kNullStringId);
310 tracker.Begin(102, track, kNullStringId, kNullStringId);
311 tracker.End(103, track);
312 tracker.End(150, track);
313 tracker.End(200, track);
314
315 SliceId parent = context_.storage->slice_table()[0].id();
316 SliceId child = context_.storage->slice_table()[1].id();
317 EXPECT_THAT(context_.storage->slice_table().parent_id().ToVectorForTesting(),
318 ElementsAre(std::nullopt, parent, child));
319 }
320
TEST_F(SliceTrackerTest,IgnoreMismatchedEnds)321 TEST_F(SliceTrackerTest, IgnoreMismatchedEnds) {
322 SliceTracker tracker(&context_);
323
324 constexpr TrackId track{22u};
325 tracker.Begin(2 /*ts*/, track, StringId::Raw(5) /*cat*/,
326 StringId::Raw(1) /*name*/);
327 tracker.End(3 /*ts*/, track, StringId::Raw(1) /*cat*/,
328 StringId::Raw(1) /*name*/);
329 tracker.End(4 /*ts*/, track, kNullStringId /*cat*/,
330 StringId::Raw(2) /*name*/);
331 tracker.End(5 /*ts*/, track, StringId::Raw(5) /*cat*/,
332 StringId::Raw(1) /*name*/);
333
334 auto slices = ToSliceInfo(context_.storage->slice_table());
335 EXPECT_THAT(slices, ElementsAre(SliceInfo{2, 3}));
336 }
337
TEST_F(SliceTrackerTest,ZeroLengthScoped)338 TEST_F(SliceTrackerTest, ZeroLengthScoped) {
339 SliceTracker tracker(&context_);
340
341 // Bug scenario: the second zero-length scoped slice prevents the first slice
342 // from being closed, leading to an inconsistency when we try to insert the
343 // final slice and it doesn't intersect with the still pending first slice.
344 constexpr TrackId track{22u};
345 tracker.Scoped(2 /*ts*/, track, kNullStringId /*cat*/,
346 StringId::Raw(1) /*name*/, 10 /* dur */);
347 tracker.Scoped(2 /*ts*/, track, kNullStringId /*cat*/,
348 StringId::Raw(1) /*name*/, 0 /* dur */);
349 tracker.Scoped(12 /*ts*/, track, kNullStringId /*cat*/,
350 StringId::Raw(1) /*name*/, 1 /* dur */);
351 tracker.Scoped(13 /*ts*/, track, kNullStringId /*cat*/,
352 StringId::Raw(1) /*name*/, 1 /* dur */);
353
354 auto slices = ToSliceInfo(context_.storage->slice_table());
355 EXPECT_THAT(slices, ElementsAre(SliceInfo{2, 10}, SliceInfo{2, 0},
356 SliceInfo{12, 1}, SliceInfo{13, 1}));
357 }
358
TEST_F(SliceTrackerTest,DifferentTracks)359 TEST_F(SliceTrackerTest, DifferentTracks) {
360 SliceTracker tracker(&context_);
361
362 constexpr TrackId track_a{22u};
363 constexpr TrackId track_b{23u};
364 tracker.Begin(0 /*ts*/, track_a, kNullStringId, kNullStringId);
365 tracker.Scoped(2 /*ts*/, track_b, kNullStringId, kNullStringId, 6);
366 tracker.Scoped(3 /*ts*/, track_b, kNullStringId, kNullStringId, 4);
367 tracker.End(10 /*ts*/, track_a);
368 tracker.FlushPendingSlices();
369
370 const auto& table = context_.storage->slice_table();
371 auto slices = ToSliceInfo(table);
372 EXPECT_THAT(slices,
373 ElementsAre(SliceInfo{0, 10}, SliceInfo{2, 6}, SliceInfo{3, 4}));
374
375 EXPECT_EQ(table[0].track_id(), track_a);
376 EXPECT_EQ(table[1].track_id(), track_b);
377 EXPECT_EQ(table[2].track_id(), track_b);
378 EXPECT_EQ(table[0].depth(), 0u);
379 EXPECT_EQ(table[1].depth(), 0u);
380 EXPECT_EQ(table[2].depth(), 1u);
381 }
382
TEST_F(SliceTrackerTest,EndEventOutOfOrder)383 TEST_F(SliceTrackerTest, EndEventOutOfOrder) {
384 SliceTracker tracker(&context_);
385
386 constexpr TrackId track{22u};
387 tracker.Scoped(50 /*ts*/, track, StringId::Raw(11) /*cat*/,
388 StringId::Raw(21) /*name*/, 100 /*dur*/);
389 tracker.Begin(100 /*ts*/, track, StringId::Raw(12) /*cat*/,
390 StringId::Raw(22) /*name*/);
391
392 // This slice should now have depth 0.
393 tracker.Scoped(450 /*ts*/, track, StringId::Raw(12) /*cat*/,
394 StringId::Raw(22) /*name*/, 100 /*dur*/);
395
396 // This slice should be ignored.
397 tracker.End(500 /*ts*/, track, StringId::Raw(12) /*cat*/,
398 StringId::Raw(22) /*name*/);
399
400 tracker.Begin(800 /*ts*/, track, StringId::Raw(13) /*cat*/,
401 StringId::Raw(23) /*name*/);
402 // Null cat and name matches everything.
403 tracker.End(1000 /*ts*/, track, kNullStringId /*cat*/,
404 kNullStringId /*name*/);
405
406 // Slice will not close if category is different.
407 tracker.Begin(1100 /*ts*/, track, StringId::Raw(11) /*cat*/,
408 StringId::Raw(21) /*name*/);
409 tracker.End(1200 /*ts*/, track, StringId::Raw(12) /*cat*/,
410 StringId::Raw(21) /*name*/);
411
412 // Slice will not close if name is different.
413 tracker.Begin(1300 /*ts*/, track, StringId::Raw(11) /*cat*/,
414 StringId::Raw(21) /*name*/);
415 tracker.End(1400 /*ts*/, track, StringId::Raw(11) /*cat*/,
416 StringId::Raw(22) /*name*/);
417
418 tracker.FlushPendingSlices();
419
420 const auto& st = context_.storage->slice_table();
421 auto slices = ToSliceInfo(st);
422 EXPECT_THAT(slices, ElementsAre(SliceInfo{50, 100}, SliceInfo{100, 50},
423 SliceInfo{450, 100}, SliceInfo{800, 200},
424 SliceInfo{1100, -1}, SliceInfo{1300, 0 - 1}));
425
426 EXPECT_EQ(st[0].depth(), 0u);
427 EXPECT_EQ(st[1].depth(), 1u);
428 EXPECT_EQ(st[2].depth(), 0u);
429 EXPECT_EQ(st[3].depth(), 0u);
430 }
431
TEST_F(SliceTrackerTest,GetTopmostSliceOnTrack)432 TEST_F(SliceTrackerTest, GetTopmostSliceOnTrack) {
433 SliceTracker tracker(&context_);
434
435 TrackId track{1u};
436 TrackId track2{2u};
437
438 EXPECT_EQ(tracker.GetTopmostSliceOnTrack(track), std::nullopt);
439
440 tracker.Begin(100, track, StringId::Raw(11), StringId::Raw(11));
441 SliceId slice1 = context_.storage->slice_table()[0].id();
442
443 EXPECT_EQ(tracker.GetTopmostSliceOnTrack(track).value(), slice1);
444
445 tracker.Begin(120, track, StringId::Raw(22), StringId::Raw(22));
446 SliceId slice2 = context_.storage->slice_table()[1].id();
447
448 EXPECT_EQ(tracker.GetTopmostSliceOnTrack(track).value(), slice2);
449
450 EXPECT_EQ(tracker.GetTopmostSliceOnTrack(track2), std::nullopt);
451
452 tracker.End(140, track, StringId::Raw(22), StringId::Raw(22));
453
454 EXPECT_EQ(tracker.GetTopmostSliceOnTrack(track).value(), slice1);
455
456 tracker.End(330, track, StringId::Raw(11), StringId::Raw(11));
457
458 EXPECT_EQ(tracker.GetTopmostSliceOnTrack(track), std::nullopt);
459 }
460
TEST_F(SliceTrackerTest,OnSliceBeginCallback)461 TEST_F(SliceTrackerTest, OnSliceBeginCallback) {
462 SliceTracker tracker(&context_);
463
464 TrackId track1{1u};
465 TrackId track2{2u};
466
467 std::vector<TrackId> track_records;
468 std::vector<SliceId> slice_records;
469 tracker.SetOnSliceBeginCallback([&](TrackId track_id, SliceId slice_id) {
470 track_records.emplace_back(track_id);
471 slice_records.emplace_back(slice_id);
472 });
473
474 EXPECT_TRUE(track_records.empty());
475 EXPECT_TRUE(slice_records.empty());
476
477 tracker.Begin(100, track1, StringId::Raw(11), StringId::Raw(11));
478 SliceId slice1 = context_.storage->slice_table()[0].id();
479 EXPECT_THAT(track_records, ElementsAre(TrackId{1u}));
480 EXPECT_THAT(slice_records, ElementsAre(slice1));
481
482 tracker.Begin(120, track2, StringId::Raw(22), StringId::Raw(22));
483 SliceId slice2 = context_.storage->slice_table()[1].id();
484 EXPECT_THAT(track_records, ElementsAre(TrackId{1u}, TrackId{2u}));
485 EXPECT_THAT(slice_records, ElementsAre(slice1, slice2));
486
487 tracker.Begin(330, track1, StringId::Raw(33), StringId::Raw(33));
488 SliceId slice3 = context_.storage->slice_table()[2].id();
489 EXPECT_THAT(track_records,
490 ElementsAre(TrackId{1u}, TrackId{2u}, TrackId{1u}));
491 EXPECT_THAT(slice_records, ElementsAre(slice1, slice2, slice3));
492 }
493
494 } // namespace
495 } // namespace perfetto::trace_processor
496