• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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