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 <optional>
22 #include <string>
23 #include <utility>
24 #include <vector>
25
26 #include "perfetto/base/logging.h"
27 #include "perfetto/ext/base/string_view.h"
28 #include "src/trace_processor/importers/common/args_tracker.h"
29 #include "src/trace_processor/importers/common/event_tracker.h"
30 #include "src/trace_processor/importers/common/flow_tracker.h"
31 #include "src/trace_processor/importers/common/process_tracker.h"
32 #include "src/trace_processor/importers/common/slice_tracker.h"
33 #include "src/trace_processor/importers/common/track_tracker.h"
34 #include "src/trace_processor/importers/common/tracks.h"
35 #include "src/trace_processor/importers/common/tracks_common.h"
36 #include "src/trace_processor/importers/fuchsia/fuchsia_record.h"
37 #include "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h"
38 #include "src/trace_processor/storage/stats.h"
39 #include "src/trace_processor/storage/trace_storage.h"
40
41 namespace perfetto::trace_processor {
42
43 namespace {
44 // Record Types
45 constexpr uint32_t kEvent = 4;
46
47 // Event Types
48 constexpr uint32_t kInstant = 0;
49 constexpr uint32_t kCounter = 1;
50 constexpr uint32_t kDurationBegin = 2;
51 constexpr uint32_t kDurationEnd = 3;
52 constexpr uint32_t kDurationComplete = 4;
53 constexpr uint32_t kAsyncBegin = 5;
54 constexpr uint32_t kAsyncInstant = 6;
55 constexpr uint32_t kAsyncEnd = 7;
56 constexpr uint32_t kFlowBegin = 8;
57 constexpr uint32_t kFlowStep = 9;
58 constexpr uint32_t kFlowEnd = 10;
59
60 // Argument Types
61 constexpr uint32_t kNull = 0;
62 constexpr uint32_t kInt32 = 1;
63 constexpr uint32_t kUint32 = 2;
64 constexpr uint32_t kInt64 = 3;
65 constexpr uint32_t kUint64 = 4;
66 constexpr uint32_t kDouble = 5;
67 constexpr uint32_t kString = 6;
68 constexpr uint32_t kPointer = 7;
69 constexpr uint32_t kKoid = 8;
70 constexpr uint32_t kBool = 9;
71
72 constexpr auto kCounterBlueprint = tracks::CounterBlueprint(
73 "fuchsia_counter",
74 tracks::UnknownUnitBlueprint(),
75 tracks::DimensionBlueprints(tracks::kProcessDimensionBlueprint,
76 tracks::kNameFromTraceDimensionBlueprint),
77 tracks::DynamicNameBlueprint());
78
79 } // namespace
80
FuchsiaTraceParser(TraceProcessorContext * context)81 FuchsiaTraceParser::FuchsiaTraceParser(TraceProcessorContext* context)
82 : context_(context) {}
83
84 FuchsiaTraceParser::~FuchsiaTraceParser() = default;
85
86 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)87 FuchsiaTraceParser::ParseArgs(
88 fuchsia_trace_utils::RecordCursor& cursor,
89 uint32_t n_args,
90 std::function<StringId(base::StringView string)> intern_string,
91 std::function<StringId(uint32_t index)> get_string) {
92 std::vector<Arg> args;
93 for (uint32_t i = 0; i < n_args; i++) {
94 size_t arg_base = cursor.WordIndex();
95 uint64_t arg_header;
96 if (!cursor.ReadUint64(&arg_header)) {
97 return std::nullopt;
98 }
99 uint32_t arg_type =
100 fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 0, 3);
101 uint32_t arg_size_words =
102 fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 4, 15);
103 uint32_t arg_name_ref =
104 fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 16, 31);
105 Arg arg;
106 if (fuchsia_trace_utils::IsInlineString(arg_name_ref)) {
107 base::StringView arg_name_view;
108 if (!cursor.ReadInlineString(arg_name_ref, &arg_name_view)) {
109 return std::nullopt;
110 }
111 arg.name = intern_string(arg_name_view);
112 } else {
113 arg.name = get_string(arg_name_ref);
114 }
115
116 switch (arg_type) {
117 case kNull:
118 arg.value = fuchsia_trace_utils::ArgValue::Null();
119 break;
120 case kInt32:
121 arg.value = fuchsia_trace_utils::ArgValue::Int32(
122 fuchsia_trace_utils::ReadField<int32_t>(arg_header, 32, 63));
123 break;
124 case kUint32:
125 arg.value = fuchsia_trace_utils::ArgValue::Uint32(
126 fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 32, 63));
127 break;
128 case kInt64: {
129 int64_t value;
130 if (!cursor.ReadInt64(&value)) {
131 return std::nullopt;
132 }
133 arg.value = fuchsia_trace_utils::ArgValue::Int64(value);
134 break;
135 }
136 case kUint64: {
137 uint64_t value;
138 if (!cursor.ReadUint64(&value)) {
139 return std::nullopt;
140 }
141 arg.value = fuchsia_trace_utils::ArgValue::Uint64(value);
142 break;
143 }
144 case kDouble: {
145 double value;
146 if (!cursor.ReadDouble(&value)) {
147 return std::nullopt;
148 }
149 arg.value = fuchsia_trace_utils::ArgValue::Double(value);
150 break;
151 }
152 case kString: {
153 uint32_t arg_value_ref =
154 fuchsia_trace_utils::ReadField<uint32_t>(arg_header, 32, 47);
155 StringId value;
156 if (fuchsia_trace_utils::IsInlineString(arg_value_ref)) {
157 base::StringView arg_value_view;
158 if (!cursor.ReadInlineString(arg_value_ref, &arg_value_view)) {
159 return std::nullopt;
160 }
161 value = intern_string(arg_value_view);
162 } else {
163 value = get_string(arg_value_ref);
164 }
165 arg.value = fuchsia_trace_utils::ArgValue::String(value);
166 break;
167 }
168 case kPointer: {
169 uint64_t value;
170 if (!cursor.ReadUint64(&value)) {
171 return std::nullopt;
172 }
173 arg.value = fuchsia_trace_utils::ArgValue::Pointer(value);
174 break;
175 }
176 case kKoid: {
177 uint64_t value;
178 if (!cursor.ReadUint64(&value)) {
179 return std::nullopt;
180 }
181 arg.value = fuchsia_trace_utils::ArgValue::Koid(value);
182 break;
183 }
184 case kBool: {
185 arg.value = fuchsia_trace_utils::ArgValue::Bool(
186 fuchsia_trace_utils::ReadField<bool>(arg_header, 32, 63));
187 break;
188 }
189 default:
190 arg.value = fuchsia_trace_utils::ArgValue::Unknown();
191 break;
192 }
193
194 args.push_back(arg);
195 cursor.SetWordIndex(arg_base + arg_size_words);
196 }
197
198 return {std::move(args)};
199 }
200
ParseFuchsiaRecord(int64_t,FuchsiaRecord fr)201 void FuchsiaTraceParser::ParseFuchsiaRecord(int64_t, FuchsiaRecord fr) {
202 // The timestamp is also present in the record, so we'll ignore the one
203 // passed as an argument.
204 fuchsia_trace_utils::RecordCursor cursor(fr.record_view()->data(),
205 fr.record_view()->length());
206 ProcessTracker* procs = context_->process_tracker.get();
207 SliceTracker* slices = context_->slice_tracker.get();
208
209 uint64_t header;
210 if (!cursor.ReadUint64(&header)) {
211 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
212 return;
213 }
214 uint32_t record_type = fuchsia_trace_utils::ReadField<uint32_t>(header, 0, 3);
215 switch (record_type) {
216 case kEvent: {
217 uint32_t event_type =
218 fuchsia_trace_utils::ReadField<uint32_t>(header, 16, 19);
219 uint32_t n_args =
220 fuchsia_trace_utils::ReadField<uint32_t>(header, 20, 23);
221 uint32_t thread_ref =
222 fuchsia_trace_utils::ReadField<uint32_t>(header, 24, 31);
223 uint32_t cat_ref =
224 fuchsia_trace_utils::ReadField<uint32_t>(header, 32, 47);
225 uint32_t name_ref =
226 fuchsia_trace_utils::ReadField<uint32_t>(header, 48, 63);
227
228 int64_t ts;
229 if (!cursor.ReadTimestamp(fr.get_ticks_per_second(), &ts)) {
230 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
231 return;
232 }
233 FuchsiaThreadInfo tinfo;
234 if (fuchsia_trace_utils::IsInlineThread(thread_ref)) {
235 if (!cursor.ReadInlineThread(&tinfo)) {
236 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
237 return;
238 }
239 } else {
240 tinfo = fr.GetThread(thread_ref);
241 }
242 StringId cat;
243 if (fuchsia_trace_utils::IsInlineString(cat_ref)) {
244 base::StringView cat_string_view;
245 if (!cursor.ReadInlineString(cat_ref, &cat_string_view)) {
246 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
247 return;
248 }
249 cat = context_->storage->InternString(cat_string_view);
250 } else {
251 cat = fr.GetString(cat_ref);
252 }
253 StringId name;
254 if (fuchsia_trace_utils::IsInlineString(name_ref)) {
255 base::StringView name_string_view;
256 if (!cursor.ReadInlineString(name_ref, &name_string_view)) {
257 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
258 return;
259 }
260 name = context_->storage->InternString(name_string_view);
261 } else {
262 name = fr.GetString(name_ref);
263 }
264
265 // Read arguments
266 const auto intern_string = [this](base::StringView string) {
267 return context_->storage->InternString(string);
268 };
269 const auto get_string = [&fr](uint32_t index) {
270 return fr.GetString(index);
271 };
272
273 auto maybe_args = FuchsiaTraceParser::ParseArgs(
274 cursor, n_args, intern_string, get_string);
275 if (!maybe_args.has_value()) {
276 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
277 return;
278 }
279
280 auto insert_args =
281 [this, args = *maybe_args](ArgsTracker::BoundInserter* inserter) {
282 for (const Arg& arg : args) {
283 inserter->AddArg(
284 arg.name, arg.name,
285 arg.value.ToStorageVariadic(context_->storage.get()));
286 }
287 };
288
289 switch (event_type) {
290 case kInstant: {
291 UniqueTid utid =
292 procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
293 static_cast<uint32_t>(tinfo.pid));
294 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
295 slices->Scoped(ts, track_id, cat, name, 0, std::move(insert_args));
296 break;
297 }
298 case kCounter: {
299 UniquePid upid =
300 procs->GetOrCreateProcess(static_cast<uint32_t>(tinfo.pid));
301 std::string name_str =
302 context_->storage->GetString(name).ToStdString();
303 uint64_t counter_id;
304 if (!cursor.ReadUint64(&counter_id)) {
305 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
306 return;
307 }
308 // Note: In the Fuchsia trace format, counter values are stored
309 // in the arguments for the record, with the data series defined
310 // by both the record name and the argument name. In Perfetto,
311 // counters only have one name, so we combine both names into
312 // one here.
313 for (const Arg& arg : *maybe_args) {
314 std::string counter_name_str = name_str + ":";
315 counter_name_str +=
316 context_->storage->GetString(arg.name).ToStdString();
317 counter_name_str += ":" + std::to_string(counter_id);
318 bool is_valid_value = false;
319 double counter_value = -1;
320 switch (arg.value.Type()) {
321 case fuchsia_trace_utils::ArgValue::kInt32:
322 is_valid_value = true;
323 counter_value = static_cast<double>(arg.value.Int32());
324 break;
325 case fuchsia_trace_utils::ArgValue::kUint32:
326 is_valid_value = true;
327 counter_value = static_cast<double>(arg.value.Uint32());
328 break;
329 case fuchsia_trace_utils::ArgValue::kInt64:
330 is_valid_value = true;
331 counter_value = static_cast<double>(arg.value.Int64());
332 break;
333 case fuchsia_trace_utils::ArgValue::kUint64:
334 is_valid_value = true;
335 counter_value = static_cast<double>(arg.value.Uint64());
336 break;
337 case fuchsia_trace_utils::ArgValue::kDouble:
338 is_valid_value = true;
339 counter_value = arg.value.Double();
340 break;
341 case fuchsia_trace_utils::ArgValue::kNull:
342 case fuchsia_trace_utils::ArgValue::kString:
343 case fuchsia_trace_utils::ArgValue::kPointer:
344 case fuchsia_trace_utils::ArgValue::kKoid:
345 case fuchsia_trace_utils::ArgValue::kBool:
346 case fuchsia_trace_utils::ArgValue::kUnknown:
347 context_->storage->IncrementStats(
348 stats::fuchsia_non_numeric_counters);
349 break;
350 }
351 if (is_valid_value) {
352 base::StringView counter_name_str_view(counter_name_str);
353 StringId counter_name_id =
354 context_->storage->InternString(counter_name_str_view);
355 TrackId track = context_->track_tracker->InternTrack(
356 kCounterBlueprint,
357 tracks::Dimensions(upid, counter_name_str_view),
358 tracks::DynamicName(counter_name_id));
359 context_->event_tracker->PushCounter(ts, counter_value, track);
360 }
361 }
362 break;
363 }
364 case kDurationBegin: {
365 UniqueTid utid =
366 procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
367 static_cast<uint32_t>(tinfo.pid));
368 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
369 slices->Begin(ts, track_id, cat, name, std::move(insert_args));
370 break;
371 }
372 case kDurationEnd: {
373 UniqueTid utid =
374 procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
375 static_cast<uint32_t>(tinfo.pid));
376 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
377 // TODO(b/131181693): |cat| and |name| are not passed here so
378 // that if two slices end at the same timestep, the slices get
379 // closed in the correct order regardless of which end event is
380 // processed first.
381 slices->End(ts, track_id, {}, {}, std::move(insert_args));
382 break;
383 }
384 case kDurationComplete: {
385 int64_t end_ts;
386 if (!cursor.ReadTimestamp(fr.get_ticks_per_second(), &end_ts)) {
387 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
388 return;
389 }
390 int64_t duration = end_ts - ts;
391 if (duration < 0) {
392 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
393 return;
394 }
395 UniqueTid utid =
396 procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
397 static_cast<uint32_t>(tinfo.pid));
398 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
399 slices->Scoped(ts, track_id, cat, name, duration,
400 std::move(insert_args));
401 break;
402 }
403 case kAsyncBegin: {
404 int64_t correlation_id;
405 if (!cursor.ReadInt64(&correlation_id)) {
406 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
407 return;
408 }
409 UniquePid upid =
410 procs->GetOrCreateProcess(static_cast<uint32_t>(tinfo.pid));
411 TrackId track_id =
412 context_->track_tracker->LegacyInternLegacyChromeAsyncTrack(
413 name, upid, correlation_id, false, kNullStringId);
414 slices->Begin(ts, track_id, cat, name, std::move(insert_args));
415 break;
416 }
417 case kAsyncInstant: {
418 int64_t correlation_id;
419 if (!cursor.ReadInt64(&correlation_id)) {
420 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
421 return;
422 }
423 UniquePid upid =
424 procs->GetOrCreateProcess(static_cast<uint32_t>(tinfo.pid));
425 TrackId track_id =
426 context_->track_tracker->LegacyInternLegacyChromeAsyncTrack(
427 name, upid, correlation_id, false, kNullStringId);
428 slices->Scoped(ts, track_id, cat, name, 0, std::move(insert_args));
429 break;
430 }
431 case kAsyncEnd: {
432 int64_t correlation_id;
433 if (!cursor.ReadInt64(&correlation_id)) {
434 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
435 return;
436 }
437 UniquePid upid =
438 procs->GetOrCreateProcess(static_cast<uint32_t>(tinfo.pid));
439 TrackId track_id =
440 context_->track_tracker->LegacyInternLegacyChromeAsyncTrack(
441 name, upid, correlation_id, false, kNullStringId);
442 slices->End(ts, track_id, cat, name, std::move(insert_args));
443 break;
444 }
445 case kFlowBegin: {
446 uint64_t correlation_id;
447 if (!cursor.ReadUint64(&correlation_id)) {
448 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
449 return;
450 }
451 UniqueTid utid =
452 procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
453 static_cast<uint32_t>(tinfo.pid));
454 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
455 context_->flow_tracker->Begin(track_id, correlation_id);
456 break;
457 }
458 case kFlowStep: {
459 uint64_t correlation_id;
460 if (!cursor.ReadUint64(&correlation_id)) {
461 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
462 return;
463 }
464 UniqueTid utid =
465 procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
466 static_cast<uint32_t>(tinfo.pid));
467 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
468 context_->flow_tracker->Step(track_id, correlation_id);
469 break;
470 }
471 case kFlowEnd: {
472 uint64_t correlation_id;
473 if (!cursor.ReadUint64(&correlation_id)) {
474 context_->storage->IncrementStats(stats::fuchsia_invalid_event);
475 return;
476 }
477 UniqueTid utid =
478 procs->UpdateThread(static_cast<uint32_t>(tinfo.tid),
479 static_cast<uint32_t>(tinfo.pid));
480 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
481 context_->flow_tracker->End(track_id, correlation_id, true, true);
482 break;
483 }
484 }
485 break;
486 }
487 default: {
488 PERFETTO_DFATAL("Unknown record type %d in FuchsiaTraceParser",
489 record_type);
490 break;
491 }
492 }
493 }
494
495 } // namespace perfetto::trace_processor
496