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