xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/proto/proto_trace_parser_impl.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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_impl.h"
18 
19 #include <algorithm>
20 #include <cstdint>
21 #include <cstring>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 #include "perfetto/base/logging.h"
27 #include "perfetto/ext/base/metatrace_events.h"
28 #include "perfetto/ext/base/string_utils.h"
29 #include "perfetto/ext/base/string_view.h"
30 #include "perfetto/ext/base/string_writer.h"
31 #include "perfetto/trace_processor/trace_blob_view.h"
32 #include "src/trace_processor/containers/null_term_string_view.h"
33 #include "src/trace_processor/importers/common/args_tracker.h"
34 #include "src/trace_processor/importers/common/cpu_tracker.h"
35 #include "src/trace_processor/importers/common/event_tracker.h"
36 #include "src/trace_processor/importers/common/metadata_tracker.h"
37 #include "src/trace_processor/importers/common/parser_types.h"
38 #include "src/trace_processor/importers/common/process_tracker.h"
39 #include "src/trace_processor/importers/common/slice_tracker.h"
40 #include "src/trace_processor/importers/common/track_tracker.h"
41 #include "src/trace_processor/importers/common/tracks.h"
42 #include "src/trace_processor/importers/common/tracks_common.h"
43 #include "src/trace_processor/importers/etw/etw_module.h"
44 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
45 #include "src/trace_processor/importers/proto/track_event_module.h"
46 #include "src/trace_processor/storage/stats.h"
47 #include "src/trace_processor/storage/trace_storage.h"
48 #include "src/trace_processor/types/trace_processor_context.h"
49 #include "src/trace_processor/types/variadic.h"
50 
51 #include "protos/perfetto/config/trace_config.pbzero.h"
52 #include "protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
53 #include "protos/perfetto/trace/perfetto/perfetto_metatrace.pbzero.h"
54 #include "protos/perfetto/trace/trace_packet.pbzero.h"
55 
56 namespace perfetto::trace_processor {
57 
ProtoTraceParserImpl(TraceProcessorContext * context)58 ProtoTraceParserImpl::ProtoTraceParserImpl(TraceProcessorContext* context)
59     : context_(context),
60       metatrace_id_(context->storage->InternString("metatrace")),
61       data_name_id_(context->storage->InternString("data")),
62       raw_chrome_metadata_event_id_(
63           context->storage->InternString("chrome_event.metadata")),
64       raw_chrome_legacy_system_trace_event_id_(
65           context->storage->InternString("chrome_event.legacy_system_trace")),
66       raw_chrome_legacy_user_trace_event_id_(
67           context->storage->InternString("chrome_event.legacy_user_trace")),
68       missing_metatrace_interned_string_id_(
69           context->storage->InternString("MISSING STRING")) {}
70 
71 ProtoTraceParserImpl::~ProtoTraceParserImpl() = default;
72 
ParseTracePacket(int64_t ts,TracePacketData data)73 void ProtoTraceParserImpl::ParseTracePacket(int64_t ts, TracePacketData data) {
74   const TraceBlobView& blob = data.packet;
75   protos::pbzero::TracePacket::Decoder packet(blob.data(), blob.length());
76   // TODO(eseckler): Propagate statuses from modules.
77   auto& modules = context_->modules_by_field;
78   for (uint32_t field_id = 1; field_id < modules.size(); ++field_id) {
79     if (!modules[field_id].empty() && packet.Get(field_id).valid()) {
80       for (ProtoImporterModule* global_module :
81            context_->modules_for_all_fields) {
82         global_module->ParseTracePacketData(packet, ts, data, field_id);
83       }
84       for (ProtoImporterModule* module : modules[field_id])
85         module->ParseTracePacketData(packet, ts, data, field_id);
86       return;
87     }
88   }
89 
90   if (packet.has_chrome_events()) {
91     ParseChromeEvents(ts, packet.chrome_events());
92   }
93 
94   if (packet.has_perfetto_metatrace()) {
95     ParseMetatraceEvent(ts, packet.perfetto_metatrace());
96   }
97 
98   if (packet.has_trace_config()) {
99     // TODO(eseckler): Propagate statuses from modules.
100     protos::pbzero::TraceConfig::Decoder config(packet.trace_config());
101     for (auto& module : context_->modules) {
102       module->ParseTraceConfig(config);
103     }
104   }
105 }
106 
ParseTrackEvent(int64_t ts,TrackEventData data)107 void ProtoTraceParserImpl::ParseTrackEvent(int64_t ts, TrackEventData data) {
108   const TraceBlobView& blob = data.trace_packet_data.packet;
109   protos::pbzero::TracePacket::Decoder packet(blob.data(), blob.length());
110   context_->track_module->ParseTrackEventData(packet, ts, data);
111   context_->args_tracker->Flush();
112 }
113 
ParseEtwEvent(uint32_t cpu,int64_t ts,TracePacketData data)114 void ProtoTraceParserImpl::ParseEtwEvent(uint32_t cpu,
115                                          int64_t ts,
116                                          TracePacketData data) {
117   PERFETTO_DCHECK(context_->etw_module);
118   context_->etw_module->ParseEtwEventData(cpu, ts, data);
119 
120   // TODO(lalitm): maybe move this to the flush method in the trace processor
121   // once we have it. This may reduce performance in the ArgsTracker though so
122   // needs to be handled carefully.
123   context_->args_tracker->Flush();
124 }
125 
ParseFtraceEvent(uint32_t cpu,int64_t ts,TracePacketData data)126 void ProtoTraceParserImpl::ParseFtraceEvent(uint32_t cpu,
127                                             int64_t ts,
128                                             TracePacketData data) {
129   PERFETTO_DCHECK(context_->ftrace_module);
130   context_->ftrace_module->ParseFtraceEventData(cpu, ts, data);
131 
132   // TODO(lalitm): maybe move this to the flush method in the trace processor
133   // once we have it. This may reduce performance in the ArgsTracker though so
134   // needs to be handled carefully.
135   context_->args_tracker->Flush();
136 }
137 
ParseInlineSchedSwitch(uint32_t cpu,int64_t ts,InlineSchedSwitch data)138 void ProtoTraceParserImpl::ParseInlineSchedSwitch(uint32_t cpu,
139                                                   int64_t ts,
140                                                   InlineSchedSwitch data) {
141   PERFETTO_DCHECK(context_->ftrace_module);
142   context_->ftrace_module->ParseInlineSchedSwitch(cpu, ts, data);
143 
144   // TODO(lalitm): maybe move this to the flush method in the trace processor
145   // once we have it. This may reduce performance in the ArgsTracker though so
146   // needs to be handled carefully.
147   context_->args_tracker->Flush();
148 }
149 
ParseInlineSchedWaking(uint32_t cpu,int64_t ts,InlineSchedWaking data)150 void ProtoTraceParserImpl::ParseInlineSchedWaking(uint32_t cpu,
151                                                   int64_t ts,
152                                                   InlineSchedWaking data) {
153   PERFETTO_DCHECK(context_->ftrace_module);
154   context_->ftrace_module->ParseInlineSchedWaking(cpu, ts, data);
155 
156   // TODO(lalitm): maybe move this to the flush method in the trace processor
157   // once we have it. This may reduce performance in the ArgsTracker though so
158   // needs to be handled carefully.
159   context_->args_tracker->Flush();
160 }
161 
ParseChromeEvents(int64_t ts,ConstBytes blob)162 void ProtoTraceParserImpl::ParseChromeEvents(int64_t ts, ConstBytes blob) {
163   TraceStorage* storage = context_->storage.get();
164   protos::pbzero::ChromeEventBundle::Decoder bundle(blob.data, blob.size);
165   ArgsTracker args(context_);
166   if (bundle.has_metadata()) {
167     auto ucpu = context_->cpu_tracker->GetOrCreateCpu(0);
168     RawId id = storage->mutable_raw_table()
169                    ->Insert({ts, raw_chrome_metadata_event_id_, 0, 0, 0, ucpu})
170                    .id;
171     auto inserter = args.AddArgsTo(id);
172 
173     uint32_t bundle_index =
174         context_->metadata_tracker->IncrementChromeMetadataBundleCount();
175 
176     // The legacy untyped metadata is proxied via a special event in the raw
177     // table to JSON export.
178     for (auto it = bundle.metadata(); it; ++it) {
179       protos::pbzero::ChromeMetadata::Decoder metadata(*it);
180       Variadic value = Variadic::Null();
181       if (metadata.has_string_value()) {
182         value =
183             Variadic::String(storage->InternString(metadata.string_value()));
184       } else if (metadata.has_int_value()) {
185         value = Variadic::Integer(metadata.int_value());
186       } else if (metadata.has_bool_value()) {
187         value = Variadic::Integer(metadata.bool_value());
188       } else if (metadata.has_json_value()) {
189         value = Variadic::Json(storage->InternString(metadata.json_value()));
190       } else {
191         context_->storage->IncrementStats(stats::empty_chrome_metadata);
192         continue;
193       }
194 
195       StringId name_id = storage->InternString(metadata.name());
196       args.AddArgsTo(id).AddArg(name_id, value);
197 
198       char buffer[2048];
199       base::StringWriter writer(buffer, sizeof(buffer));
200       writer.AppendString("cr-");
201       // If we have data from multiple Chrome instances, append a suffix
202       // to differentiate them.
203       if (bundle_index > 1) {
204         writer.AppendUnsignedInt(bundle_index);
205         writer.AppendChar('-');
206       }
207       writer.AppendString(metadata.name());
208 
209       auto metadata_id = storage->InternString(writer.GetStringView());
210       context_->metadata_tracker->SetDynamicMetadata(metadata_id, value);
211     }
212   }
213 
214   if (bundle.has_legacy_ftrace_output()) {
215     auto ucpu = context_->cpu_tracker->GetOrCreateCpu(0);
216     RawId id = storage->mutable_raw_table()
217                    ->Insert({ts, raw_chrome_legacy_system_trace_event_id_, 0, 0,
218                              0, ucpu})
219                    .id;
220 
221     std::string data;
222     for (auto it = bundle.legacy_ftrace_output(); it; ++it) {
223       data += (*it).ToStdString();
224     }
225     Variadic value =
226         Variadic::String(storage->InternString(base::StringView(data)));
227     args.AddArgsTo(id).AddArg(data_name_id_, value);
228   }
229 
230   if (bundle.has_legacy_json_trace()) {
231     for (auto it = bundle.legacy_json_trace(); it; ++it) {
232       protos::pbzero::ChromeLegacyJsonTrace::Decoder legacy_trace(*it);
233       if (legacy_trace.type() !=
234           protos::pbzero::ChromeLegacyJsonTrace::USER_TRACE) {
235         continue;
236       }
237       auto ucpu = context_->cpu_tracker->GetOrCreateCpu(0);
238       RawId id = storage->mutable_raw_table()
239                      ->Insert({ts, raw_chrome_legacy_user_trace_event_id_, 0, 0,
240                                0, ucpu})
241                      .id;
242       Variadic value =
243           Variadic::String(storage->InternString(legacy_trace.data()));
244       args.AddArgsTo(id).AddArg(data_name_id_, value);
245     }
246   }
247 }
248 
ParseMetatraceEvent(int64_t ts,ConstBytes blob)249 void ProtoTraceParserImpl::ParseMetatraceEvent(int64_t ts, ConstBytes blob) {
250   protos::pbzero::PerfettoMetatrace::Decoder event(blob);
251   auto utid = context_->process_tracker->GetOrCreateThread(event.thread_id());
252 
253   StringId cat_id = metatrace_id_;
254   for (auto it = event.interned_strings(); it; ++it) {
255     protos::pbzero::PerfettoMetatrace::InternedString::Decoder interned_string(
256         it->data(), it->size());
257     metatrace_interned_strings_.Insert(
258         interned_string.iid(),
259         context_->storage->InternString(interned_string.value()));
260   }
261 
262   // This function inserts the args from the proto into the args table.
263   // Args inserted with the same key multiple times are treated as an array:
264   // this function correctly creates the key and flat key for each arg array.
265   auto args_fn = [this, &event](ArgsTracker::BoundInserter* inserter) {
266     using Arg = std::pair<StringId, StringId>;
267 
268     // First, get a list of all the args so we can group them by key.
269     std::vector<Arg> interned;
270     for (auto it = event.args(); it; ++it) {
271       protos::pbzero::PerfettoMetatrace::Arg::Decoder arg_proto(*it);
272       StringId key;
273       if (arg_proto.has_key_iid()) {
274         key = GetMetatraceInternedString(arg_proto.key_iid());
275       } else {
276         key = context_->storage->InternString(arg_proto.key());
277       }
278       StringId value;
279       if (arg_proto.has_value_iid()) {
280         value = GetMetatraceInternedString(arg_proto.value_iid());
281       } else {
282         value = context_->storage->InternString(arg_proto.value());
283       }
284       interned.emplace_back(key, value);
285     }
286 
287     // We stable sort insted of sorting here to avoid changing the order of the
288     // args in arrays.
289     std::stable_sort(interned.begin(), interned.end(),
290                      [](const Arg& a, const Arg& b) {
291                        return a.first.raw_id() < b.first.raw_id();
292                      });
293 
294     // Compute the correct key for each arg, possibly adding an index to
295     // the end of the key if needed.
296     char buffer[2048];
297     uint32_t current_idx = 0;
298     for (auto it = interned.begin(); it != interned.end(); ++it) {
299       auto next = it + 1;
300       StringId key = it->first;
301       StringId next_key = next == interned.end() ? kNullStringId : next->first;
302 
303       if (key != next_key && current_idx == 0) {
304         inserter->AddArg(key, Variadic::String(it->second));
305       } else {
306         constexpr size_t kMaxIndexSize = 20;
307         NullTermStringView key_str = context_->storage->GetString(key);
308         if (key_str.size() >= sizeof(buffer) - kMaxIndexSize) {
309           PERFETTO_DLOG("Ignoring arg with unreasonbly large size");
310           continue;
311         }
312 
313         base::StackString<2048> array_key("%s[%u]", key_str.c_str(),
314                                           current_idx);
315         StringId new_key =
316             context_->storage->InternString(array_key.string_view());
317         inserter->AddArg(key, new_key, Variadic::String(it->second));
318 
319         current_idx = key == next_key ? current_idx + 1 : 0;
320       }
321     }
322   };
323 
324   if (event.has_event_id() || event.has_event_name() ||
325       event.has_event_name_iid()) {
326     StringId name_id;
327     if (event.has_event_id()) {
328       auto eid = event.event_id();
329       if (eid < metatrace::EVENTS_MAX) {
330         name_id = context_->storage->InternString(metatrace::kEventNames[eid]);
331       } else {
332         base::StackString<64> fallback("Event %d", eid);
333         name_id = context_->storage->InternString(fallback.string_view());
334       }
335     } else if (event.has_event_name_iid()) {
336       name_id = GetMetatraceInternedString(event.event_name_iid());
337     } else {
338       name_id = context_->storage->InternString(event.event_name());
339     }
340     TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
341     context_->slice_tracker->Scoped(
342         ts, track_id, cat_id, name_id,
343         static_cast<int64_t>(event.event_duration_ns()), args_fn);
344   } else if (event.has_counter_id() || event.has_counter_name()) {
345     static constexpr auto kBlueprint = tracks::CounterBlueprint(
346         "metatrace_counter", tracks::UnknownUnitBlueprint(),
347         tracks::DimensionBlueprints(
348             tracks::kThreadDimensionBlueprint,
349             tracks::StringDimensionBlueprint("counter_name")),
350         tracks::DynamicNameBlueprint());
351     TrackId track;
352     if (event.has_counter_id()) {
353       auto cid = event.counter_id();
354       StringId name_id;
355       if (cid < metatrace::COUNTERS_MAX) {
356         name_id =
357             context_->storage->InternString(metatrace::kCounterNames[cid]);
358       } else {
359         base::StackString<64> fallback("Counter %d", cid);
360         name_id = context_->storage->InternString(fallback.string_view());
361       }
362       track = context_->track_tracker->InternTrack(
363           kBlueprint,
364           tracks::Dimensions(utid, context_->storage->GetString(name_id)),
365           tracks::DynamicName(name_id));
366     } else {
367       track = context_->track_tracker->InternTrack(
368           kBlueprint, tracks::Dimensions(utid, event.counter_name()),
369           tracks::DynamicName(
370               context_->storage->InternString(event.counter_name())));
371     }
372     auto opt_id =
373         context_->event_tracker->PushCounter(ts, event.counter_value(), track);
374     if (opt_id) {
375       auto inserter = context_->args_tracker->AddArgsTo(*opt_id);
376       args_fn(&inserter);
377     }
378   }
379 
380   if (event.has_overruns())
381     context_->storage->IncrementStats(stats::metatrace_overruns);
382 }
383 
GetMetatraceInternedString(uint64_t iid)384 StringId ProtoTraceParserImpl::GetMetatraceInternedString(uint64_t iid) {
385   StringId* maybe_id = metatrace_interned_strings_.Find(iid);
386   if (!maybe_id)
387     return missing_metatrace_interned_string_id_;
388   return *maybe_id;
389 }
390 
391 }  // namespace perfetto::trace_processor
392