• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 #include <cstddef>
19 #include <cstdint>
20 #include <functional>
21 #include <limits>
22 #include <optional>
23 #include <string>
24 #include <utility>
25 #include <vector>
26 
27 #include "perfetto/base/logging.h"
28 #include "perfetto/ext/base/string_view.h"
29 #include "src/trace_processor/importers/common/args_tracker.h"
30 #include "src/trace_processor/importers/common/cpu_tracker.h"
31 #include "src/trace_processor/importers/common/event_tracker.h"
32 #include "src/trace_processor/importers/common/flow_tracker.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/track_tracker.h"
36 #include "src/trace_processor/importers/common/tracks.h"
37 #include "src/trace_processor/importers/common/tracks_common.h"
38 #include "src/trace_processor/importers/fuchsia/fuchsia_record.h"
39 #include "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h"
40 #include "src/trace_processor/storage/stats.h"
41 #include "src/trace_processor/storage/trace_storage.h"
42 #include "src/trace_processor/tables/sched_tables_py.h"
43 
44 namespace perfetto::trace_processor {
45 
46 namespace {
47 
48 // Record Types
49 constexpr uint32_t kEvent = 4;
50 constexpr uint32_t kSchedulerEvent = 8;
51 
52 constexpr uint32_t kSchedulerEventLegacyContextSwitch = 0;
53 constexpr uint32_t kSchedulerEventContextSwitch = 1;
54 constexpr uint32_t kSchedulerEventThreadWakeup = 2;
55 
56 // Event Types
57 constexpr uint32_t kInstant = 0;
58 constexpr uint32_t kCounter = 1;
59 constexpr uint32_t kDurationBegin = 2;
60 constexpr uint32_t kDurationEnd = 3;
61 constexpr uint32_t kDurationComplete = 4;
62 constexpr uint32_t kAsyncBegin = 5;
63 constexpr uint32_t kAsyncInstant = 6;
64 constexpr uint32_t kAsyncEnd = 7;
65 constexpr uint32_t kFlowBegin = 8;
66 constexpr uint32_t kFlowStep = 9;
67 constexpr uint32_t kFlowEnd = 10;
68 
69 // Argument Types
70 constexpr uint32_t kNull = 0;
71 constexpr uint32_t kInt32 = 1;
72 constexpr uint32_t kUint32 = 2;
73 constexpr uint32_t kInt64 = 3;
74 constexpr uint32_t kUint64 = 4;
75 constexpr uint32_t kDouble = 5;
76 constexpr uint32_t kString = 6;
77 constexpr uint32_t kPointer = 7;
78 constexpr uint32_t kKoid = 8;
79 constexpr uint32_t kBool = 9;
80 
81 // Thread states
82 constexpr uint32_t kThreadNew = 0;
83 constexpr uint32_t kThreadRunning = 1;
84 constexpr uint32_t kThreadSuspended = 2;
85 constexpr uint32_t kThreadBlocked = 3;
86 constexpr uint32_t kThreadDying = 4;
87 constexpr uint32_t kThreadDead = 5;
88 
89 constexpr int32_t kIdleWeight = std::numeric_limits<int32_t>::min();
90 
91 constexpr auto kCounterBlueprint = tracks::CounterBlueprint(
92     "fuchsia_counter",
93     tracks::UnknownUnitBlueprint(),
94     tracks::DimensionBlueprints(tracks::kProcessDimensionBlueprint,
95                                 tracks::kNameFromTraceDimensionBlueprint),
96     tracks::DynamicNameBlueprint());
97 
98 }  // namespace
99 
FuchsiaTraceParser(TraceProcessorContext * context)100 FuchsiaTraceParser::FuchsiaTraceParser(TraceProcessorContext* context)
101     : context_(context),
102       weight_id_(context->storage->InternString("weight")),
103       incoming_weight_id_(context->storage->InternString("incoming_weight")),
104       outgoing_weight_id_(context->storage->InternString("outgoing_weight")),
105       running_string_id_(context->storage->InternString("Running")),
106       runnable_string_id_(context->storage->InternString("R")),
107       waking_string_id_(context->storage->InternString("W")),
108       blocked_string_id_(context->storage->InternString("S")),
109       suspended_string_id_(context->storage->InternString("T")),
110       exit_dying_string_id_(context->storage->InternString("Z")),
111       exit_dead_string_id_(context->storage->InternString("X")) {}
112 
113 FuchsiaTraceParser::~FuchsiaTraceParser() = default;
114 
115 std::optional<std::vector<FuchsiaTraceParser::Arg>>
ParseArgs(fuchsia_trace_utils::RecordCursor & cursor,uint32_t n_args,std::function<StringId (base::StringView string)> intern_string,std::function<StringId (uint32_t index)> get_string)116 FuchsiaTraceParser::ParseArgs(
117     fuchsia_trace_utils::RecordCursor& cursor,
118     uint32_t n_args,
119     std::function<StringId(base::StringView string)> intern_string,
120     std::function<StringId(uint32_t index)> get_string) {
121   std::vector<Arg> args;
122   for (uint32_t i = 0; i < n_args; i++) {
123     size_t arg_base = cursor.WordIndex();
124     uint64_t arg_header;
125     if (!cursor.ReadUint64(&arg_header)) {
126       return std::nullopt;
127     }
128     uint32_t arg_type =
129         fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 0, 3);
130     uint32_t arg_size_words =
131         fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 4, 15);
132     uint32_t arg_name_ref =
133         fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 16, 31);
134     Arg arg;
135     if (fuchsia_trace_utils::IsInlineString(arg_name_ref)) {
136       base::StringView arg_name_view;
137       if (!cursor.ReadInlineString(arg_name_ref, &arg_name_view)) {
138         return std::nullopt;
139       }
140       arg.name = intern_string(arg_name_view);
141     } else {
142       arg.name = get_string(arg_name_ref);
143     }
144 
145     switch (arg_type) {
146       case kNull:
147         arg.value = fuchsia_trace_utils::ArgValue::Null();
148         break;
149       case kInt32:
150         arg.value = fuchsia_trace_utils::ArgValue::Int32(
151             fuchsia_trace_utils::ReadField<int32_t>(arg_header, 32, 63));
152         break;
153       case kUint32:
154         arg.value = fuchsia_trace_utils::ArgValue::Uint32(
155             fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 32, 63));
156         break;
157       case kInt64: {
158         int64_t value;
159         if (!cursor.ReadInt64(&value)) {
160           return std::nullopt;
161         }
162         arg.value = fuchsia_trace_utils::ArgValue::Int64(value);
163         break;
164       }
165       case kUint64: {
166         uint64_t value;
167         if (!cursor.ReadUint64(&value)) {
168           return std::nullopt;
169         }
170         arg.value = fuchsia_trace_utils::ArgValue::Uint64(value);
171         break;
172       }
173       case kDouble: {
174         double value;
175         if (!cursor.ReadDouble(&value)) {
176           return std::nullopt;
177         }
178         arg.value = fuchsia_trace_utils::ArgValue::Double(value);
179         break;
180       }
181       case kString: {
182         uint32_t arg_value_ref =
183             fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 32, 47);
184         StringId value;
185         if (fuchsia_trace_utils::IsInlineString(arg_value_ref)) {
186           base::StringView arg_value_view;
187           if (!cursor.ReadInlineString(arg_value_ref, &arg_value_view)) {
188             return std::nullopt;
189           }
190           value = intern_string(arg_value_view);
191         } else {
192           value = get_string(arg_value_ref);
193         }
194         arg.value = fuchsia_trace_utils::ArgValue::String(value);
195         break;
196       }
197       case kPointer: {
198         uint64_t value;
199         if (!cursor.ReadUint64(&value)) {
200           return std::nullopt;
201         }
202         arg.value = fuchsia_trace_utils::ArgValue::Pointer(value);
203         break;
204       }
205       case kKoid: {
206         uint64_t value;
207         if (!cursor.ReadUint64(&value)) {
208           return std::nullopt;
209         }
210         arg.value = fuchsia_trace_utils::ArgValue::Koid(value);
211         break;
212       }
213       case kBool: {
214         arg.value = fuchsia_trace_utils::ArgValue::Bool(
215             fuchsia_trace_utils::ReadField<bool>(arg_header, 32, 63));
216         break;
217       }
218       default:
219         arg.value = fuchsia_trace_utils::ArgValue::Unknown();
220         break;
221     }
222 
223     args.push_back(arg);
224     cursor.SetWordIndex(arg_base + arg_size_words);
225   }
226 
227   return {std::move(args)};
228 }
229 
ParseFuchsiaRecord(int64_t,FuchsiaRecord fr)230 void FuchsiaTraceParser::ParseFuchsiaRecord(int64_t, FuchsiaRecord fr) {
231   // The timestamp is also present in the record, so we'll ignore the one
232   // passed as an argument.
233   fuchsia_trace_utils::RecordCursor cursor(fr.record_view()->data(),
234                                            fr.record_view()->length());
235   ProcessTracker* procs = context_->process_tracker.get();
236   SliceTracker* slices = context_->slice_tracker.get();
237 
238   // Read arguments
239   const auto intern_string = [this](base::StringView string) {
240     return context_->storage->InternString(string);
241   };
242   const auto get_string = [&fr](uint32_t index) { return fr.GetString(index); };
243 
244   uint64_t header;
245   if (!cursor.ReadUint64(&header)) {
246     context_->storage->IncrementStats(stats::fuchsia_record_read_error);
247     return;
248   }
249   auto record_type = fuchsia_trace_utils::ReadField<uint32_t>(header, 0, 3);
250   switch (record_type) {
251     case kEvent: {
252       auto event_type =
253           fuchsia_trace_utils::ReadField<uint32_t>(header, 16, 19);
254       auto n_args = fuchsia_trace_utils::ReadField<uint32_t>(header, 20, 23);
255       auto thread_ref =
256           fuchsia_trace_utils::ReadField<uint32_t>(header, 24, 31);
257       auto cat_ref = fuchsia_trace_utils::ReadField<uint32_t>(header, 32, 47);
258       auto name_ref = fuchsia_trace_utils::ReadField<uint32_t>(header, 48, 63);
259 
260       int64_t ts;
261       if (!cursor.ReadTimestamp(fr.get_ticks_per_second(), &ts)) {
262         context_->storage->IncrementStats(stats::fuchsia_record_read_error);
263         return;
264       }
265       FuchsiaThreadInfo tinfo;
266       if (fuchsia_trace_utils::IsInlineThread(thread_ref)) {
267         if (!cursor.ReadInlineThread(&tinfo)) {
268           context_->storage->IncrementStats(stats::fuchsia_record_read_error);
269           return;
270         }
271       } else {
272         tinfo = fr.GetThread(thread_ref);
273       }
274       StringId cat;
275       if (fuchsia_trace_utils::IsInlineString(cat_ref)) {
276         base::StringView cat_string_view;
277         if (!cursor.ReadInlineString(cat_ref, &cat_string_view)) {
278           context_->storage->IncrementStats(stats::fuchsia_record_read_error);
279           return;
280         }
281         cat = context_->storage->InternString(cat_string_view);
282       } else {
283         cat = fr.GetString(cat_ref);
284       }
285       StringId name;
286       if (fuchsia_trace_utils::IsInlineString(name_ref)) {
287         base::StringView name_string_view;
288         if (!cursor.ReadInlineString(name_ref, &name_string_view)) {
289           context_->storage->IncrementStats(stats::fuchsia_record_read_error);
290           return;
291         }
292         name = context_->storage->InternString(name_string_view);
293       } else {
294         name = fr.GetString(name_ref);
295       }
296 
297       auto maybe_args = FuchsiaTraceParser::ParseArgs(
298           cursor, n_args, intern_string, get_string);
299       if (!maybe_args.has_value()) {
300         context_->storage->IncrementStats(stats::fuchsia_record_read_error);
301         return;
302       }
303 
304       auto insert_args =
305           [this, args = *maybe_args](ArgsTracker::BoundInserter* inserter) {
306             for (const Arg& arg : args) {
307               inserter->AddArg(
308                   arg.name, arg.name,
309                   arg.value.ToStorageVariadic(context_->storage.get()));
310             }
311           };
312 
313       switch (event_type) {
314         case kInstant: {
315           UniqueTid utid =
316               procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
317                                   static_cast<uint32_t>(tinfo.pid));
318           TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
319           slices->Scoped(ts, track_id, cat, name, 0, std::move(insert_args));
320           break;
321         }
322         case kCounter: {
323           UniquePid upid =
324               procs->GetOrCreateProcess(static_cast<uint32_t>(tinfo.pid));
325           std::string name_str =
326               context_->storage->GetString(name).ToStdString();
327           uint64_t counter_id;
328           if (!cursor.ReadUint64(&counter_id)) {
329             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
330             return;
331           }
332           // Note: In the Fuchsia trace format, counter values are stored
333           // in the arguments for the record, with the data series defined
334           // by both the record name and the argument name. In Perfetto,
335           // counters only have one name, so we combine both names into
336           // one here.
337           for (const Arg& arg : *maybe_args) {
338             std::string counter_name_str = name_str + ":";
339             counter_name_str +=
340                 context_->storage->GetString(arg.name).ToStdString();
341             counter_name_str += ":" + std::to_string(counter_id);
342             bool is_valid_value = false;
343             double counter_value = -1;
344             switch (arg.value.Type()) {
345               case fuchsia_trace_utils::ArgValue::kInt32:
346                 is_valid_value = true;
347                 counter_value = static_cast<double>(arg.value.Int32());
348                 break;
349               case fuchsia_trace_utils::ArgValue::kUint32:
350                 is_valid_value = true;
351                 counter_value = static_cast<double>(arg.value.Uint32());
352                 break;
353               case fuchsia_trace_utils::ArgValue::kInt64:
354                 is_valid_value = true;
355                 counter_value = static_cast<double>(arg.value.Int64());
356                 break;
357               case fuchsia_trace_utils::ArgValue::kUint64:
358                 is_valid_value = true;
359                 counter_value = static_cast<double>(arg.value.Uint64());
360                 break;
361               case fuchsia_trace_utils::ArgValue::kDouble:
362                 is_valid_value = true;
363                 counter_value = arg.value.Double();
364                 break;
365               case fuchsia_trace_utils::ArgValue::kNull:
366               case fuchsia_trace_utils::ArgValue::kString:
367               case fuchsia_trace_utils::ArgValue::kPointer:
368               case fuchsia_trace_utils::ArgValue::kKoid:
369               case fuchsia_trace_utils::ArgValue::kBool:
370               case fuchsia_trace_utils::ArgValue::kUnknown:
371                 context_->storage->IncrementStats(
372                     stats::fuchsia_non_numeric_counters);
373                 break;
374             }
375             if (is_valid_value) {
376               base::StringView counter_name_str_view(counter_name_str);
377               StringId counter_name_id =
378                   context_->storage->InternString(counter_name_str_view);
379               TrackId track = context_->track_tracker->InternTrack(
380                   kCounterBlueprint,
381                   tracks::Dimensions(upid, counter_name_str_view),
382                   tracks::DynamicName(counter_name_id));
383               context_->event_tracker->PushCounter(ts, counter_value, track);
384             }
385           }
386           break;
387         }
388         case kDurationBegin: {
389           UniqueTid utid =
390               procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
391                                   static_cast<uint32_t>(tinfo.pid));
392           TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
393           slices->Begin(ts, track_id, cat, name, std::move(insert_args));
394           break;
395         }
396         case kDurationEnd: {
397           UniqueTid utid =
398               procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
399                                   static_cast<uint32_t>(tinfo.pid));
400           TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
401           // TODO(b/131181693): |cat| and |name| are not passed here so
402           // that if two slices end at the same timestep, the slices get
403           // closed in the correct order regardless of which end event is
404           // processed first.
405           slices->End(ts, track_id, {}, {}, std::move(insert_args));
406           break;
407         }
408         case kDurationComplete: {
409           int64_t end_ts;
410           if (!cursor.ReadTimestamp(fr.get_ticks_per_second(), &end_ts)) {
411             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
412             return;
413           }
414           int64_t duration = end_ts - ts;
415           if (duration < 0) {
416             context_->storage->IncrementStats(stats::fuchsia_timestamp_overflow);
417             return;
418           }
419           UniqueTid utid =
420               procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
421                                   static_cast<uint32_t>(tinfo.pid));
422           TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
423           slices->Scoped(ts, track_id, cat, name, duration,
424                          std::move(insert_args));
425           break;
426         }
427         case kAsyncBegin: {
428           int64_t correlation_id;
429           if (!cursor.ReadInt64(&correlation_id)) {
430             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
431             return;
432           }
433           UniquePid upid =
434               procs->GetOrCreateProcess(static_cast<uint32_t>(tinfo.pid));
435           TrackId track_id = context_->track_tracker->InternLegacyAsyncTrack(
436               name, upid, correlation_id, false, kNullStringId);
437           slices->Begin(ts, track_id, cat, name, std::move(insert_args));
438           break;
439         }
440         case kAsyncInstant: {
441           int64_t correlation_id;
442           if (!cursor.ReadInt64(&correlation_id)) {
443             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
444             return;
445           }
446           UniquePid upid =
447               procs->GetOrCreateProcess(static_cast<uint32_t>(tinfo.pid));
448           TrackId track_id = context_->track_tracker->InternLegacyAsyncTrack(
449               name, upid, correlation_id, false, kNullStringId);
450           slices->Scoped(ts, track_id, cat, name, 0, std::move(insert_args));
451           break;
452         }
453         case kAsyncEnd: {
454           int64_t correlation_id;
455           if (!cursor.ReadInt64(&correlation_id)) {
456             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
457             return;
458           }
459           UniquePid upid =
460               procs->GetOrCreateProcess(static_cast<uint32_t>(tinfo.pid));
461           TrackId track_id = context_->track_tracker->InternLegacyAsyncTrack(
462               name, upid, correlation_id, false, kNullStringId);
463           slices->End(ts, track_id, cat, name, std::move(insert_args));
464           break;
465         }
466         case kFlowBegin: {
467           uint64_t correlation_id;
468           if (!cursor.ReadUint64(&correlation_id)) {
469             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
470             return;
471           }
472           UniqueTid utid =
473               procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
474                                   static_cast<uint32_t>(tinfo.pid));
475           TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
476           context_->flow_tracker->Begin(track_id, correlation_id);
477           break;
478         }
479         case kFlowStep: {
480           uint64_t correlation_id;
481           if (!cursor.ReadUint64(&correlation_id)) {
482             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
483             return;
484           }
485           UniqueTid utid =
486               procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
487                                   static_cast<uint32_t>(tinfo.pid));
488           TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
489           context_->flow_tracker->Step(track_id, correlation_id);
490           break;
491         }
492         case kFlowEnd: {
493           uint64_t correlation_id;
494           if (!cursor.ReadUint64(&correlation_id)) {
495             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
496             return;
497           }
498           UniqueTid utid =
499               procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
500                                   static_cast<uint32_t>(tinfo.pid));
501           TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
502           context_->flow_tracker->End(track_id, correlation_id, true, true);
503           break;
504         }
505       }
506       break;
507     }
508     case kSchedulerEvent: {
509       auto event_type =
510           fuchsia_trace_utils::ReadField<uint32_t>(header, 60, 63);
511       switch (event_type) {
512         case kSchedulerEventLegacyContextSwitch: {
513           auto cpu = fuchsia_trace_utils::ReadField<uint32_t>(header, 16, 23);
514           auto outgoing_state =
515               fuchsia_trace_utils::ReadField<uint32_t>(header, 24, 27);
516           auto outgoing_thread_ref =
517               fuchsia_trace_utils::ReadField<uint32_t>(header, 28, 35);
518           auto incoming_thread_ref =
519               fuchsia_trace_utils::ReadField<uint32_t>(header, 36, 43);
520           auto outgoing_priority =
521               fuchsia_trace_utils::ReadField<int32_t>(header, 44, 51);
522           auto incoming_priority =
523               fuchsia_trace_utils::ReadField<int32_t>(header, 52, 59);
524 
525           int64_t ts;
526           if (!cursor.ReadTimestamp(fr.get_ticks_per_second(), &ts)) {
527             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
528             return;
529           }
530           if (ts < 0) {
531             context_->storage->IncrementStats(stats::fuchsia_timestamp_overflow);
532             return;
533           }
534 
535           FuchsiaThreadInfo outgoing_thread_info;
536           if (fuchsia_trace_utils::IsInlineThread(outgoing_thread_ref)) {
537             if (!cursor.ReadInlineThread(&outgoing_thread_info)) {
538               context_->storage->IncrementStats(stats::fuchsia_record_read_error);
539               return;
540             }
541           } else {
542             outgoing_thread_info = fr.GetThread(outgoing_thread_ref);
543           }
544           Thread& outgoing_thread = GetThread(outgoing_thread_info.tid);
545 
546           FuchsiaThreadInfo incoming_thread_info;
547           if (fuchsia_trace_utils::IsInlineThread(incoming_thread_ref)) {
548             if (!cursor.ReadInlineThread(&incoming_thread_info)) {
549               context_->storage->IncrementStats(stats::fuchsia_record_read_error);
550               return;
551             }
552           } else {
553             incoming_thread_info = fr.GetThread(incoming_thread_ref);
554           }
555           Thread& incoming_thread = GetThread(incoming_thread_info.tid);
556 
557           // Idle threads are identified by pid == 0 and prio == 0.
558           const bool incoming_is_idle =
559               incoming_thread.info.pid == 0 && incoming_priority == 0;
560           const bool outgoing_is_idle =
561               outgoing_thread.info.pid == 0 && outgoing_priority == 0;
562 
563           // Handle switching away from the currently running thread.
564           if (!outgoing_is_idle) {
565             SwitchFrom(&outgoing_thread, ts, cpu, outgoing_state);
566           }
567 
568           // Handle switching to the new currently running thread.
569           if (!incoming_is_idle) {
570             SwitchTo(&incoming_thread, ts, cpu, incoming_priority);
571           }
572           break;
573         }
574         case kSchedulerEventContextSwitch: {
575           const auto argument_count =
576               fuchsia_trace_utils::ReadField<uint32_t>(header, 16, 19);
577           const auto cpu =
578               fuchsia_trace_utils::ReadField<uint32_t>(header, 20, 35);
579           const auto outgoing_state =
580               fuchsia_trace_utils::ReadField<uint32_t>(header, 36, 39);
581 
582           int64_t ts;
583           if (!cursor.ReadTimestamp(fr.get_ticks_per_second(), &ts)) {
584             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
585             return;
586           }
587           if (ts < 0) {
588             context_->storage->IncrementStats(stats::fuchsia_timestamp_overflow);
589             return;
590           }
591 
592           uint64_t outgoing_tid;
593           if (!cursor.ReadUint64(&outgoing_tid)) {
594             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
595             return;
596           }
597           Thread& outgoing_thread = GetThread(outgoing_tid);
598 
599           uint64_t incoming_tid;
600           if (!cursor.ReadUint64(&incoming_tid)) {
601             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
602             return;
603           }
604           Thread& incoming_thread = GetThread(incoming_tid);
605 
606           auto maybe_args = FuchsiaTraceParser::ParseArgs(
607               cursor, argument_count, intern_string, get_string);
608           if (!maybe_args.has_value()) {
609             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
610             return;
611           }
612 
613           int32_t incoming_weight = 0;
614           int32_t outgoing_weight = 0;
615 
616           for (const auto& arg : *maybe_args) {
617             if (arg.name == incoming_weight_id_) {
618               if (arg.value.Type() !=
619                   fuchsia_trace_utils::ArgValue::ArgType::kInt32) {
620                 context_->storage->IncrementStats(stats::fuchsia_invalid_event_arg_type);
621                 return;
622               }
623               incoming_weight = arg.value.Int32();
624             } else if (arg.name == outgoing_weight_id_) {
625               if (arg.value.Type() !=
626                   fuchsia_trace_utils::ArgValue::ArgType::kInt32) {
627                 context_->storage->IncrementStats(stats::fuchsia_invalid_event_arg_type);
628                 return;
629               }
630               outgoing_weight = arg.value.Int32();
631             }
632           }
633 
634           const bool incoming_is_idle = incoming_weight == kIdleWeight;
635           const bool outgoing_is_idle = outgoing_weight == kIdleWeight;
636 
637           // Handle switching away from the currently running thread.
638           if (!outgoing_is_idle) {
639             SwitchFrom(&outgoing_thread, ts, cpu, outgoing_state);
640           }
641 
642           // Handle switching to the new currently running thread.
643           if (!incoming_is_idle) {
644             SwitchTo(&incoming_thread, ts, cpu, incoming_weight);
645           }
646           break;
647         }
648         case kSchedulerEventThreadWakeup: {
649           const auto argument_count =
650               fuchsia_trace_utils::ReadField<uint32_t>(header, 16, 19);
651           const auto cpu =
652               fuchsia_trace_utils::ReadField<uint32_t>(header, 20, 35);
653 
654           int64_t ts;
655           if (!cursor.ReadTimestamp(fr.get_ticks_per_second(), &ts)) {
656             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
657             return;
658           }
659           if (ts < 0) {
660             context_->storage->IncrementStats(stats::fuchsia_timestamp_overflow);
661             return;
662           }
663 
664           uint64_t waking_tid;
665           if (!cursor.ReadUint64(&waking_tid)) {
666             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
667             return;
668           }
669           Thread& waking_thread = GetThread(waking_tid);
670 
671           auto maybe_args = FuchsiaTraceParser::ParseArgs(
672               cursor, argument_count, intern_string, get_string);
673           if (!maybe_args.has_value()) {
674             context_->storage->IncrementStats(stats::fuchsia_record_read_error);
675             return;
676           }
677 
678           int32_t waking_weight = 0;
679 
680           for (const auto& arg : *maybe_args) {
681             if (arg.name == weight_id_) {
682               if (arg.value.Type() !=
683                   fuchsia_trace_utils::ArgValue::ArgType::kInt32) {
684                 context_->storage->IncrementStats(
685                     stats::fuchsia_invalid_event_arg_type);
686                 return;
687               }
688               waking_weight = arg.value.Int32();
689             }
690           }
691 
692           const bool waking_is_idle = waking_weight == kIdleWeight;
693           if (!waking_is_idle) {
694             Wake(&waking_thread, ts, cpu);
695           }
696           break;
697         }
698         default:
699           PERFETTO_DLOG("Skipping unknown scheduler event type %d", event_type);
700           break;
701       }
702       break;
703     }
704     default: {
705       PERFETTO_DFATAL("Unknown record type %d in FuchsiaTraceParser",
706                       record_type);
707       break;
708     }
709   }
710 }
711 
SwitchFrom(Thread * thread,int64_t ts,uint32_t cpu,uint32_t thread_state)712 void FuchsiaTraceParser::SwitchFrom(Thread* thread,
713                                     int64_t ts,
714                                     uint32_t cpu,
715                                     uint32_t thread_state) {
716   TraceStorage* storage = context_->storage.get();
717   ProcessTracker* procs = context_->process_tracker.get();
718 
719   StringId state = IdForOutgoingThreadState(thread_state);
720   UniqueTid utid = procs->UpdateThread(static_cast<uint32_t>(thread->info.tid),
721                                        static_cast<uint32_t>(thread->info.pid));
722 
723   const auto duration = ts - thread->last_ts;
724   thread->last_ts = ts;
725 
726   // Close the slice record if one is open for this thread.
727   if (thread->last_slice_row.has_value()) {
728     auto row_ref = thread->last_slice_row->ToRowReference(
729         storage->mutable_sched_slice_table());
730     row_ref.set_dur(duration);
731     row_ref.set_end_state(state);
732     thread->last_slice_row.reset();
733   }
734 
735   // Close the state record if one is open for this thread.
736   if (thread->last_state_row.has_value()) {
737     auto row_ref = thread->last_state_row->ToRowReference(
738         storage->mutable_thread_state_table());
739     row_ref.set_dur(duration);
740     thread->last_state_row.reset();
741   }
742 
743   // Open a new state record to track the duration of the outgoing
744   // state.
745   tables::ThreadStateTable::Row state_row;
746   state_row.ts = ts;
747   state_row.ucpu = context_->cpu_tracker->GetOrCreateCpu(cpu);
748   state_row.dur = -1;
749   state_row.state = state;
750   state_row.utid = utid;
751   auto state_row_number =
752       storage->mutable_thread_state_table()->Insert(state_row).row_number;
753   thread->last_state_row = state_row_number;
754 }
755 
SwitchTo(Thread * thread,int64_t ts,uint32_t cpu,int32_t weight)756 void FuchsiaTraceParser::SwitchTo(Thread* thread,
757                                   int64_t ts,
758                                   uint32_t cpu,
759                                   int32_t weight) {
760   TraceStorage* storage = context_->storage.get();
761   ProcessTracker* procs = context_->process_tracker.get();
762 
763   UniqueTid utid = procs->UpdateThread(static_cast<uint32_t>(thread->info.tid),
764                                        static_cast<uint32_t>(thread->info.pid));
765 
766   const auto duration = ts - thread->last_ts;
767   thread->last_ts = ts;
768 
769   // Close the state record if one is open for this thread.
770   if (thread->last_state_row.has_value()) {
771     auto row_ref = thread->last_state_row->ToRowReference(
772         storage->mutable_thread_state_table());
773     row_ref.set_dur(duration);
774     thread->last_state_row.reset();
775   }
776 
777   auto ucpu = context_->cpu_tracker->GetOrCreateCpu(cpu);
778   // Open a new slice record for this thread.
779   tables::SchedSliceTable::Row slice_row;
780   slice_row.ts = ts;
781   slice_row.ucpu = ucpu;
782   slice_row.dur = -1;
783   slice_row.utid = utid;
784   slice_row.priority = weight;
785   auto slice_row_number =
786       storage->mutable_sched_slice_table()->Insert(slice_row).row_number;
787   thread->last_slice_row = slice_row_number;
788 
789   // Open a new state record for this thread.
790   tables::ThreadStateTable::Row state_row;
791   state_row.ts = ts;
792   state_row.ucpu = context_->cpu_tracker->GetOrCreateCpu(cpu);
793   state_row.dur = -1;
794   state_row.state = running_string_id_;
795   state_row.utid = utid;
796   auto state_row_number =
797       storage->mutable_thread_state_table()->Insert(state_row).row_number;
798   thread->last_state_row = state_row_number;
799 }
800 
Wake(Thread * thread,int64_t ts,uint32_t cpu)801 void FuchsiaTraceParser::Wake(Thread* thread, int64_t ts, uint32_t cpu) {
802   TraceStorage* storage = context_->storage.get();
803   ProcessTracker* procs = context_->process_tracker.get();
804 
805   UniqueTid utid = procs->UpdateThread(static_cast<uint32_t>(thread->info.tid),
806                                        static_cast<uint32_t>(thread->info.pid));
807 
808   const auto duration = ts - thread->last_ts;
809   thread->last_ts = ts;
810 
811   // Close the state record if one is open for this thread.
812   if (thread->last_state_row.has_value()) {
813     auto row_ref = thread->last_state_row->ToRowReference(
814         storage->mutable_thread_state_table());
815     row_ref.set_dur(duration);
816     thread->last_state_row.reset();
817   }
818 
819   // Open a new state record for this thread.
820   tables::ThreadStateTable::Row state_row;
821   state_row.ts = ts;
822   state_row.ucpu = context_->cpu_tracker->GetOrCreateCpu(cpu);
823   state_row.dur = -1;
824   state_row.state = waking_string_id_;
825   state_row.utid = utid;
826   auto state_row_number =
827       storage->mutable_thread_state_table()->Insert(state_row).row_number;
828   thread->last_state_row = state_row_number;
829 }
830 
IdForOutgoingThreadState(uint32_t state)831 StringId FuchsiaTraceParser::IdForOutgoingThreadState(uint32_t state) {
832   switch (state) {
833     case kThreadNew:
834     case kThreadRunning:
835       return runnable_string_id_;
836     case kThreadBlocked:
837       return blocked_string_id_;
838     case kThreadSuspended:
839       return suspended_string_id_;
840     case kThreadDying:
841       return exit_dying_string_id_;
842     case kThreadDead:
843       return exit_dead_string_id_;
844     default:
845       return kNullStringId;
846   }
847 }
848 
849 }  // namespace perfetto::trace_processor
850