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