1 /*
2 * Copyright (C) 2018 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/proto/proto_trace_parser.h"
18
19 #include <inttypes.h>
20 #include <string.h>
21
22 #include <string>
23
24 #include "perfetto/base/logging.h"
25 #include "perfetto/ext/base/metatrace_events.h"
26 #include "perfetto/ext/base/string_utils.h"
27 #include "perfetto/ext/base/string_view.h"
28 #include "perfetto/ext/base/string_writer.h"
29 #include "perfetto/ext/base/utils.h"
30 #include "perfetto/ext/base/uuid.h"
31 #include "perfetto/trace_processor/status.h"
32 #include "src/trace_processor/importers/common/args_tracker.h"
33 #include "src/trace_processor/importers/common/clock_tracker.h"
34 #include "src/trace_processor/importers/common/event_tracker.h"
35 #include "src/trace_processor/importers/common/process_tracker.h"
36 #include "src/trace_processor/importers/common/slice_tracker.h"
37 #include "src/trace_processor/importers/common/track_tracker.h"
38 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
39 #include "src/trace_processor/importers/proto/heap_profile_tracker.h"
40 #include "src/trace_processor/importers/proto/metadata_tracker.h"
41 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
42 #include "src/trace_processor/importers/proto/perf_sample_tracker.h"
43 #include "src/trace_processor/importers/proto/profile_packet_utils.h"
44 #include "src/trace_processor/importers/proto/stack_profile_tracker.h"
45 #include "src/trace_processor/storage/metadata.h"
46 #include "src/trace_processor/timestamped_trace_piece.h"
47 #include "src/trace_processor/types/trace_processor_context.h"
48 #include "src/trace_processor/types/variadic.h"
49
50 #include "protos/perfetto/common/builtin_clock.pbzero.h"
51 #include "protos/perfetto/common/trace_stats.pbzero.h"
52 #include "protos/perfetto/config/trace_config.pbzero.h"
53 #include "protos/perfetto/trace/chrome/chrome_benchmark_metadata.pbzero.h"
54 #include "protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
55 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
56 #include "protos/perfetto/trace/perfetto/perfetto_metatrace.pbzero.h"
57 #include "protos/perfetto/trace/perfetto/tracing_service_event.pbzero.h"
58 #include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
59 #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
60 #include "protos/perfetto/trace/profiling/smaps.pbzero.h"
61 #include "protos/perfetto/trace/trace.pbzero.h"
62 #include "protos/perfetto/trace/trace_packet.pbzero.h"
63 #include "protos/perfetto/trace/trigger.pbzero.h"
64
65 namespace perfetto {
66 namespace trace_processor {
67
ProtoTraceParser(TraceProcessorContext * context)68 ProtoTraceParser::ProtoTraceParser(TraceProcessorContext* context)
69 : context_(context),
70 metatrace_id_(context->storage->InternString("metatrace")),
71 data_name_id_(context->storage->InternString("data")),
72 raw_chrome_metadata_event_id_(
73 context->storage->InternString("chrome_event.metadata")),
74 raw_chrome_legacy_system_trace_event_id_(
75 context->storage->InternString("chrome_event.legacy_system_trace")),
76 raw_chrome_legacy_user_trace_event_id_(
77 context->storage->InternString("chrome_event.legacy_user_trace")) {
78 // TODO(140860736): Once we support null values for
79 // stack_profile_frame.symbol_set_id remove this hack
80 context_->storage->mutable_symbol_table()->Insert(
81 {0, kNullStringId, kNullStringId, 0});
82 }
83
84 ProtoTraceParser::~ProtoTraceParser() = default;
85
ParseTracePacket(int64_t ts,TimestampedTracePiece ttp)86 void ProtoTraceParser::ParseTracePacket(int64_t ts, TimestampedTracePiece ttp) {
87 const TracePacketData* data = nullptr;
88 if (ttp.type == TimestampedTracePiece::Type::kTracePacket) {
89 data = &ttp.packet_data;
90 } else {
91 PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTrackEvent);
92 data = ttp.track_event_data.get();
93 }
94
95 const TraceBlobView& blob = data->packet;
96 protos::pbzero::TracePacket::Decoder packet(blob.data(), blob.length());
97
98 ParseTracePacketImpl(ts, std::move(ttp), data, packet);
99
100 // TODO(lalitm): maybe move this to the flush method in the trace processor
101 // once we have it. This may reduce performance in the ArgsTracker though so
102 // needs to be handled carefully.
103 context_->args_tracker->Flush();
104 PERFETTO_DCHECK(!packet.bytes_left());
105 }
106
ParseTracePacketImpl(int64_t ts,TimestampedTracePiece ttp,const TracePacketData * data,const protos::pbzero::TracePacket::Decoder & packet)107 void ProtoTraceParser::ParseTracePacketImpl(
108 int64_t ts,
109 TimestampedTracePiece ttp,
110 const TracePacketData* data,
111 const protos::pbzero::TracePacket::Decoder& packet) {
112 // TODO(eseckler): Propagate statuses from modules.
113 auto& modules = context_->modules_by_field;
114 for (uint32_t field_id = 1; field_id < modules.size(); ++field_id) {
115 if (modules[field_id] && packet.Get(field_id).valid()) {
116 modules[field_id]->ParsePacket(packet, ttp, field_id);
117 return;
118 }
119 }
120
121 if (packet.has_trace_stats())
122 ParseTraceStats(packet.trace_stats());
123
124 if (packet.has_profile_packet()) {
125 ParseProfilePacket(ts, data->sequence_state,
126 packet.trusted_packet_sequence_id(),
127 packet.profile_packet());
128 }
129
130 if (packet.has_perf_sample()) {
131 ParsePerfSample(ts, data->sequence_state, packet.perf_sample());
132 }
133
134 if (packet.has_chrome_benchmark_metadata()) {
135 ParseChromeBenchmarkMetadata(packet.chrome_benchmark_metadata());
136 }
137
138 if (packet.has_chrome_events()) {
139 ParseChromeEvents(ts, packet.chrome_events());
140 }
141
142 if (packet.has_perfetto_metatrace()) {
143 ParseMetatraceEvent(ts, packet.perfetto_metatrace());
144 }
145
146 if (packet.has_trace_config()) {
147 ParseTraceConfig(packet.trace_config());
148 }
149
150 if (packet.has_module_symbols()) {
151 ParseModuleSymbols(packet.module_symbols());
152 }
153
154 if (packet.has_trigger()) {
155 ParseTrigger(ts, packet.trigger());
156 }
157
158 if (packet.has_service_event()) {
159 ParseServiceEvent(ts, packet.service_event());
160 }
161
162 if (packet.has_smaps_packet()) {
163 ParseSmapsPacket(ts, packet.smaps_packet());
164 }
165 }
166
ParseFtracePacket(uint32_t cpu,int64_t,TimestampedTracePiece ttp)167 void ProtoTraceParser::ParseFtracePacket(uint32_t cpu,
168 int64_t /*ts*/,
169 TimestampedTracePiece ttp) {
170 PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kFtraceEvent ||
171 ttp.type == TimestampedTracePiece::Type::kInlineSchedSwitch ||
172 ttp.type == TimestampedTracePiece::Type::kInlineSchedWaking);
173 PERFETTO_DCHECK(context_->ftrace_module);
174 context_->ftrace_module->ParseFtracePacket(cpu, ttp);
175
176 // TODO(lalitm): maybe move this to the flush method in the trace processor
177 // once we have it. This may reduce performance in the ArgsTracker though so
178 // needs to be handled carefully.
179 context_->args_tracker->Flush();
180 }
181
ParseTraceStats(ConstBytes blob)182 void ProtoTraceParser::ParseTraceStats(ConstBytes blob) {
183 protos::pbzero::TraceStats::Decoder evt(blob.data, blob.size);
184 auto* storage = context_->storage.get();
185 storage->SetStats(stats::traced_producers_connected,
186 static_cast<int64_t>(evt.producers_connected()));
187 storage->SetStats(stats::traced_data_sources_registered,
188 static_cast<int64_t>(evt.data_sources_registered()));
189 storage->SetStats(stats::traced_data_sources_seen,
190 static_cast<int64_t>(evt.data_sources_seen()));
191 storage->SetStats(stats::traced_tracing_sessions,
192 static_cast<int64_t>(evt.tracing_sessions()));
193 storage->SetStats(stats::traced_total_buffers,
194 static_cast<int64_t>(evt.total_buffers()));
195 storage->SetStats(stats::traced_chunks_discarded,
196 static_cast<int64_t>(evt.chunks_discarded()));
197 storage->SetStats(stats::traced_patches_discarded,
198 static_cast<int64_t>(evt.patches_discarded()));
199
200 int buf_num = 0;
201 for (auto it = evt.buffer_stats(); it; ++it, ++buf_num) {
202 protos::pbzero::TraceStats::BufferStats::Decoder buf(*it);
203 storage->SetIndexedStats(stats::traced_buf_buffer_size, buf_num,
204 static_cast<int64_t>(buf.buffer_size()));
205 storage->SetIndexedStats(stats::traced_buf_bytes_written, buf_num,
206 static_cast<int64_t>(buf.bytes_written()));
207 storage->SetIndexedStats(stats::traced_buf_bytes_overwritten, buf_num,
208 static_cast<int64_t>(buf.bytes_overwritten()));
209 storage->SetIndexedStats(stats::traced_buf_bytes_read, buf_num,
210 static_cast<int64_t>(buf.bytes_read()));
211 storage->SetIndexedStats(stats::traced_buf_padding_bytes_written, buf_num,
212 static_cast<int64_t>(buf.padding_bytes_written()));
213 storage->SetIndexedStats(stats::traced_buf_padding_bytes_cleared, buf_num,
214 static_cast<int64_t>(buf.padding_bytes_cleared()));
215 storage->SetIndexedStats(stats::traced_buf_chunks_written, buf_num,
216 static_cast<int64_t>(buf.chunks_written()));
217 storage->SetIndexedStats(stats::traced_buf_chunks_rewritten, buf_num,
218 static_cast<int64_t>(buf.chunks_rewritten()));
219 storage->SetIndexedStats(stats::traced_buf_chunks_overwritten, buf_num,
220 static_cast<int64_t>(buf.chunks_overwritten()));
221 storage->SetIndexedStats(stats::traced_buf_chunks_discarded, buf_num,
222 static_cast<int64_t>(buf.chunks_discarded()));
223 storage->SetIndexedStats(stats::traced_buf_chunks_read, buf_num,
224 static_cast<int64_t>(buf.chunks_read()));
225 storage->SetIndexedStats(
226 stats::traced_buf_chunks_committed_out_of_order, buf_num,
227 static_cast<int64_t>(buf.chunks_committed_out_of_order()));
228 storage->SetIndexedStats(stats::traced_buf_write_wrap_count, buf_num,
229 static_cast<int64_t>(buf.write_wrap_count()));
230 storage->SetIndexedStats(stats::traced_buf_patches_succeeded, buf_num,
231 static_cast<int64_t>(buf.patches_succeeded()));
232 storage->SetIndexedStats(stats::traced_buf_patches_failed, buf_num,
233 static_cast<int64_t>(buf.patches_failed()));
234 storage->SetIndexedStats(stats::traced_buf_readaheads_succeeded, buf_num,
235 static_cast<int64_t>(buf.readaheads_succeeded()));
236 storage->SetIndexedStats(stats::traced_buf_readaheads_failed, buf_num,
237 static_cast<int64_t>(buf.readaheads_failed()));
238 storage->SetIndexedStats(
239 stats::traced_buf_trace_writer_packet_loss, buf_num,
240 static_cast<int64_t>(buf.trace_writer_packet_loss()));
241 }
242 }
243
ParseProfilePacket(int64_t,PacketSequenceStateGeneration * sequence_state,uint32_t seq_id,ConstBytes blob)244 void ProtoTraceParser::ParseProfilePacket(
245 int64_t,
246 PacketSequenceStateGeneration* sequence_state,
247 uint32_t seq_id,
248 ConstBytes blob) {
249 protos::pbzero::ProfilePacket::Decoder packet(blob.data, blob.size);
250 context_->heap_profile_tracker->SetProfilePacketIndex(seq_id, packet.index());
251
252 for (auto it = packet.strings(); it; ++it) {
253 protos::pbzero::InternedString::Decoder entry(*it);
254
255 const char* str = reinterpret_cast<const char*>(entry.str().data);
256 auto str_view = base::StringView(str, entry.str().size);
257 sequence_state->state()->stack_profile_tracker().AddString(entry.iid(),
258 str_view);
259 }
260
261 for (auto it = packet.mappings(); it; ++it) {
262 protos::pbzero::Mapping::Decoder entry(*it);
263 StackProfileTracker::SourceMapping src_mapping =
264 ProfilePacketUtils::MakeSourceMapping(entry);
265 sequence_state->state()->stack_profile_tracker().AddMapping(entry.iid(),
266 src_mapping);
267 }
268
269 for (auto it = packet.frames(); it; ++it) {
270 protos::pbzero::Frame::Decoder entry(*it);
271 StackProfileTracker::SourceFrame src_frame =
272 ProfilePacketUtils::MakeSourceFrame(entry);
273 sequence_state->state()->stack_profile_tracker().AddFrame(entry.iid(),
274 src_frame);
275 }
276
277 for (auto it = packet.callstacks(); it; ++it) {
278 protos::pbzero::Callstack::Decoder entry(*it);
279 StackProfileTracker::SourceCallstack src_callstack =
280 ProfilePacketUtils::MakeSourceCallstack(entry);
281 sequence_state->state()->stack_profile_tracker().AddCallstack(
282 entry.iid(), src_callstack);
283 }
284
285 for (auto it = packet.process_dumps(); it; ++it) {
286 protos::pbzero::ProfilePacket::ProcessHeapSamples::Decoder entry(*it);
287
288 auto maybe_timestamp = context_->clock_tracker->ToTraceTime(
289 protos::pbzero::BUILTIN_CLOCK_MONOTONIC_COARSE,
290 static_cast<int64_t>(entry.timestamp()));
291
292 // ToTraceTime() increments the clock_sync_failure error stat in this case.
293 if (!maybe_timestamp)
294 continue;
295
296 int64_t timestamp = *maybe_timestamp;
297
298 int pid = static_cast<int>(entry.pid());
299
300 if (entry.disconnected())
301 context_->storage->IncrementIndexedStats(
302 stats::heapprofd_client_disconnected, pid);
303 if (entry.buffer_corrupted())
304 context_->storage->IncrementIndexedStats(
305 stats::heapprofd_buffer_corrupted, pid);
306 if (entry.buffer_overran())
307 context_->storage->IncrementIndexedStats(stats::heapprofd_buffer_overran,
308 pid);
309 if (entry.rejected_concurrent())
310 context_->storage->IncrementIndexedStats(
311 stats::heapprofd_rejected_concurrent, pid);
312 if (entry.hit_guardrail())
313 context_->storage->IncrementIndexedStats(stats::heapprofd_hit_guardrail,
314 pid);
315
316 for (auto sample_it = entry.samples(); sample_it; ++sample_it) {
317 protos::pbzero::ProfilePacket::HeapSample::Decoder sample(*sample_it);
318
319 HeapProfileTracker::SourceAllocation src_allocation;
320 src_allocation.pid = entry.pid();
321 src_allocation.timestamp = timestamp;
322 src_allocation.callstack_id = sample.callstack_id();
323 if (sample.self_max()) {
324 src_allocation.self_allocated = sample.self_max();
325 } else {
326 src_allocation.self_allocated = sample.self_allocated();
327 src_allocation.self_freed = sample.self_freed();
328 }
329 src_allocation.alloc_count = sample.alloc_count();
330 src_allocation.free_count = sample.free_count();
331
332 context_->heap_profile_tracker->StoreAllocation(seq_id, src_allocation);
333 }
334 }
335 if (!packet.continued()) {
336 PERFETTO_CHECK(sequence_state);
337 ProfilePacketInternLookup intern_lookup(sequence_state);
338 context_->heap_profile_tracker->FinalizeProfile(
339 seq_id, &sequence_state->state()->stack_profile_tracker(),
340 &intern_lookup);
341 }
342 }
343
ParsePerfSample(int64_t ts,PacketSequenceStateGeneration * sequence_state,ConstBytes blob)344 void ProtoTraceParser::ParsePerfSample(
345 int64_t ts,
346 PacketSequenceStateGeneration* sequence_state,
347 ConstBytes blob) {
348 using PerfSample = protos::pbzero::PerfSample;
349 PerfSample::Decoder sample(blob.data, blob.size);
350
351 // Not a sample, but an indication of data loss in the ring buffer shared with
352 // the kernel.
353 if (sample.kernel_records_lost() > 0) {
354 PERFETTO_DCHECK(sample.pid() == 0);
355
356 context_->storage->IncrementIndexedStats(
357 stats::perf_cpu_lost_records, static_cast<int>(sample.cpu()),
358 static_cast<int64_t>(sample.kernel_records_lost()));
359 return;
360 }
361
362 // Sample that looked relevant for the tracing session, but had to be skipped.
363 // Either we failed to look up the procfs file descriptors necessary for
364 // remote stack unwinding (not unexpected in most cases), or the unwind queue
365 // was out of capacity (producer lost data on its own).
366 if (sample.has_sample_skipped_reason()) {
367 context_->storage->IncrementStats(stats::perf_samples_skipped);
368
369 if (sample.sample_skipped_reason() ==
370 PerfSample::PROFILER_SKIP_UNWIND_ENQUEUE)
371 context_->storage->IncrementStats(stats::perf_samples_skipped_dataloss);
372
373 return;
374 }
375
376 uint64_t callstack_iid = sample.callstack_iid();
377 StackProfileTracker& stack_tracker =
378 sequence_state->state()->stack_profile_tracker();
379 ProfilePacketInternLookup intern_lookup(sequence_state);
380
381 base::Optional<CallsiteId> cs_id =
382 stack_tracker.FindOrInsertCallstack(callstack_iid, &intern_lookup);
383 if (!cs_id) {
384 context_->storage->IncrementStats(stats::stackprofile_parser_error);
385 PERFETTO_ELOG("PerfSample referencing invalid callstack iid [%" PRIu64
386 "] at timestamp [%" PRIi64 "]",
387 callstack_iid, ts);
388 return;
389 }
390
391 context_->perf_sample_tracker->AddStackToSliceTrack(
392 ts, *cs_id, sample.pid(), sample.tid(), sample.cpu());
393 }
394
ParseChromeBenchmarkMetadata(ConstBytes blob)395 void ProtoTraceParser::ParseChromeBenchmarkMetadata(ConstBytes blob) {
396 TraceStorage* storage = context_->storage.get();
397 MetadataTracker* metadata = context_->metadata_tracker.get();
398
399 protos::pbzero::ChromeBenchmarkMetadata::Decoder packet(blob.data, blob.size);
400 if (packet.has_benchmark_name()) {
401 auto benchmark_name_id = storage->InternString(packet.benchmark_name());
402 metadata->SetMetadata(metadata::benchmark_name,
403 Variadic::String(benchmark_name_id));
404 }
405 if (packet.has_benchmark_description()) {
406 auto benchmark_description_id =
407 storage->InternString(packet.benchmark_description());
408 metadata->SetMetadata(metadata::benchmark_description,
409 Variadic::String(benchmark_description_id));
410 }
411 if (packet.has_label()) {
412 auto label_id = storage->InternString(packet.label());
413 metadata->SetMetadata(metadata::benchmark_label,
414 Variadic::String(label_id));
415 }
416 if (packet.has_story_name()) {
417 auto story_name_id = storage->InternString(packet.story_name());
418 metadata->SetMetadata(metadata::benchmark_story_name,
419 Variadic::String(story_name_id));
420 }
421 for (auto it = packet.story_tags(); it; ++it) {
422 auto story_tag_id = storage->InternString(*it);
423 metadata->AppendMetadata(metadata::benchmark_story_tags,
424 Variadic::String(story_tag_id));
425 }
426 if (packet.has_benchmark_start_time_us()) {
427 metadata->SetMetadata(metadata::benchmark_start_time_us,
428 Variadic::Integer(packet.benchmark_start_time_us()));
429 }
430 if (packet.has_story_run_time_us()) {
431 metadata->SetMetadata(metadata::benchmark_story_run_time_us,
432 Variadic::Integer(packet.story_run_time_us()));
433 }
434 if (packet.has_story_run_index()) {
435 metadata->SetMetadata(metadata::benchmark_story_run_index,
436 Variadic::Integer(packet.story_run_index()));
437 }
438 if (packet.has_had_failures()) {
439 metadata->SetMetadata(metadata::benchmark_had_failures,
440 Variadic::Integer(packet.had_failures()));
441 }
442 }
443
ParseChromeEvents(int64_t ts,ConstBytes blob)444 void ProtoTraceParser::ParseChromeEvents(int64_t ts, ConstBytes blob) {
445 TraceStorage* storage = context_->storage.get();
446 protos::pbzero::ChromeEventBundle::Decoder bundle(blob.data, blob.size);
447 ArgsTracker args(context_);
448 if (bundle.has_metadata()) {
449 RawId id = storage->mutable_raw_table()
450 ->Insert({ts, raw_chrome_metadata_event_id_, 0, 0})
451 .id;
452 auto inserter = args.AddArgsTo(id);
453
454 // Metadata is proxied via a special event in the raw table to JSON export.
455 for (auto it = bundle.metadata(); it; ++it) {
456 protos::pbzero::ChromeMetadata::Decoder metadata(*it);
457 StringId name_id = storage->InternString(metadata.name());
458 Variadic value;
459 if (metadata.has_string_value()) {
460 value =
461 Variadic::String(storage->InternString(metadata.string_value()));
462 } else if (metadata.has_int_value()) {
463 value = Variadic::Integer(metadata.int_value());
464 } else if (metadata.has_bool_value()) {
465 value = Variadic::Integer(metadata.bool_value());
466 } else if (metadata.has_json_value()) {
467 value = Variadic::Json(storage->InternString(metadata.json_value()));
468 } else {
469 context_->storage->IncrementStats(stats::empty_chrome_metadata);
470 continue;
471 }
472 args.AddArgsTo(id).AddArg(name_id, value);
473 }
474 }
475
476 if (bundle.has_legacy_ftrace_output()) {
477 RawId id =
478 storage->mutable_raw_table()
479 ->Insert({ts, raw_chrome_legacy_system_trace_event_id_, 0, 0})
480 .id;
481
482 std::string data;
483 for (auto it = bundle.legacy_ftrace_output(); it; ++it) {
484 data += (*it).ToStdString();
485 }
486 Variadic value =
487 Variadic::String(storage->InternString(base::StringView(data)));
488 args.AddArgsTo(id).AddArg(data_name_id_, value);
489 }
490
491 if (bundle.has_legacy_json_trace()) {
492 for (auto it = bundle.legacy_json_trace(); it; ++it) {
493 protos::pbzero::ChromeLegacyJsonTrace::Decoder legacy_trace(*it);
494 if (legacy_trace.type() !=
495 protos::pbzero::ChromeLegacyJsonTrace::USER_TRACE) {
496 continue;
497 }
498 RawId id =
499 storage->mutable_raw_table()
500 ->Insert({ts, raw_chrome_legacy_user_trace_event_id_, 0, 0})
501 .id;
502 Variadic value =
503 Variadic::String(storage->InternString(legacy_trace.data()));
504 args.AddArgsTo(id).AddArg(data_name_id_, value);
505 }
506 }
507 }
508
ParseMetatraceEvent(int64_t ts,ConstBytes blob)509 void ProtoTraceParser::ParseMetatraceEvent(int64_t ts, ConstBytes blob) {
510 protos::pbzero::PerfettoMetatrace::Decoder event(blob.data, blob.size);
511 auto utid = context_->process_tracker->GetOrCreateThread(event.thread_id());
512
513 StringId cat_id = metatrace_id_;
514 StringId name_id = kNullStringId;
515 char fallback[64];
516
517 // This function inserts the args from the proto into the args table.
518 // Args inserted with the same key multiple times are treated as an array:
519 // this function correctly creates the key and flat key for each arg array.
520 auto args_fn = [this, &event](ArgsTracker::BoundInserter* inserter) {
521 using Arg = std::pair<StringId, StringId>;
522
523 // First, get a list of all the args so we can group them by key.
524 std::vector<Arg> interned;
525 for (auto it = event.args(); it; ++it) {
526 protos::pbzero::PerfettoMetatrace::Arg::Decoder arg_proto(*it);
527 StringId key = context_->storage->InternString(arg_proto.key());
528 StringId value = context_->storage->InternString(arg_proto.value());
529 interned.emplace_back(key, value);
530 }
531
532 // We stable sort insted of sorting here to avoid changing the order of the
533 // args in arrays.
534 std::stable_sort(interned.begin(), interned.end(),
535 [](const Arg& a, const Arg& b) {
536 return a.first.raw_id() < b.second.raw_id();
537 });
538
539 // Compute the correct key for each arg, possibly adding an index to
540 // the end of the key if needed.
541 char buffer[2048];
542 uint32_t current_idx = 0;
543 for (auto it = interned.begin(); it != interned.end(); ++it) {
544 auto next = it + 1;
545 StringId key = it->first;
546 StringId next_key = next == interned.end() ? kNullStringId : next->first;
547
548 if (key != next_key && current_idx == 0) {
549 inserter->AddArg(key, Variadic::String(it->second));
550 } else {
551 constexpr size_t kMaxIndexSize = 20;
552 base::StringView key_str = context_->storage->GetString(key);
553 if (key_str.size() >= sizeof(buffer) - kMaxIndexSize) {
554 PERFETTO_DLOG("Ignoring arg with unreasonbly large size");
555 continue;
556 }
557
558 base::StringWriter writer(buffer, sizeof(buffer));
559 writer.AppendString(key_str);
560 writer.AppendChar('[');
561 writer.AppendUnsignedInt(current_idx);
562 writer.AppendChar(']');
563
564 StringId new_key =
565 context_->storage->InternString(writer.GetStringView());
566 inserter->AddArg(key, new_key, Variadic::String(it->second));
567
568 current_idx = key == next_key ? current_idx + 1 : 0;
569 }
570 }
571 };
572
573 if (event.has_event_id() || event.has_event_name()) {
574 if (event.has_event_id()) {
575 auto eid = event.event_id();
576 if (eid < metatrace::EVENTS_MAX) {
577 name_id = context_->storage->InternString(metatrace::kEventNames[eid]);
578 } else {
579 sprintf(fallback, "Event %d", eid);
580 name_id = context_->storage->InternString(fallback);
581 }
582 } else {
583 name_id = context_->storage->InternString(event.event_name());
584 }
585 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
586 context_->slice_tracker->Scoped(ts, track_id, cat_id, name_id,
587 event.event_duration_ns(), args_fn);
588 } else if (event.has_counter_id() || event.has_counter_name()) {
589 if (event.has_counter_id()) {
590 auto cid = event.counter_id();
591 if (cid < metatrace::COUNTERS_MAX) {
592 name_id =
593 context_->storage->InternString(metatrace::kCounterNames[cid]);
594 } else {
595 sprintf(fallback, "Counter %d", cid);
596 name_id = context_->storage->InternString(fallback);
597 }
598 } else {
599 name_id = context_->storage->InternString(event.counter_name());
600 }
601 TrackId track =
602 context_->track_tracker->InternThreadCounterTrack(name_id, utid);
603 auto opt_id =
604 context_->event_tracker->PushCounter(ts, event.counter_value(), track);
605 if (opt_id) {
606 auto inserter = context_->args_tracker->AddArgsTo(*opt_id);
607 args_fn(&inserter);
608 }
609 }
610
611 if (event.has_overruns())
612 context_->storage->IncrementStats(stats::metatrace_overruns);
613 }
614
ParseTraceConfig(ConstBytes blob)615 void ProtoTraceParser::ParseTraceConfig(ConstBytes blob) {
616 protos::pbzero::TraceConfig::Decoder trace_config(blob.data, blob.size);
617
618 // TODO(eseckler): Propagate statuses from modules.
619 for (auto& module : context_->modules) {
620 module->ParseTraceConfig(trace_config);
621 }
622
623 int64_t uuid_msb = trace_config.trace_uuid_msb();
624 int64_t uuid_lsb = trace_config.trace_uuid_lsb();
625 if (uuid_msb != 0 || uuid_lsb != 0) {
626 base::Uuid uuid(uuid_lsb, uuid_msb);
627 std::string str = uuid.ToPrettyString();
628 StringId id = context_->storage->InternString(base::StringView(str));
629 context_->metadata_tracker->SetMetadata(metadata::trace_uuid,
630 Variadic::String(id));
631 }
632
633 if (trace_config.has_unique_session_name()) {
634 StringId id = context_->storage->InternString(
635 base::StringView(trace_config.unique_session_name()));
636 context_->metadata_tracker->SetMetadata(metadata::unique_session_name,
637 Variadic::String(id));
638 }
639 }
640
ParseModuleSymbols(ConstBytes blob)641 void ProtoTraceParser::ParseModuleSymbols(ConstBytes blob) {
642 protos::pbzero::ModuleSymbols::Decoder module_symbols(blob.data, blob.size);
643 StringId build_id;
644 // TODO(b/148109467): Remove workaround once all active Chrome versions
645 // write raw bytes instead of a string as build_id.
646 if (module_symbols.build_id().size == 33) {
647 build_id = context_->storage->InternString(module_symbols.build_id());
648 } else {
649 build_id = context_->storage->InternString(base::StringView(base::ToHex(
650 module_symbols.build_id().data, module_symbols.build_id().size)));
651 }
652
653 auto mapping_ids = context_->storage->FindMappingRow(
654 context_->storage->InternString(module_symbols.path()), build_id);
655 if (mapping_ids.empty()) {
656 context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
657 return;
658 }
659 for (auto addr_it = module_symbols.address_symbols(); addr_it; ++addr_it) {
660 protos::pbzero::AddressSymbols::Decoder address_symbols(*addr_it);
661
662 uint32_t symbol_set_id = context_->storage->symbol_table().row_count();
663
664 bool has_lines = false;
665 for (auto line_it = address_symbols.lines(); line_it; ++line_it) {
666 protos::pbzero::Line::Decoder line(*line_it);
667 context_->storage->mutable_symbol_table()->Insert(
668 {symbol_set_id, context_->storage->InternString(line.function_name()),
669 context_->storage->InternString(line.source_file_name()),
670 line.line_number()});
671 has_lines = true;
672 }
673 if (!has_lines) {
674 continue;
675 }
676 bool frame_found = false;
677 for (MappingId mapping_id : mapping_ids) {
678 std::vector<FrameId> frame_ids = context_->storage->FindFrameIds(
679 mapping_id, address_symbols.address());
680
681 for (const FrameId frame_id : frame_ids) {
682 auto* frames = context_->storage->mutable_stack_profile_frame_table();
683 uint32_t frame_row = *frames->id().IndexOf(frame_id);
684 frames->mutable_symbol_set_id()->Set(frame_row, symbol_set_id);
685 frame_found = true;
686 }
687 }
688
689 if (!frame_found) {
690 context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
691 continue;
692 }
693
694 }
695 }
696
ParseTrigger(int64_t ts,ConstBytes blob)697 void ProtoTraceParser::ParseTrigger(int64_t ts, ConstBytes blob) {
698 protos::pbzero::Trigger::Decoder trigger(blob.data, blob.size);
699 StringId cat_id = kNullStringId;
700 TrackId track_id = context_->track_tracker->GetOrCreateTriggerTrack();
701 StringId name_id = context_->storage->InternString(trigger.trigger_name());
702 context_->slice_tracker->Scoped(
703 ts, track_id, cat_id, name_id,
704 /* duration = */ 0,
705 [&trigger, this](ArgsTracker::BoundInserter* args_table) {
706 StringId producer_name_key =
707 context_->storage->InternString("producer_name");
708 args_table->AddArg(producer_name_key,
709 Variadic::String(context_->storage->InternString(
710 trigger.producer_name())));
711 StringId trusted_producer_uid_key =
712 context_->storage->InternString("trusted_producer_uid");
713 args_table->AddArg(trusted_producer_uid_key,
714 Variadic::Integer(trigger.trusted_producer_uid()));
715 });
716 }
717
ParseServiceEvent(int64_t ts,ConstBytes blob)718 void ProtoTraceParser::ParseServiceEvent(int64_t ts, ConstBytes blob) {
719 protos::pbzero::TracingServiceEvent::Decoder tse(blob.data, blob.size);
720 if (tse.all_data_sources_started()) {
721 context_->metadata_tracker->SetMetadata(
722 metadata::all_data_source_started_ns, Variadic::Integer(ts));
723 }
724 }
725
ParseSmapsPacket(int64_t ts,ConstBytes blob)726 void ProtoTraceParser::ParseSmapsPacket(int64_t ts, ConstBytes blob) {
727 protos::pbzero::SmapsPacket::Decoder sp(blob.data, blob.size);
728 auto upid = context_->process_tracker->GetOrCreateProcess(sp.pid());
729
730 for (auto it = sp.entries(); it; ++it) {
731 protos::pbzero::SmapsEntry::Decoder e(*it);
732 context_->storage->mutable_profiler_smaps_table()->Insert(
733 {upid, ts, context_->storage->InternString(e.path()),
734 static_cast<int64_t>(e.size_kb()),
735 static_cast<int64_t>(e.private_dirty_kb()),
736 static_cast<int64_t>(e.swap_kb())});
737 }
738 }
739
740 } // namespace trace_processor
741 } // namespace perfetto
742