• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 "src/trace_processor/importers/fuchsia/fuchsia_trace_parser.h"
18 
19 #include "perfetto/base/logging.h"
20 #include "perfetto/ext/base/string_view.h"
21 #include "perfetto/protozero/scattered_heap_buffer.h"
22 #include "perfetto/trace_processor/trace_blob.h"
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/clock_tracker.h"
27 #include "src/trace_processor/importers/common/cpu_tracker.h"
28 #include "src/trace_processor/importers/common/event_tracker.h"
29 #include "src/trace_processor/importers/common/flow_tracker.h"
30 #include "src/trace_processor/importers/common/machine_tracker.h"
31 #include "src/trace_processor/importers/common/metadata_tracker.h"
32 #include "src/trace_processor/importers/common/process_track_translation_table.h"
33 #include "src/trace_processor/importers/common/process_tracker.h"
34 #include "src/trace_processor/importers/common/slice_tracker.h"
35 #include "src/trace_processor/importers/common/stack_profile_tracker.h"
36 #include "src/trace_processor/importers/common/trace_parser.h"
37 #include "src/trace_processor/importers/common/track_tracker.h"
38 #include "src/trace_processor/importers/ftrace/ftrace_sched_event_tracker.h"
39 #include "src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h"
40 #include "src/trace_processor/importers/proto/additional_modules.h"
41 #include "src/trace_processor/importers/proto/default_modules.h"
42 #include "src/trace_processor/importers/proto/proto_trace_parser_impl.h"
43 #include "src/trace_processor/sorter/trace_sorter.h"
44 #include "src/trace_processor/storage/metadata.h"
45 #include "src/trace_processor/storage/trace_storage.h"
46 #include "src/trace_processor/util/descriptors.h"
47 #include "test/gtest_and_gmock.h"
48 
49 #include "protos/perfetto/common/builtin_clock.pbzero.h"
50 #include "protos/perfetto/common/sys_stats_counters.pbzero.h"
51 #include "protos/perfetto/config/trace_config.pbzero.h"
52 #include "protos/perfetto/trace/android/packages_list.pbzero.h"
53 #include "protos/perfetto/trace/chrome/chrome_benchmark_metadata.pbzero.h"
54 #include "protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
55 #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
56 #include "protos/perfetto/trace/ftrace/ftrace.pbzero.h"
57 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
58 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
59 #include "protos/perfetto/trace/ftrace/generic.pbzero.h"
60 #include "protos/perfetto/trace/ftrace/power.pbzero.h"
61 #include "protos/perfetto/trace/ftrace/sched.pbzero.h"
62 #include "protos/perfetto/trace/ftrace/task.pbzero.h"
63 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
64 #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
65 #include "protos/perfetto/trace/ps/process_tree.pbzero.h"
66 #include "protos/perfetto/trace/sys_stats/sys_stats.pbzero.h"
67 #include "protos/perfetto/trace/trace.pbzero.h"
68 #include "protos/perfetto/trace/trace_packet.pbzero.h"
69 #include "protos/perfetto/trace/track_event/chrome_thread_descriptor.pbzero.h"
70 #include "protos/perfetto/trace/track_event/counter_descriptor.pbzero.h"
71 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
72 #include "protos/perfetto/trace/track_event/log_message.pbzero.h"
73 #include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
74 #include "protos/perfetto/trace/track_event/source_location.pbzero.h"
75 #include "protos/perfetto/trace/track_event/task_execution.pbzero.h"
76 #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
77 #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
78 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
79 
80 namespace perfetto {
81 namespace trace_processor {
82 namespace {
83 using ::testing::_;
84 using ::testing::Args;
85 using ::testing::AtLeast;
86 using ::testing::DoAll;
87 using ::testing::ElementsAreArray;
88 using ::testing::Eq;
89 using ::testing::HasSubstr;
90 using ::testing::IgnoreResult;
91 using ::testing::InSequence;
92 using ::testing::Invoke;
93 using ::testing::InvokeArgument;
94 using ::testing::NiceMock;
95 using ::testing::Pointwise;
96 using ::testing::Return;
97 using ::testing::ReturnRef;
98 using ::testing::UnorderedElementsAreArray;
99 class MockSchedEventTracker : public FtraceSchedEventTracker {
100  public:
MockSchedEventTracker(TraceProcessorContext * context)101   explicit MockSchedEventTracker(TraceProcessorContext* context)
102       : FtraceSchedEventTracker(context) {}
103 
104   MOCK_METHOD(void,
105               PushSchedSwitch,
106               (uint32_t cpu,
107                int64_t timestamp,
108                uint32_t prev_pid,
109                base::StringView prev_comm,
110                int32_t prev_prio,
111                int64_t prev_state,
112                uint32_t next_pid,
113                base::StringView next_comm,
114                int32_t next_prio),
115               (override));
116 };
117 
118 class MockProcessTracker : public ProcessTracker {
119  public:
MockProcessTracker(TraceProcessorContext * context)120   explicit MockProcessTracker(TraceProcessorContext* context)
121       : ProcessTracker(context) {}
122 
123   MOCK_METHOD(UniquePid,
124               SetProcessMetadata,
125               (uint32_t pid,
126                std::optional<uint32_t> ppid,
127                base::StringView process_name,
128                base::StringView cmdline),
129               (override));
130 
131   MOCK_METHOD(UniqueTid,
132               UpdateThreadName,
133               (uint32_t tid,
134                StringId thread_name_id,
135                ThreadNamePriority priority),
136               (override));
137   MOCK_METHOD(void,
138               UpdateThreadNameByUtid,
139               (UniqueTid utid,
140                StringId thread_name_id,
141                ThreadNamePriority priority),
142               (override));
143   MOCK_METHOD(UniqueTid,
144               UpdateThread,
145               (uint32_t tid, uint32_t tgid),
146               (override));
147 
148   MOCK_METHOD(UniquePid, GetOrCreateProcess, (uint32_t pid), (override));
149   MOCK_METHOD(void,
150               SetProcessNameIfUnset,
151               (UniquePid upid, StringId process_name_id),
152               (override));
153 };
154 class MockBoundInserter : public ArgsTracker::BoundInserter {
155  public:
MockBoundInserter()156   MockBoundInserter()
157       : ArgsTracker::BoundInserter(&tracker_, nullptr, 0u), tracker_(nullptr) {
158     ON_CALL(*this, AddArg(_, _, _, _)).WillByDefault(ReturnRef(*this));
159   }
160 
161   MOCK_METHOD(ArgsTracker::BoundInserter&,
162               AddArg,
163               (StringId flat_key,
164                StringId key,
165                Variadic v,
166                ArgsTracker::UpdatePolicy update_policy),
167               (override));
168 
169  private:
170   ArgsTracker tracker_;
171 };
172 
173 class MockEventTracker : public EventTracker {
174  public:
MockEventTracker(TraceProcessorContext * context)175   explicit MockEventTracker(TraceProcessorContext* context)
176       : EventTracker(context) {}
177   ~MockEventTracker() override = default;
178 
179   MOCK_METHOD(void,
180               PushSchedSwitch,
181               (uint32_t cpu,
182                int64_t timestamp,
183                uint32_t prev_pid,
184                base::StringView prev_comm,
185                int32_t prev_prio,
186                int64_t prev_state,
187                uint32_t next_pid,
188                base::StringView next_comm,
189                int32_t next_prio));
190 
191   MOCK_METHOD(std::optional<CounterId>,
192               PushCounter,
193               (int64_t timestamp, double value, TrackId track_id),
194               (override));
195 };
196 
197 class MockSliceTracker : public SliceTracker {
198  public:
MockSliceTracker(TraceProcessorContext * context)199   explicit MockSliceTracker(TraceProcessorContext* context)
200       : SliceTracker(context) {}
201 
202   MOCK_METHOD(std::optional<SliceId>,
203               Begin,
204               (int64_t timestamp,
205                TrackId track_id,
206                StringId cat,
207                StringId name,
208                SetArgsCallback args_callback),
209               (override));
210   MOCK_METHOD(std::optional<SliceId>,
211               End,
212               (int64_t timestamp,
213                TrackId track_id,
214                StringId cat,
215                StringId name,
216                SetArgsCallback args_callback),
217               (override));
218   MOCK_METHOD(std::optional<SliceId>,
219               Scoped,
220               (int64_t timestamp,
221                TrackId track_id,
222                StringId cat,
223                StringId name,
224                int64_t duration,
225                SetArgsCallback args_callback),
226               (override));
227   MOCK_METHOD(std::optional<SliceId>,
228               StartSlice,
229               (int64_t timestamp,
230                TrackId track_id,
231                SetArgsCallback args_callback,
232                std::function<SliceId()> inserter),
233               (override));
234 };
235 
236 class FuchsiaTraceParserTest : public ::testing::Test {
237  public:
FuchsiaTraceParserTest()238   FuchsiaTraceParserTest() {
239     context_.storage.reset(new TraceStorage());
240     storage_ = context_.storage.get();
241     context_.track_tracker.reset(new TrackTracker(&context_));
242     context_.global_args_tracker.reset(
243         new GlobalArgsTracker(context_.storage.get()));
244     context_.stack_profile_tracker.reset(new StackProfileTracker(&context_));
245     context_.args_tracker.reset(new ArgsTracker(&context_));
246     context_.args_translation_table.reset(new ArgsTranslationTable(storage_));
247     context_.metadata_tracker.reset(
248         new MetadataTracker(context_.storage.get()));
249     context_.machine_tracker.reset(new MachineTracker(&context_, 0));
250     context_.cpu_tracker.reset(new CpuTracker(&context_));
251     event_ = new MockEventTracker(&context_);
252     context_.event_tracker.reset(event_);
253     sched_ = new MockSchedEventTracker(&context_);
254     context_.ftrace_sched_tracker.reset(sched_);
255     process_ = new NiceMock<MockProcessTracker>(&context_);
256     context_.process_tracker.reset(process_);
257     context_.process_track_translation_table.reset(
258         new ProcessTrackTranslationTable(storage_));
259     slice_ = new NiceMock<MockSliceTracker>(&context_);
260     context_.slice_tracker.reset(slice_);
261     context_.slice_translation_table.reset(new SliceTranslationTable(storage_));
262     context_.clock_tracker.reset(new ClockTracker(&context_));
263     clock_ = context_.clock_tracker.get();
264     context_.flow_tracker.reset(new FlowTracker(&context_));
265     context_.fuchsia_record_parser.reset(new FuchsiaTraceParser(&context_));
266     context_.proto_trace_parser.reset(new ProtoTraceParserImpl(&context_));
267     context_.sorter.reset(
268         new TraceSorter(&context_, TraceSorter::SortingMode::kFullSort));
269     context_.descriptor_pool_.reset(new DescriptorPool());
270 
271     RegisterDefaultModules(&context_);
272     RegisterAdditionalModules(&context_);
273   }
274 
push_word(uint64_t word)275   void push_word(uint64_t word) { trace_bytes_.push_back(word); }
276 
ResetTraceBuffers()277   void ResetTraceBuffers() {
278     trace_bytes_.clear();
279     // Write the FXT Magic Bytes
280     push_word(0x0016547846040010);
281   }
282 
SetUp()283   void SetUp() override { ResetTraceBuffers(); }
284 
Tokenize()285   util::Status Tokenize() {
286     const size_t num_bytes = trace_bytes_.size() * sizeof(uint64_t);
287     std::unique_ptr<uint8_t[]> raw_trace(new uint8_t[num_bytes]);
288     memcpy(raw_trace.get(), trace_bytes_.data(), num_bytes);
289     context_.chunk_reader.reset(new FuchsiaTraceTokenizer(&context_));
290     auto status = context_.chunk_reader->Parse(TraceBlobView(
291         TraceBlob::TakeOwnership(std::move(raw_trace), num_bytes)));
292 
293     ResetTraceBuffers();
294     return status;
295   }
296 
297  protected:
298   std::vector<uint64_t> trace_bytes_;
299 
300   TraceProcessorContext context_;
301   MockEventTracker* event_;
302   MockSchedEventTracker* sched_;
303   MockProcessTracker* process_;
304   MockSliceTracker* slice_;
305   ClockTracker* clock_;
306   TraceStorage* storage_;
307 };
308 
TEST_F(FuchsiaTraceParserTest,CorruptedFxt)309 TEST_F(FuchsiaTraceParserTest, CorruptedFxt) {
310   // Invalid record of size 0
311   push_word(0x0016547846040000);
312   EXPECT_FALSE(Tokenize().ok());
313 }
314 
TEST_F(FuchsiaTraceParserTest,InlineInstantEvent)315 TEST_F(FuchsiaTraceParserTest, InlineInstantEvent) {
316   // Inline name of 8 bytes
317   uint64_t name_ref = uint64_t{0x8008} << 48;
318   // Inline category of 8 bytes
319   uint64_t category_ref = uint64_t{0x8008} << 32;
320   // Inline threadref
321   uint64_t threadref = uint64_t{0};
322   // Instant Event
323   uint64_t event_type = 0 << 16;
324   uint64_t size = 6 << 4;
325   uint64_t record_type = 4;
326 
327   auto header =
328       name_ref | category_ref | threadref | event_type | size | record_type;
329   push_word(header);
330   // Timestamp
331   push_word(0xAAAAAAAAAAAAAAAA);
332   // Pid + tid
333   push_word(0xBBBBBBBBBBBBBBBB);
334   push_word(0xCCCCCCCCCCCCCCCC);
335   // Inline Category
336   push_word(0xDDDDDDDDDDDDDDDD);
337   // Inline Name
338   push_word(0xEEEEEEEEEEEEEEEE);
339   EXPECT_TRUE(Tokenize().ok());
340   EXPECT_EQ(context_.storage->stats()[stats::fuchsia_invalid_event].value, 0);
341 }
342 
TEST_F(FuchsiaTraceParserTest,BooleanArguments)343 TEST_F(FuchsiaTraceParserTest, BooleanArguments) {
344   // Inline name of 8 bytes
345   uint64_t name_ref = uint64_t{0x8008} << 48;
346   // Inline category of 8 bytes
347   uint64_t category_ref = uint64_t{0x8008} << 32;
348   // Inline threadref
349   uint64_t threadref = uint64_t{0};
350   // 2 arguments
351   uint64_t argument_count = uint64_t{2} << 20;
352   // Instant Event
353   uint64_t event_type = 0 << 16;
354   uint64_t size = 8 << 4;
355   uint64_t record_type = 4;
356 
357   auto header = name_ref | category_ref | threadref | event_type |
358                 argument_count | size | record_type;
359   push_word(header);
360   // Timestamp
361   push_word(0xAAAAAAAAAAAAAAAA);
362   // Pid + tid
363   push_word(0xBBBBBBBBBBBBBBBB);
364   push_word(0xCCCCCCCCCCCCCCCC);
365   // Inline Category
366   push_word(0xDDDDDDDDDDDDDDDD);
367   // Inline Name
368   push_word(0xEEEEEEEEEEEEEEEE);
369   // Boolean argument true
370   push_word(0x0000'0001'8008'0029);
371   // 8 byte arg name stream
372   push_word(0x0000'0000'0000'0000);
373   // Boolean argument false
374   push_word(0x0000'0000'8008'002A);
375   // 8 byte arg name stream
376   push_word(0x0000'0000'0000'0000);
377   EXPECT_TRUE(Tokenize().ok());
378   EXPECT_EQ(context_.storage->stats()[stats::fuchsia_invalid_event].value, 0);
379 }
380 
TEST_F(FuchsiaTraceParserTest,FxtWithProtos)381 TEST_F(FuchsiaTraceParserTest, FxtWithProtos) {
382   // Serialize some protos to bytes
383   protozero::HeapBuffered<protos::pbzero::Trace> protos;
384   {
385     auto* packet = protos->add_packet();
386     packet->set_trusted_packet_sequence_id(1);
387     packet->set_incremental_state_cleared(true);
388     auto* thread_desc = packet->set_thread_descriptor();
389     thread_desc->set_pid(15);
390     thread_desc->set_tid(16);
391     thread_desc->set_reference_timestamp_us(1000);
392     thread_desc->set_reference_thread_time_us(2000);
393   }
394   {
395     auto* packet = protos->add_packet();
396     packet->set_trusted_packet_sequence_id(1);
397     auto* event = packet->set_track_event();
398     event->set_timestamp_delta_us(10);   // absolute: 1010.
399     event->set_thread_time_delta_us(5);  // absolute: 2005.
400     event->add_category_iids(1);
401     auto* legacy_event = event->set_legacy_event();
402     legacy_event->set_name_iid(1);
403     legacy_event->set_phase('B');
404   }
405   {
406     auto* packet = protos->add_packet();
407     packet->set_trusted_packet_sequence_id(1);
408     auto* event = packet->set_track_event();
409     event->set_timestamp_delta_us(10);   // absolute: 1020.
410     event->set_thread_time_delta_us(5);  // absolute: 2010.
411     event->add_category_iids(1);
412     auto* legacy_event = event->set_legacy_event();
413     legacy_event->set_name_iid(1);
414     legacy_event->set_phase('E');
415   }
416   {
417     auto* packet = protos->add_packet();
418     packet->set_trusted_packet_sequence_id(1);
419     auto* event = packet->set_track_event();
420     event->set_timestamp_absolute_us(1005);
421     event->set_thread_time_absolute_us(2003);
422     event->add_category_iids(2);
423     event->add_category_iids(3);
424     auto* legacy_event = event->set_legacy_event();
425     legacy_event->set_name_iid(2);
426     legacy_event->set_phase('X');
427     legacy_event->set_duration_us(23);         // absolute end: 1028.
428     legacy_event->set_thread_duration_us(12);  // absolute end: 2015.
429   }
430 
431   protos->Finalize();
432   std::vector<uint8_t> perfetto_bytes = protos.SerializeAsArray();
433 
434   // Set up an FXT Perfetto Blob Header
435   uint64_t blob_type_perfetto = uint64_t{3} << 48;
436   uint64_t unpadded_blob_size_bytes = uint64_t{perfetto_bytes.size()} << 32;
437   uint64_t blob_name_ref = uint64_t{0x8008} << 16;
438   uint64_t size_words = ((perfetto_bytes.size() + 7) / 8 + 2) << 4;
439   uint64_t record_type = 5;
440 
441   uint64_t header = blob_type_perfetto | unpadded_blob_size_bytes |
442                     blob_name_ref | size_words | record_type;
443 
444   // Pad the blob to a multiple of 8 bytes.
445   while (perfetto_bytes.size() % 8) {
446     perfetto_bytes.push_back(0);
447   }
448 
449   push_word(header);
450   // Inline Name Ref
451   push_word(0xBBBBBBBBBBBBBBBB);
452   trace_bytes_.insert(trace_bytes_.end(),
453                       reinterpret_cast<uint64_t*>(perfetto_bytes.data()),
454                       reinterpret_cast<uint64_t*>(perfetto_bytes.data() +
455                                                   perfetto_bytes.size()));
456   EXPECT_CALL(*process_, UpdateThread(16, 15)).WillRepeatedly(Return(1u));
457 
458   tables::ThreadTable::Row row(16);
459   row.upid = 1u;
460   storage_->mutable_thread_table()->Insert(row);
461 
462   MockBoundInserter inserter;
463 
464   StringId unknown_cat = storage_->InternString("unknown(1)");
465   ASSERT_NE(storage_, nullptr);
466 
467   constexpr TrackId track{0u};
468   constexpr TrackId thread_time_track{1u};
469 
470   InSequence in_sequence;  // Below slices should be sorted by timestamp.
471   // Only the begin thread time can be imported into the counter table.
472   EXPECT_CALL(*event_, PushCounter(1005000, testing::DoubleEq(2003000),
473                                    thread_time_track));
474   EXPECT_CALL(*slice_, StartSlice(1005000, track, _, _))
475       .WillOnce(DoAll(IgnoreResult(InvokeArgument<3>()),
476                       InvokeArgument<2>(&inserter), Return(SliceId(0u))));
477   EXPECT_CALL(*event_, PushCounter(1010000, testing::DoubleEq(2005000),
478                                    thread_time_track));
479   EXPECT_CALL(*slice_, StartSlice(1010000, track, _, _))
480       .WillOnce(DoAll(IgnoreResult(InvokeArgument<3>()),
481                       InvokeArgument<2>(&inserter), Return(SliceId(1u))));
482   EXPECT_CALL(*event_, PushCounter(1020000, testing::DoubleEq(2010000),
483                                    thread_time_track));
484   EXPECT_CALL(*slice_, End(1020000, track, unknown_cat, kNullStringId, _))
485       .WillOnce(DoAll(InvokeArgument<4>(&inserter), Return(SliceId(1u))));
486 
487   auto status = Tokenize();
488   EXPECT_TRUE(status.ok());
489   context_.sorter->ExtractEventsForced();
490 }
491 
TEST_F(FuchsiaTraceParserTest,SchedulerEvents)492 TEST_F(FuchsiaTraceParserTest, SchedulerEvents) {
493   uint64_t thread1_tid = 0x1AAA'AAAA'AAAA'AAAA;
494   uint64_t thread2_tid = 0x2CCC'CCCC'CCCC'CCCC;
495 
496   // We'll emit a wake up for thread 1, a switch to thread 2, and a switch back
497   // to thread 1 and expect to see that the process tracker was properly updated
498 
499   uint64_t wakeup_record_type = uint64_t{2} << 60;
500   uint64_t context_switch_record_type = uint64_t{1} << 60;
501   uint64_t cpu = 1 << 20;
502   uint64_t record_type = 8;
503 
504   uint64_t wakeup_size = uint64_t{3} << 4;
505   uint64_t context_switch_size = uint64_t{4} << 4;
506 
507   uint64_t wakeup_header = wakeup_record_type | cpu | record_type | wakeup_size;
508   push_word(wakeup_header);
509   // Timestamp
510   push_word(0x1);
511   // wakeup tid
512   push_word(thread1_tid);
513 
514   uint64_t context_switch_header =
515       context_switch_record_type | cpu | record_type | context_switch_size;
516   push_word(context_switch_header);
517   // Timestamp
518   push_word(0x2);
519   // outgoing tid
520   push_word(thread1_tid);
521   // incoming tid
522   push_word(thread2_tid);
523 
524   push_word(context_switch_header);
525   // Timestamp
526   push_word(0x3);
527   // outgoing tid
528   push_word(thread2_tid);
529   // incoming tid
530   push_word(thread1_tid);
531 
532   // We should get:
533   // - A thread1 update call on wake up
534   // - thread1 & thread2 update calls on the first context switch
535   // - thread2 & thread1 update cals on the second context switch
536   EXPECT_CALL(*process_, UpdateThread(static_cast<uint32_t>(thread1_tid), _))
537       .Times(3);
538   EXPECT_CALL(*process_, UpdateThread(static_cast<uint32_t>(thread2_tid), _))
539       .Times(2);
540 
541   EXPECT_TRUE(Tokenize().ok());
542   EXPECT_EQ(context_.storage->stats()[stats::fuchsia_invalid_event].value, 0);
543 
544   context_.sorter->ExtractEventsForced();
545 }
546 
TEST_F(FuchsiaTraceParserTest,LegacySchedulerEvents)547 TEST_F(FuchsiaTraceParserTest, LegacySchedulerEvents) {
548   uint64_t thread1_pid = 0x1AAA'AAAA'AAAA'AAAA;
549   uint64_t thread1_tid = 0x1BBB'BBBB'BBBB'BBBB;
550   uint64_t thread2_pid = 0x2CCC'CCCC'CCCC'CCCC;
551   uint64_t thread2_tid = 0x2DDD'DDDD'DDDD'DDDD;
552 
553   // We'll emit a wake up for thread 1, a switch to thread 2, and a switch back
554   // to thread 1 and expect to see that the process tracker was properly updated
555 
556   uint64_t context_switch_size = uint64_t{6} << 4;
557   uint64_t cpu = 1 << 16;
558   uint64_t record_type = 8;
559   uint64_t outoing_state = 2 << 24;
560   uint64_t outoing_thread = 0;   // Inline thread-ref
561   uint64_t incoming_thread = 0;  // Inline thread-ref
562   uint64_t outgoing_prio = uint64_t{1} << 44;
563   uint64_t incoming_prio = uint64_t{1} << 52;
564   uint64_t outgoing_idle_prio = uint64_t{0} << 44;
565 
566   uint64_t context_switch_header =
567       record_type | context_switch_size | cpu | outoing_state | outoing_thread |
568       incoming_thread | outgoing_prio | incoming_prio;
569   uint64_t wakeup_header = record_type | context_switch_size | cpu |
570                            outoing_state | outoing_thread | incoming_thread |
571                            outgoing_idle_prio | incoming_prio;
572 
573   push_word(wakeup_header);
574   // Timestamp
575   push_word(0x1);
576   // outgoing pid+tid
577   push_word(0);  // Idle thread
578   push_word(0);  // Idle thread
579   // incoming pid+tid
580   push_word(thread1_pid);
581   push_word(thread1_tid);
582 
583   push_word(context_switch_header);
584   // Timestamp
585   push_word(0x2);
586   // outgoing pid+tid
587   push_word(thread1_pid);
588   push_word(thread1_tid);
589   // incoming pid+tid
590   push_word(thread2_pid);
591   push_word(thread2_tid);
592 
593   push_word(context_switch_header);
594   // Timestamp
595   push_word(0x3);
596   // outgoing pid+tid
597   push_word(thread2_pid);
598   push_word(thread2_tid);
599   // incoming pid+tid
600   push_word(thread1_pid);
601   push_word(thread1_tid);
602 
603   // We should get:
604   // - A thread1 update call on wake up
605   // - thread1 & thread2 update calls on the first context switch
606   // - thread2 & thread1 update cals on the second context switch
607   EXPECT_CALL(*process_, UpdateThread(static_cast<uint32_t>(thread1_tid), _))
608       .Times(3);
609   EXPECT_CALL(*process_, UpdateThread(static_cast<uint32_t>(thread2_tid), _))
610       .Times(2);
611 
612   EXPECT_TRUE(Tokenize().ok());
613   EXPECT_EQ(context_.storage->stats()[stats::fuchsia_invalid_event].value, 0);
614 
615   context_.sorter->ExtractEventsForced();
616 }
617 
618 }  // namespace
619 }  // namespace trace_processor
620 }  // namespace perfetto
621