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