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